Vous êtes sur la page 1sur 4

T.D.

nᵒ 5 : Étude du jeu Othello

Implémentation et résolution du jeu d’Othello


Le jeu Othello original (aussi appelé Reversi) se joue sur une grille de 8×8 cases, et avec des pions bicolores (une face blanche,
une face noire).

En commençant par la position initiale, illustrée à gauche de la figure de la page suivante, chaque joueur place un pion avec
la couleur lui appartenant (noire pour le joueur 1, blanche pour le joueur 2) sur la face visible. Il peut le placer sur une case vide
de son choix, à condition que le placement de ce pion provoque le retournement d’au moins un pion adverse. Ces retournements
d’un ou plusieurs pions adverses peuvent avoir lieu pour des pions situés sur la grille dans les 8 directions possibles à partir du
pion posé (horizontales, verticales et diagonales). Un tel retournement a lieu lorsque le pion posé est immédiatement suivi, dans
une direction donnée, d’un nombre quelconque (non nul) de pions adverses puis d’un pion du joueur : les pions adverses ainsi
« enfermés » sont alors retournés et prennent la couleur du joueur. Cette règle est illustrée sur la figure en haut de la page suivante.

Ce sont toujours les noirs qui commencent, à savoir le joueur 1.

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0 0 0
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 7

À gauche, la position de départ. Au milieu, une position en cours de partie (les noirs s’apprêtent à
jouer en (2,4)). À droite, la position après que les noirs ont joué en (2,4) (les pions initialement
blancs mais retournés sur le côté noir après ce coup sont représentés en gris pour l’illustration).

Si l’un des joueurs ne peut pas jouer – c’est-à-dire s’il ne lui est pas possible de provoquer un retournement d’au moins un
pion adverse en posant un de ses pions –, il passe son tour. La partie se termine lorsqu’aucun des joueurs ne peut jouer – ce qui
arrive lorsque la grille est pleine ou composée, mais peut également se produire avant. La partie est alors gagnée par le joueur
qui a le plus de pions de sa couleur sur la grille – sauf en cas d’égalité, auquel cas la partie est nulle.

L’objectif de ce problème est d’implémenter le jeu, et notamment de concevoir un programme capable de jouer intelligemment
à Othello – dans une version légèrement généralisée, où la grille est de taille 2n , n ∈ J2, +∞J –, grâce à l’utilisation de l’algorithme
minimax, avec une fonction d’utilité appropriée.

Une partie en cours sera modélisée par une liste G de 2n listes, chaque liste étant de taille 2n , représentant la grille de jeu de
( )
la façon suivante : pour tout i , j ∈ J0, 2n − 1K 2 , G[i][j] vaut
( )
∗ 0 si la case i , j (ligne i , colonne j ) est vide ;

∗ 1 si la case est occupée par un pion noir (appartenant au joueur 1) ;

∗ 2 si la case est occupée par un pion blanc (appartenant au joueur 2).

1
Partie I : premières fonctions d’implémentation
Question 1. Écrire une fonction init(n), qui prend en argument un entier naturel non nul, et qui renvoie une grille grille corres-
pondant à la position initiale, implémentée, avec la convention indiquée précédemment, par une liste de 2n listes, toutes
de longueur 2n , lesquelles listes doivent être indépendantes les unes des autres (c’est-à-dire que la modification de l’une ne
doit pas provoquer la modification d’une autre).

La position initiale est celle d’une grille dont toutes les lignes sont vides, à l’exception des deux lignes centrales, d’indices
n − 1 et n :

− la ligne centrale supérieure ayant ses cases vides sauf ses deux cases centrales, respectivement blanche et noire ;

− la ligne centrale inférieure ayant ses cases vides sauf ses deux cases centrales, respectivement noire et blanche.

Ainsi par exemple, init(3) devra renvoyer


