King Domino.

Programmation Python

Partie A : Gestion du royaume CODE BINÔME

Initialisation des structures de données (Matrices). (Cliquer pour dérouler)

import copy
import random

def creerRoyaume(taille=7):
    """
    Crée une matrice taille x taille remplie de None,
    sauf la case centrale qui contient 'CH' (Château).
    """
    
    # Correction : < au lieu de < 
    if taille < 0 or taille % 2 == 0:  
        print("Impossible de créer un royaume : 'taille' est pair ou négatif")
        return None 

    # Création de la matrice vide (Liste de listes)
    royaume = [[None for _ in range(taille)] for _ in range(taille)]  
   
    milieu = taille // 2  # Trouve le centre
    royaume[milieu][milieu] = 'CH'  # Place le château
    
    return royaume 

def init_tuple_royaumes(taille=7):
    """Initialise les royaumes pour 2 joueurs."""
    
    royaume1 = creerRoyaume(taille)
    # Deepcopy nécessaire pour éviter que les joueurs modifient le même objet mémoire
    royaume2 = copy.deepcopy(royaume1)  
    return (royaume1, royaume2)

def afficherCoordonnees(royaume):
    """Aide au débogage : affiche les coordonnées (x,y) de chaque case."""
    
    n = len(royaume)
    for i in range(n):  
        for j in range(n):  
            print("|"+str((i,j)), end ="")  
        print("|") 

def init_cases_libres(taille):
    """Génère la liste de toutes les coordonnées disponibles au début."""
    
    cases_libres = [(i, j) for i in range(taille) for j in range(taille)]  
    milieu = taille // 2  
    cases_libres.remove((milieu, milieu)) # On enlève le château
    return cases_libres 

def init_tuple_libres(taille):
    return (init_cases_libres(taille), init_cases_libres(taille))  

def afficherRoyaume(royaume, joueur="A", vide="  "):
    """Affiche la grille dans la console de manière lisible."""
    
    if royaume is None:
        print("Royaume invalide.")
        return
    
    print(f"Royaume du joueur {joueur}:")  
    n = len(royaume)  
    for i in range(n):  
        for j in range(n):  
            case = royaume[i][j] if royaume[i][j] is not None else vide  
            print(f"|{case:2}", end="")  
        print("|")  
    print()
                            

Partie B : Gestion du placement MON CODE

Gestion du positionnement des dominos et respect des règles du jeu. (Cliquer pour dérouler)

from A_gestion_royaume import * def partie_droite_domino(posL, posC, dir):
    """
    Calcule les coordonnées de la deuxième moitié du domino 
    en fonction de la direction (Haut, Bas, Gauche, Droite).
    """
    
    if dir == 'top':
        return (posL + 1, posC) # Attention : en matrice, +1 ligne = descendre visuellement
    elif dir == 'bottom':
        return (posL - 1, posC)
    elif dir == 'left':
        return (posL, posC + 1)
    elif dir == 'right':
        return (posL, posC - 1)
    else:
        print(f"Direction mal renseignée : {dir}")
        return None

def espaceLibre(royaume, posL, posC, dir):
    """
    Vérifie si un domino peut être posé à un endroit donné.
    """
    
    taille_roy = len(royaume)
    droite = partie_droite_domino(posL, posC, dir)

    if droite == None: return False

    # Vérification des bornes (0 <= index < taille)
    # Correction : Utilisation de < et <= pour le HTML
    if not(0 <= posL < taille_roy and 0 <= posC < taille_roy and 
           0 <= droite[0] < taille_roy and 0 <= droite[1] < taille_roy):
        return False

    # Vérification du contenu des cases
    if royaume[posL][posC] == None and royaume[droite[0]][droite[1]] == None:
        return True
    else:
        return False

def ajoutDomino(royaume, cases_libres, domino, posL, posC, dir):
    """
    Place définitivement un domino dans la matrice 'royaume'
    et met à jour la liste des 'cases_libres'.
    """
    
    if espaceLibre(royaume, posL, posC, dir):
        # Placement de la première case
        royaume[posL][posC] = domino[1]
        if (posL, posC) in cases_libres:
            cases_libres.remove((posL, posC))

        # Calcul des coordonnées de la deuxième case
        droite = partie_droite_domino(posL, posC, dir)
        posL_droite, posC_droite = droite
        
        # Placement de la deuxième case
        royaume[posL_droite][posC_droite] = domino[2]
        if (posL_droite, posC_droite) in cases_libres:
            cases_libres.remove((posL_droite, posC_droite))

        print(f"Succès : Domino {domino} ajouté en ({posL}, {posC}).")
    else:
        print(f"Erreur : Impossible d'ajouter le domino {domino} ici.")

