import copy
import solutionsProjet 1 - Puissance 4
Dans ce projet nous allons implémenter un puissance 4 avec une interface graphique assez sommaire. Pour y arriver, nous allons utiliser les objets fondamentaux de Python.
Règles du jeu
Le but du jeu de Puissance 4 est d’aligner une suite de 4 pions de même couleur sur une grille comptant 6 rangées et 7 colonnes. Chaque joueur dispose de 21 pions d’une couleur (par convention, en général jaune ou rouge). Tour à tour, les deux joueurs placent un pion dans la colonne de leur choix, le pion coulisse alors jusqu’à la position la plus basse possible dans la dite colonne à la suite de quoi c’est à l’adversaire de jouer. Le vainqueur est le joueur qui réalise le premier un alignement (horizontal, vertical ou diagonal) consécutif d’au moins quatre pions de sa couleur. Si, alors que toutes les cases de la grille de jeu sont remplies, aucun des deux joueurs n’a réalisé un tel alignement, la partie est déclarée nulle.
Afin de simplifier le code de ce projet, on partira du principe que les alignements victorieux ne peuvent être qu’horizontaux ou verticaux. Les diagonales ne seront donc pas considérées (mais constituent un exercice intéressant pour aller plus loin !).
Plan du projet
Nous allons décomposer la construction du jeu en différentes parties :
initialisation de la grille
représentation de la grille
fonction de jeu
détection d’une victoire (horizontale)
détection d’une victoire (verticale)
fin de partie
Initialisation de la grille
L’objectif de cette partie est d’initialiser un objet Python qui représente une grille de puissance 4. Le choix que nous allons faire est de représenter la grille comme une liste de listes. Il s’agira d’une matrice 6x7 : on aura par conséquent une liste de 6 élements (qui représenteront les lignes de la grille), dont chacun des éléments sera une liste contenant 7 éléments (qui représenterons les pions).
Chaque élément de la grille sera représenté par un string, qui pourra prendre trois valeurs :
’ ’ : s’il s’agit d’une case vide
‘R’ : s’il s’agit d’un pion rouge.
‘Y’ : s’il s’agit d’un pion jaune (yellow).
Dans la fonction d’initialisation de la grille, chaque élément sera donc initialisé comme un string contenant un espace.
Attention : Bien faire attention à ce que les lignes soient des objets indépendants, autrement dit que modifier l’une des listes n’affecte pas les autres.
Résultat attendu
grid_solution = solutions.initialize_grid()
grid_solutionprint(f'Nombre de lignes : {len(grid_solution)}')
print(f'Nombre de colonnes : {len(grid_solution[0])}')A vous de jouer !
def initialize_grid():
# Votre code ici
return grid# Vérification du résultat
grid = initialize_grid()
grid# Vérification du résultat
print(f'Nombre de lignes : {len(grid)}')
print(f'Nombre de colonnes : {len(grid[0])}')Représentation de la grille
Notre grille est initialisée, mais son affichage est assez sommaire. L’idée de cette partie est d’offrir une représentation plus visuelle du jeu pendant une partie.
Pour cela, nous allons créer une fonction qui prend en entrée la grille précédemment initialisée et renvoie sa représentation (via la fonction print). Les colonnes seront séparées par le caractère | (barre verticale).
Indice : une solution possible fait intervenir deux notions que nous avons vues dans les TP précédents : la concaténation de strings et la fonction join qui “joint” les éléments d’une liste en les séparant par un certain caractère. Pour rappel, voici un exemple qui utilise ces deux concepts :
l = ["a", "b", "c", "d", "e"]
l_join = "DEBUT " + ", ".join(l) + " FIN"
print(l_join)Résultat attendu
solutions.display_grid(grid_solution)A vous de jouer !
def display_grid():
# Votre code ici# Vérification du résultat
display_grid(grid)Fonction de jeu
Maintenant que nous pouvons représenter notre grille, intéressons-nous au coeur du puissance 4 : le jeu. L’objectif de cette partie est de coder une fonction make_move qui va modifier la grille lorsqu’un joueur joue son tour.
Cette fonction prend en entrée :
la grille
la colonne choisie par le joueur
la couleur du pion (‘R’ pour le pion rouge, et ‘Y’ pour le pion jaune)
et renvoie en sortie la grille actualisée suite au tour du joueur.
Si la colonne choisie est déjà complète, renvoyer un message d’erreur.
Attention : en Python, la numérotation commence à 0. La première colonne correspond donc à la colonne 0 du point de vue de l’indexation.
Optionnel : Renvoyer un message d’erreur si un joueur essaie de jouer dans une colonne inexistante ou bien avec une couleur non autorisée.
Résultat attendu
grid_solution = solutions.initialize_grid() # Initialisation
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=2, disc_color="R") # 1er tour de jeu
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=5, disc_color="J") # 2ème tour de jeu
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=2, disc_color="R") # 3ème tour de jeu
solutions.display_grid(grid_solution)A vous de jouer !
def make_move(grid, column_to_play, disc_color):
new_grid = copy.deepcopy(grid) # Evite la modification de la grille initiale
# Votre code ici
return new_grid# Vérification du résultat
grid = initialize_grid() # Initialisation
grid = make_move(grid=grid, column_to_play=2, disc_color="R") # 1er tour de jeu
grid = make_move(grid=grid, column_to_play=5, disc_color="J") # 2ème tour de jeu
grid = make_move(grid=grid, column_to_play=2, disc_color="R") # 3ème tour de jeu
display_grid(grid)Détection d’une victoire (horizontale)
Maintenant qu’il est possible de jouer effectivement à notre puissance 4, il faut pouvoir détecter une victoire pour mettre fin à la partie en cours. Pour se faire, on va simplifier le problème en le décomposant au maximum.
Dans un premier temps, on s’intéresse à la détection d’une victoire horizontale. Pour cela, on va s’aider de deux fonctions :
une fonction
check_row_victoryqui prend en entrée une ligne du puissance 4 (i.e. une liste de taille 7) et retourneTruesi jamais 4 pions consécutifs de même couleur se trouvent sur la ligne, etFalsesinonune fonction
check_horizontal_victoryqui prend en entrée une grille complète et retourneTruesi jamais une ligne de la grille remplit la condition précédente, etFalsesinon
Résultat attendu
# Détection d'une victoire (horizontale) sur une ligne
ligne1 = [" ", "R", "R", "R", "J", "J", " "]
ligne2 = [" ", "R", "R", "R", "R", "J", " "]
print(solutions.check_row_victory(ligne1)) # Renvoie False
print() # Retour à la ligne
print(solutions.check_row_victory(ligne2)) # Renvoie True# Détection d'une victoire (horizontale) sur une grille
grid_solution = solutions.initialize_grid() # Initialisation
print(solutions.check_horizontal_victory(grid_solution)) # Renvoie False
print() # Retour à la ligne
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=2, disc_color="R")
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=3, disc_color="R")
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=4, disc_color="R")
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=5, disc_color="R")
solutions.display_grid(grid_solution)
print() # Retour à la ligne
print(solutions.check_horizontal_victory(grid_solution)) # Renvoie TrueA vous de jouer !
def check_row_victory(ligne):
# Votre code ici# Vérification du résultat
row1 = [" ", "R", "R", "R", "J", "R", " "]
row2 = [" ", "R", "R", "R", "R", "J", " "]
print(check_row_victory(row1)) # Renvoie False
print(check_row_victory(row2)) # Renvoie Truedef check_horizontal_victory(grid):
# Votre code ici# Vérification du résultat
grid = initialize_grid() # Initialisation
print(check_horizontal_victory(grid)) # Renvoie False
grid = make_move(grid=grid, column_to_play=2, disc_color="R")
grid = make_move(grid=grid, column_to_play=3, disc_color="R")
grid = make_move(grid=grid, column_to_play=4, disc_color="R")
grid = make_move(grid=grid, column_to_play=5, disc_color="R")
display_grid(grid)
print(check_horizontal_victory(grid)) # Renvoie TrueDétection d’une victoire (verticale)
A présent, on s’intéresse à la détection d’une victoire verticale. Par rapport à la situation précédente, la difficulté est que l’on ne peut pas directement boucler sur les colonnes. On va donc construire une fonction check_vertical_victory qui, pour chaque colonne :
récupère les éléments de la colonne dans une liste
applique à cette liste la fonction
check_row_victorypour vérifier la présence de 4 pions consécutifs de même couleur dans la colonne considérée
Résultat attendu
# Détection d'une victoire (verticale) sur une grille
grid_solution = solutions.initialize_grid() # Initialisation
print(solutions.check_vertical_victory(grid_solution)) # Renvoie False
print() # Retour à la ligne
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=2, disc_color="J")
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=2, disc_color="J")
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=2, disc_color="J")
grid_solution = solutions.make_move(grid=grid_solution, column_to_play=2, disc_color="J")
solutions.display_grid(grid_solution)
print() # Retour à la ligne
print(solutions.check_vertical_victory(grid_solution)) # Renvoie TrueA vous de jouer !
def check_vertical_victory(grid):
# Votre code ici# Vérification du résultat
grid = initialize_grid() # Initialisation
print(check_vertical_victory(grid)) # Renvoie False
print() # Retour à la ligne
grid = make_move(grid=grid, column_to_play=2, disc_color="J")
grid = make_move(grid=grid, column_to_play=2, disc_color="J")
grid = make_move(grid=grid, column_to_play=2, disc_color="J")
grid = make_move(grid=grid, column_to_play=2, disc_color="J")
display_grid(grid)
print() # Retour à la ligne
print(check_vertical_victory(grid)) # Renvoie TrueFin de partie
Dans notre version simplifiée du puissance 4, on peut à présent déclarer la fin de partie : dès lors qu’une victoire horizontale ou verticale est détectée.
On va donc pour commencer créer une fonction victoire qui prend la grille en entrée et renvoie True si une victoire horizontale ou verticale est détectée, et False sinon.
Dans l’idéal, on voudrait ne pas avoir à tester manuellement après chaque coup si la partie est terminée afin de limiter la duplication de code. On va donc ensuite créer une fonction make_move_and_check_victory qui :
prend en entrée les mêmes inputs que la fonction
tourva appeler la fonction
tourpour réaliser le tour de jeuva tester après le tour de jeu si une victoire est détectée via la fonction
victoire. Si une victoire est détectée, la fonction imprime “FIN DE PARTIE”.
Résultat attendu
grid_solution = solutions.initialize_grid() # Initialisation
print("Tour 1")
grid_solution = solutions.make_move_and_check_victory(grid=grid_solution, column_to_play=2, disc_color="J")
print("Tour 2")
grid_solution = solutions.make_move_and_check_victory(grid=grid_solution, column_to_play=2, disc_color="J")
print("Tour 3")
grid_solution = solutions.make_move_and_check_victory(grid=grid_solution, column_to_play=2, disc_color="J")
print("Tour 4")
grid_solution = solutions.make_move_and_check_victory(grid=grid_solution, column_to_play=2, disc_color="J")A vous de jouer !
def check_victory(grid):
# Votre code icidef make_move_and_check_victory(grille, column_to_play, disc_color):
grid = copy.deepcopy(grid)
# Votre code ici
return grid# Vérification du résultat
grid = initialize_grid() # Initialisation
print("Tour 1")
grid = make_move_and_check_victory(grid=grid, column_to_play=2, disc_color="J")
print("Tour 2")
grid = make_move_and_check_victory(grid=grid, column_to_play=2, disc_color="J")
print("Tour 3")
grid = make_move_and_check_victory(grid=grid, column_to_play=2, disc_color="J")
print("Tour 4")
grid = make_move_and_check_victory(grid=grid, column_to_play=2, disc_color="J")