[[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 2, 1, 0, 0],
[0, 0, 1, 2, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]

Question 2. Écrire une fonction affichage(grille) qui prend en entrée une liste de listes, toutes ces listes étant de même taille,
et affiche la grille qu’elles représentent selon le format ci-dessous, avec le numéro de chaque colonne au-dessus de celle-ci
et, outre la ligne initiale donnant les numéros des colonnes, une ligne par liste, chaque liste étant précédée de son numéro
de ligne et d’une espace horizontale, les contenus de cases d’une même ligne étant de même séparés par une espace.
À l’emplacement d’une case, sera affiché
− un point . si la case a la valeur 0 dans la grille (case vide) ;

− la lettre o si elle a la valeur 1 dans la grille (pion noir) ;

− la lettre x si elle a la valeur 2 dans la grille (pion blanc).

Ainsi, affichage(init(3)) devra provoquer l’affichage suivant.

0 1 2 3 4 5
0 . . . . . .
1 . . . . . .
2 . . x o . .
3 . . o x . .
4 . . . . . .
5 . . . . . .

On utilisera obligatoirement un dictionnaire dont les clefs sont les valeurs possibles des cases de la grille, et les valeurs associées
les caractères qui sont affichés à l’endroit correspondant. Pour cette question, on écrira obligatoirement la documentation, en
donnant les précisions sur la pré-condition omises par l’énoncé.

Question 3. Écrire une fonction est_sur_la_grille(i, j, n), qui prend en argument trois entiers, et renvoie un booléen :
True si le couple (i, j) est un couple d’indices valide d’une grille pour le jeu avec paramètre n (c’est-à-dire d’une grille
de taille 2n ), et False sinon.

Ainsi, est_sur_la_grille(0, 7, 4) doit renvoyer True, et est_sur_la_grille(3, 10, 5), False.

2
Question 4. Il s’agit à présent d’implémenter l’effet d’un coup sur une grille, en commençant par les retournements éventuels de pions
situés à la droite du pion placé.

a) Écrire une fonction retourne_droite(grille, i, j, joueur), qui prend en argument une liste de
listes représentant une grille du jeu, deux indices supposés former un couple valide d’indices de la grille, ainsi qu’un
entier joueur égal à 1 ou 2. Elle renverra la liste des positions des pions situés immédiatement à droite du pion et
qui, selon les règles du jeu, seraient retournés si un pion de la couleur du joueur joueur est effectivement posé en
(i, j). La liste renvoyée sera vide si la pose du pion ne peut provoquer aucun retournement à droite, ou si la case
(i, j) est déjà occupée par un pion.

Ainsi, retourne_droite(init(4), 3, 2, 1) devra renvoyer [(3, 3)] et


retourne_droite(init(4), 4, 3, 1)devra renvoyer [].
b) Sans utiliser la fonction précédente, mais en généralisant son principe, écrire une fonction
retourne(grille, i, j, joueur), possédant la même pré-condition que la fonction précédente, mais
qui renverra la liste des positions de tous les pions dont la pose d’un pion de la couleur du joueur joueur en
(i, j) provoque le retournement. Comme précédemment, la liste renvoyée sera vide si la case (i, j) est déjà
occupée par un pion, ou si le pion ne provoque aucun retournement (auquel cas le coup n’est en fait pas licite).

On pourra écrire une boucle for sur une liste de 8 couples indiquant les 8 directions à explorer, par exemple
(1, -1) dans le cas de l’examen des pions éventuellement à retourner situés immédiatement en bas à droite du
pion à poser.

Ainsi par exemple, si grille1 représente la deuxième grille de la première figure,


retourne(grille1, 2, 4, 1) renverra, à l’ordre près des tuples,

[(1, 3), (1, 5), (2, 5), (3, 4), (4, 4), (5, 4)].

Question 5. Il faut désormais, pour ensuite pouvoir choisir parmi ces coups, d’établir la liste des coups possibles à partir d’un état donné
du jeu, c’est-à-dire des couples d’indices qui sont des indices de la grille et qui provoquent au moins un retournement.
À cet effet, écrire une fonction coups_possibles(grille, joueur), qui prend en argument une liste de listes
représentant une grille de jeu, et un entier représentant un joueur, et qui renvoie une liste de tuples. Chaque tuple comportera
trois éléments : les deux premiers donnant les indices i et j d’une case où les règles du jeu permettent au joueur joueur
de jouer, le troisième la liste des pions à retourner au cas où un pion de couleur joueur est posé en (i, j). On utilisera
la fonction retourne.

Ainsi par exemple, coups_possibles(init(4), 1) renverra, à l’ordre près des triplets,

[(2, 3, [(3, 3)]), (3, 2, [(3, 3)]), (4, 5, [(4, 4)]), (5, 4, [(4, 4)])].

Question 6. Écrire, à l’aide des fonctions précédentes, une fonction partie_aleatoire(n), prenant le paramètre n du jeu comme
unique argument, et qui simule, pour deux joueurs virtuels, une partie aléatoire jusqu’à son terme en choisissant à chaque
étape un coup aléatoire parmi tous les coups possibles pour le joueur dont c’est le tour, s’il y en a – sinon ne fait rien –,
modifie la grille en jouant ce coup, puis fait la même chose pour l’autre joueur, et ainsi de suite jusqu’au terme de la partie
selon les règles du jeu. La fonction renverra la grille obtenue à la fin de la partie.

On pourra utiliser la fonction randrange du module random pour effectuer le tirage aléatoire parmi les coups.

3
Question 7. Calculer, pour plusieurs valeurs de n , et en appelant un grand nombre de fois la fonction partie_aleatoire, le score
moyen (entre 0 et 1) de chaque joueur (1 et 2), à savoir le quotient du score total par le nombre de parties jouées, le score
total étant la somme du score de chaque partie jouée. Le score d’un joueur à l’issue d’une partie est défini comme 1 en cas
1
de victoire, 0 en cas de défaite et 2 en cas de match nul.
Si chaque joueur au hasard, l’avantage est-il à celui qui commence, ou l’autre ?

Vous aimerez peut-être aussi