def voisinages(royaume, posL, posC, libres=False):
    """
    Retourne la liste des coordonnées voisines (Haut, Bas, Gauche, Droite).
    """
    
    voisins = []
    taille_roy = len(royaume)

    # Vérification simplifiée des 4 directions
    # Correction : < au lieu de <
    if posL < taille_roy - 1: voisins.append((posL+1, posC)) # Haut
    if posL > 0: voisins.append((posL-1, posC)) # Bas
    if posC > 0: voisins.append((posL, posC-1)) # Gauche
    if posC < taille_roy - 1: voisins.append((posL, posC+1)) # Droite
            
    return voisins
                            

Partie C : Gestion de la pioche des dominos CODE BINÔME

Chargement des données en format CSV et tirage aléatoire des dominos. (Cliquer pour dérouler)

import random
import csv

def extraire_dominos(nom_fichier):
    """Extrait les données brutes depuis un fichier CSV."""
    
    dominos = []
    with open(nom_fichier, 'r') as file:
        for line in file:
            line = line.strip()
            a = line.split(';')
            b = a[0]
            case1 = a[1]
            case2 = a[2]
            # Gestion des intervalles (ex: "1-5")
            if '-' in b:
                v1, v2 = b.split('-')
                i = int(v1)
                # Correction : <= 
                while int(i) <= int(v2):
                    dominos.append((int(i), case1, case2))
                    i = i + 1
            else:
                dominos.append((int(b), case1, case2))
    
    random.shuffle(dominos) # Mélange du paquet
    return dominos

def extraire_premier_bloc(liste_dominos):
    """Prend les 4 premiers dominos et les trie par numéro."""
    
    premiers_dominos = liste_dominos[:4]
    # Tri à bulles classique
    for i in range(len(premiers_dominos)):
        min_index = i
        for j in range(i + 1, len(premiers_dominos)):
            # Correction : <
            if premiers_dominos[j][0] < premiers_dominos[min_index][0]:
                min_index = j
        premiers_dominos[i], premiers_dominos[min_index] = premiers_dominos[min_index], premiers_dominos[i]
    return premiers_dominos

def piocher_bloc(liste_dominos):
    """Retire 4 dominos de la pioche principale."""
    
    premiers_dominos = liste_dominos[:4]
    liste_dominos[:] = liste_dominos[4:] # Met à jour la liste originale
    # Tri... (identique ci-dessus)
    for i in range(len(premiers_dominos)):
        min_index = i
        for j in range(i + 1, len(premiers_dominos)):
            # Correction : <
            if premiers_dominos[j][0] < premiers_dominos[min_index][0]:
                min_index = j
        premiers_dominos[i], premiers_dominos[min_index] = premiers_dominos[min_index], premiers_dominos[i]
    return premiers_dominos

def remplir_choix(liste_dominos, dico_choix):
    if not liste_dominos:
        dico_choix.clear()
        return
    premiers_dominos = piocher_bloc(liste_dominos)
    for i in range(4):
        dico_choix[i + 1] = [premiers_dominos[i], None]

def afficher_choix_ou_depot(dico):
    for i in range(1, 5):
        print(i, "- Domino", dico[i][0], "Joueur", dico[i][1])
                            

Partie D : Gestion des joueurs & IA MON CODE

Système d'interaction avec l'utilisateur et IA classique. (Cliquer pour dérouler)

from B_gestion_dominos import *
import random

def init_tuple_joueurs(perso = False):
    """Initialise les noms des joueurs."""
    nom_A = "A"
    nom_B = "B"
    if perso:
        nom_A = input("Nom du premier joueur : ")
        nom_B = input("Nom du second joueur : ")
    return nom_A,nom_B

def init_configurations(tuple_joueurs):
    """Demande à chaque joueur s'il veut jouer manuellement ou laisser l'ordi jouer."""
    
    joueur_A, joueur_B = tuple_joueurs
    configurations = {}

    for joueur in (joueur_A, joueur_B):
        mode = input(f"Quel mode de jeu pour le joueur {joueur} : manuel ou random (m/r) ? ").strip()
        while mode not in ("m", "r"):
            print("Saisie incorrecte !", end=" ")
            mode = input(f"Quel mode de jeu pour le joueur {joueur} : manuel ou random (m/r) ? ").strip()
        configurations[joueur] = mode
    return configurations

