Morpion

Étape 1

Pour ce premier projet de la semaine, vous allez concevoir un jeu de Morpion ainsi qu'une intelligence artificielle pour ce jeu.

Le Morpion est un jeu où deux joueurs s'affrontent autour d'une grille de dimensions 3 par 3. Chacun leur tour, les joueurs doivent poser un jeton dans une case libre du plateau. Les jetons du premier joueur sont représentés par des croix, alors que ceux du deuxième joueur sont représentés par des cercles. Le premier joueur qui arrive à aligner trois de ses jetons en ligne, en colonne ou en diagonale est déclaré gagnant.

Fichiers du projet

Pour ce projet, un ensemble de fichiers vous est fourni. Cliquez sur le lien ci-dessous pour les télécharger.

Télécharger les fichiers du projet.

Une fois l'archive téléchargée, ouvrez-la et déplacez les fichiers sur votre espace de stockage persistant dans le dossier projets/tictactoe.

Les fichiers sont les suivants :

Ces quatres fichiers sont à mettre dans votre dossier projets/tictactoe. Pour cette première étape, nous allons principalement modifier le fichier state.py. Le fichier solver.py sera le sujet de la deuxième étape.

Description

Pour la premier étape, vous allez coder les règles du jeu du Morpion.

Pour cela, vous aurez ces fonctions à implémenter dans le fichier state.py :

Avant de se lancer dans l'implémentation de ces fonctions, il est important de réflechir à comment représenter l'état d'un jeu de Morpion, et en particulier l'état du plateau de jeu.

Représentation du plateau de jeu

Le jeu du morpion se joue autour d'un plateau de 3 lignes et 3 colonnes. Chacune des cases du plateau peut soit être vide, soit contenir un jeton de joueur (soit une croix, soit un cercle). Pour concevoir un programme qui permet de jouer au Morpion, il nous faudra trouver un moyen de représenter de tels plateaux en Python.

Remarque: Vous êtes totalement libres de la représentation que vous utiliserez pour le plateau de jeu tant que vous pouvez implémenter les fonctions décrites plus haut. Cependant, dans cette section, nous vous présentons une façon de procéder que vous pouvez adopter.

Pour représenter un plateau de jeu de morpion, il est possible d'utiliser une liste de listes. Chacune des listes représente une colonne du plateau qui contiendra trois valeurs, une pour chaque case.

Cases

Dans chaque case du plateau, on utilisera la valeur 0 pour indiquer une case vide, la valeur 1 pour indiquer une case occupée par un jeton du premier joueur (croix) et la valeur -1 pour une case occupée par le deuxième joueur (cercle).

Coordonnées

Les cases du plateau de jeu ont toute une position selon un système de coordonnées. La case en haut à gauche est en position 0, 0, celle au milieu en haut est 1, 0, jusqu'à la position en bas à droite qui est 2, 2.

Fonction à implémenter

Ci-dessous vous trouverez la description des fonctions à implémenter.

empty_board

Comme première fonction, implémenter la fonction empty_board. La fonction doit retourner une valeur qui représente un plateau vide.

def empty_board():
    pass

Selon la représentation suggérée, il s'agira d'une liste de trois colonne, chaque colonne étant une liste de trois 0.

token_at

La deuxième fonction à implémenter est la fonction token_at. La fonction a trois paramètres :

La fonction doit retourner :

def token_at(board, x, y):
    pass

Suivant la représentation du plateau choisie, cette fonction sera plus ou moins triviale à implémenter.

is_board_full

La troisième fonction à implémenter est is_board_full. La fonction a un unique paramètre qui représente le plateau de jeu. La fonction doit retourner True si le plateau est totalement rempli et False s'il reste au moins une case de vide.

def is_board_full(board):
    pass

Pour cela, il vous faudra parcourir les différentes cases du plateau et indiquez si le tableau est totalement rempli ou non.

free_cells

La fonction suivante à implémenter est free_cells. La fonction a un unique paramètre, board, pour le plateau de jeu. La fonction doit retourner une liste des positions des cases vides du plateau. Chaque position sera représentée par un tuple (x, y)x est l'abscisse de la position et y l'ordonnée.

def free_cells(board):
    pass

Pour cela, il vous faudra créer une liste de cases vides, initialement vide, puis parcourir les différentes cases du plateau et ajouter au fur et à mesure les cases à la liste.

play

La fonction suivante est la fonction play. Cette fonction est la plus complexe à implémenter pour cette étape.

La fonction a quatre paramètres :

On partira du principe que la case choisie est vide et qu'il n'y a pas encore de gagnant.

La fonction doit mettre à jour le plateau de jeu et vérifier si le coup amène à une victoire du joueur. Pour cela, il faut vérifier si par ce coup le joueur arrive à aligner trois jetons sur la même ligne, la même colonne, ou la même diagonale.

def play(board, x, y, player):
    pass

unplay

La dernière fonction à implémenter est unplay. La fonction a trois paramètres :

La fonction doit modifier le plateau afin d'enlever le jeton à la position indiquée. Bien que cela ne soit pas une action valide au Morpion, nous pourrons faire usage de cette fonction dans la prochaine étape, lorsque nous implémenterons une intelligence artificielle pour le Morpion.

Essayer votre code

Une fois les quelques fonctions ci-dessus implémentées, vous devriez pouvoir lancer une partie de Morpion en exécutant le script main.py dans le dossier du projet.

Par défaut, un jeu se lance dans une nouvelle fenêtre avec les coups des deux joueurs entrés par le biais de l'interface graphique. Il suffit de cliquer sur une case pour y jouer un coup.

Changer les joueurs

Dans ce même fichier, vous pouvez modifier l'appel à la fonction start_ui pour changer le comportement des joueurs. La fonction start_ui prend deux argument, un par joueur. L'argument indique la façon de jouer du joueur.

L'argument None sert à indiquer qu'il n'y a pas de fonction pour déterminer les coups à jouer, c'est donc à l'utilisateur de les rentrer. En changeant None par random_move, vous indiquez que les coups du joueur en question seront aléatoires.

Par exemple, pour que le joueur 1 joue de manière aléatoire contre un joueur 2 controlé par l'utilisateur, on aura:

if __name__ == '__main__':
    start_ui(random_move, None)

En plus de None et random_move, il est possible d'utiliser la valeur best_move. Cependant, la fonction best_move n'est pas encore implémentée. Cela sera votre travail pour l'étape suivante du projet !