def pose_domino_manuel(royaume, cases_libres, domino, joueur="A"):
    """Interface console pour demander les coordonnées au joueur."""
    
    valide = False
    while not valide:
        # Saisie sécurisée des coordonnées
        ligne = int(input(f"{joueur} : Sur quelle ligne poser le domino {domino} ? "))
        colonne = int(input(f"{joueur} : Sur quelle colonne poser le domino {domino} ? "))
        orientation = input(f"{joueur} : Orientation (top/bottom/left/right) ? ").lower()

        if orientation not in ['top', 'bottom', 'left', 'right']:
            print("Erreur : orientation invalide.")
            continue

        # Vérification via la fonction codée en Partie B
        if espaceLibre(royaume, ligne, colonne, orientation): 
            ajoutDomino(royaume, cases_libres, domino, ligne, colonne, orientation)
            print(f"{joueur} : Le domino {domino} a bien été ajouté.")
            valide = True
        else:
            print("Erreur ! Le domino ne peut pas être placé à cet endroit.")

def pose_domino_random(royaume, cases_libres, domino, joueur="A", TENTATIVES=10000):
    """
    IA Aléatoire : Tente 10 000 fois de placer le domino au hasard.
    """
    
    directions = ["top", "bottom", "left", "right"]
    domino_place = False 
    i = 0 
    
    # Correction : <
    while i < TENTATIVES and not domino_place:
        # Choix aléatoire d'une case libre
        posL, posC = random.choice(cases_libres)
        direction = random.choice(directions)
        
        # Test de validité
        if espaceLibre(royaume, posL, posC, direction): 
            ajoutDomino(royaume, cases_libres, domino, posL, posC, direction)
            print(f"{joueur} (IA) : Domino {domino} placé.")
            domino_place = True 
            break
        i += 1 
        
    if not domino_place:
        print(f"IA : Impossible de placer le domino après {TENTATIVES} essais.")

def pose_domino(royaume, cases_libres, domino, dico_configurations, joueur):
    """Aiguillage vers la bonne fonction selon la config du joueur."""
    
    if dico_configurations[joueur] == 'm':
        pose_domino_manuel(royaume, cases_libres, domino, joueur)
    elif dico_configurations[joueur] == 'r':
        pose_domino_random(royaume, cases_libres, domino, joueur)
                            

Partie E : Tour de jeu CODE BINÔME

Contrôle de l'enchaînement des tours et du choix des dominos. (Cliquer pour dérouler)

from B_gestion_dominos import *
from C_pile_dominos import *
from D_gestion_joueurs import *
import random

def vide_et_transfere_depot(dico_depot, dico_choix):
    """Prépare le tour suivant en déplaçant les choix."""
    dico_depot.clear()
    for cle, valeur in dico_choix.items():
        dico_depot[cle] = valeur[:]

def choix_pion(dico_choix, joueur, dico_configurations):
    """Permet au joueur de réserver son prochain domino."""
    
    if joueur not in dico_configurations:
        return None
    
    mode = dico_configurations[joueur]
    # Filtre les dominos qui n'ont pas encore de propriétaire
    dominos_disponibles = [num for num, (domino, possesseur) in dico_choix.items() if possesseur is None]
    
    if mode == "m":
        choix_valide = False
        while not choix_valide:
            try:
                choix = int(input(f"{joueur} : Sur quel domino posez-vous votre pion ? "))
                if choix in dominos_disponibles:
                    choix_valide = True
                    return choix
                else:
                    print("Impossible ! Ce domino n'est pas disponible.")
            except ValueError:
                print("Entrée invalide !")
    elif mode == "r":
        choix = random.choice(dominos_disponibles)
        print(f"{joueur} a choisi aléatoirement le domino {choix}")
        return choix

def pose_et_choix(royaume, cases_libres, dico_depot, dico_choix, dico_configurations, indice_depot):
    """Séquence complète d'un tour individuel : Pose puis Choix."""
    
    print("\n*** Joueur {} : à vous de jouer !".format(dico_depot[indice_depot][1]))
    afficherRoyaume(royaume)
    joueur = dico_depot[indice_depot][1]
    domino = dico_depot[indice_depot][0]
    
    # Phase 1 : Pose
    pose_domino(royaume, cases_libres, domino, dico_configurations, joueur)
    
    # Phase 2 : Choix futur
    if dico_choix:
        for k, v in dico_choix.items():
            print(f"{k}- Domino {v[0]} Joueur {v[1]}")
        choix = choix_pion(dico_choix, joueur, dico_configurations)
        dico_choix[choix][1] = joueur

def tour_de_jeu(tuple_royaumes, tuple_libres, liste_dominos, dico_depot, dico_choix, tuple_joueurs, dico_configurations):
    """Simule un tour complet pour les deux joueurs."""
    
    for i in range(0, 4):
        dico_choix[i+1][0] = liste_dominos[i]
        
    for i in range(1, 5):
        # Détermine qui joue en fonction de l'ordre du dépôt
        if dico_depot[i][1] == tuple_joueurs[0]:
            pose_et_choix(tuple_royaumes[0], tuple_libres[0], dico_depot, dico_choix, dico_configurations, i)
        elif dico_depot[i][1] == tuple_joueurs[1]:
            pose_et_choix(tuple_royaumes[1], tuple_libres[1], dico_depot, dico_choix, dico_configurations, i)

def premier_tour(liste_dominos, joueurs, configurations):
    """Gestion spécifique du tout premier tour (tirage au sort)."""
    
    premier_joueur = random.choice(joueurs)
    deuxieme_joueur = joueurs[1] if joueurs[0] == premier_joueur else joueurs[0]
    print(f"Tirage au sort : le joueur {premier_joueur} commence !")
    
    choix = {}
    depot = {}
    
    # Initialisation manuelle
    for i in range(4):
        choix[i + 1] = [liste_dominos[i], None]
        
    choix_pion(choix, premier_joueur, configurations[premier_joueur])
    choix_pion(choix, deuxieme_joueur, configurations[deuxieme_joueur])
    
    depot = choix.copy()
    return depot, choix
                            

Partie F : Calcul des scores MON CODE

L'algorithme qui calcule les points par groupe de paysages. (Cliquer pour dérouler)
Concept clé : La fonction couronnes_zone s'appelle elle-même (récursivité) pour explorer toutes les cases voisins d'un même type, jusqu'à ce qu'il n'y ait plus de voisins identiques.

from E_tour_de_jeu import *
import random

def init_dominos(taille, nom_fichier):
    taille_dominos = taille * taille - 1
    dominos = extraire_dominos(nom_fichier)
    domino_liste = dominos[:taille_dominos]
    return domino_liste

def couronnes_zone(royaume, posL, posC, cases_visitees, couronnes_total):
    """
    ALGORITHME RÉCURSIF :
    Parcourt une zone contiguë pour compter les couronnes et marquer les cases visitées.
    """
    
    # Cas de base : si la case est déjà traitée, on arrête pour éviter une boucle infinie
    if (posL, posC) in cases_visitees:
        return couronnes_total
    
    # Marquer la case actuelle comme visitée
    cases_visitees.append((posL, posC))
    
    # Ajoute les couronnes de la case actuelle (valeur extraite du tuple)
    couronnes_total += int(royaume[posL][posC][1])
    
    # Récupère les voisins via la fonction de la Partie B
    liste_pos = voisinages(royaume, posL, posC)
    
    # Pour chaque voisin
    for c in liste_pos:
        # Si le voisin existe, n'est pas visité, et est du même type de terrain
        if royaume[c[0]][c[1]] is not None and \
           not ((c[0], c[1]) in cases_visitees) and \
           royaume[c[0]][c[1]][0] == royaume[posL][posC][0]:
            
            # APPEL RÉCURSIF : On plonge dans le voisin
            couronnes_total = couronnes_zone(royaume, c[0], c[1], cases_visitees, couronnes_total)
            
    return couronnes_total

def score_zone(royaume, posL, posC):
    """Calcule le score d'une zone spécifique."""
    
    if royaume[posL][posC] == None:
        return 0
    
    cases_visitees = []
    # Appel de la fonction récursive
    nb_couronnes = couronnes_zone(royaume, posL, posC, cases_visitees, 0)
    
    # Le score est : Nombre de Couronnes * Taille de la Zone
    return (nb_couronnes * len(cases_visitees), cases_visitees) 

def total_score(royaume):
    """Parcourt tout le royaume pour calculer le score final."""
    
    score_total = 0
    cases_non_traitees = []
    
    # On liste toutes les cases occupées qui ne sont pas le château
    for x in range(len(royaume)):
        for y in range(len(royaume[x])):
            if royaume[x][y] != None and royaume[x][y] != 'CH':
                cases_non_traitees.append((x, y))
    
    # Tant qu'il reste des cases à analyser
    while cases_non_traitees != []:
        # On prend la première case et on calcule le score de toute sa zone
        zone_score, cases_de_la_zone = score_zone(royaume, cases_non_traitees[0][0], cases_non_traitees[0][1])
        
        score_total += zone_score
        
        # Optimisation : On retire toutes les cases de cette zone de la liste à traiter
        for i in cases_de_la_zone:
            if i in cases_non_traitees:
                cases_non_traitees.remove(i)
                
    return score_total

def jeu_complet():
    """Boucle principale du jeu (Main Loop)."""
    
    tuple_joueurs = init_tuple_joueurs() 
    n, pioche, configurations = init_configurations(tuple_joueurs)

    configurations = premier_tour(n, configurations, pioche)
    
    while pioche != []:
        configurations, pioche = tour_de_jeu(n, configurations, pioche)
        
    for i in range(n):
        print("Château du joueur", i + 1)
        print("Score :", configurations[i][3])
        
    scores = [configurations[i][3] for i in range(n)]
    max_score = max(scores)
    gagnant = scores.index(max_score)
    print("Le joueur gagnant est :", gagnant + 1)