- Implémentation complète de minimax
- Meilleure gestion des erreurs
This commit is contained in:
parent
4969221f23
commit
28fa26c6d6
1 changed files with 56 additions and 11 deletions
67
minimax.py
67
minimax.py
|
@ -13,12 +13,15 @@ class Morpion():
|
||||||
- Le joueur B se nomme `O`.
|
- Le joueur B se nomme `O`.
|
||||||
- Le plateau à une taille `3x3`.
|
- Le plateau à une taille `3x3`.
|
||||||
"""
|
"""
|
||||||
|
self.erreur = False
|
||||||
if len(joueurA) != 1 or len(joueurB) != 1: # Gestion erreur nom des joueurs
|
if len(joueurA) != 1 or len(joueurB) != 1: # Gestion erreur nom des joueurs
|
||||||
print("Nom des joueurs invalide.")
|
print("Nom des joueurs invalide.")
|
||||||
|
self.erreur = True
|
||||||
return
|
return
|
||||||
self.plateau: list = self._definitionPlateau(taille[0], taille[1])
|
self.plateau: list = self._definitionPlateau(taille[0], taille[1])
|
||||||
if len(self.plateau) == 0: # Gestion erreur du plateau
|
if len(self.plateau) == 0: # Gestion erreur du plateau
|
||||||
print("Taille du plateau invalide.")
|
print("Taille du plateau invalide.")
|
||||||
|
self.erreur = True
|
||||||
return
|
return
|
||||||
self.nbCasesGagnantes = self._recuperationNbCasesGagnantes() # définit combien de cases les joueurs doivent aligner pour gagner
|
self.nbCasesGagnantes = self._recuperationNbCasesGagnantes() # définit combien de cases les joueurs doivent aligner pour gagner
|
||||||
self.joueurA = joueurA # définit le joueur A
|
self.joueurA = joueurA # définit le joueur A
|
||||||
|
@ -164,10 +167,10 @@ class Morpion():
|
||||||
"""Renvoie les coordonnées d'une case."""
|
"""Renvoie les coordonnées d'une case."""
|
||||||
return ((numero - 1) // len(self.plateau[0]), (numero - 1) % len(self.plateau[0]))
|
return ((numero - 1) // len(self.plateau[0]), (numero - 1) % len(self.plateau[0]))
|
||||||
|
|
||||||
def _placementPiece(self, joueur: str, n: int) -> None:
|
def _placementPiece(self, valeur, n: int) -> None: # valeur est une Union de str et int car il peut être ammené à être modifié avec Minimax
|
||||||
"""Place la pièce d'un joueur dans le plateau."""
|
"""Place la pièce d'un joueur dans le plateau."""
|
||||||
x, y = self._coordonneesCaseDepuisNumero(n)
|
x, y = self._coordonneesCaseDepuisNumero(n)
|
||||||
self.plateau[x][y] = joueur
|
self.plateau[x][y] = valeur
|
||||||
|
|
||||||
def _demandeCase(self, joueur) -> int:
|
def _demandeCase(self, joueur) -> int:
|
||||||
"""Demande au joueur sur quelle case il veut poser sa pièce."""
|
"""Demande au joueur sur quelle case il veut poser sa pièce."""
|
||||||
|
@ -231,24 +234,65 @@ class Morpion():
|
||||||
|
|
||||||
class Minimax(Morpion):
|
class Minimax(Morpion):
|
||||||
"""Définition de l'algorithme Minimax."""
|
"""Définition de l'algorithme Minimax."""
|
||||||
def __init__(self, joueurA: str = 'X', joueurB: str = 'O', taille: tuple = (3, 3)) -> None:
|
def __init__(self, humain: str = 'X', ordinateur: str = 'O', taille: tuple = (3, 3)) -> None:
|
||||||
"""
|
"""
|
||||||
Initalise la classe Minimax héritant du Morpion.
|
Initalise la classe Minimax héritant du Morpion.
|
||||||
- Taille par défaut : `3x3`.
|
- Taille par défaut : `3x3`.
|
||||||
- Joueur A est l'humain.
|
- Joueur A est l'humain.
|
||||||
- Joueur B est l'ordinateur.
|
- Joueur B est l'ordinateur.
|
||||||
"""
|
"""
|
||||||
super().__init__(joueurA, joueurB, taille = taille)
|
super().__init__(humain, ordinateur, taille = taille)
|
||||||
|
if self.erreur: # en cas d'erreur dans la classe parent
|
||||||
|
return
|
||||||
|
self.humain = self.joueurA
|
||||||
|
self.robot = self.joueurB
|
||||||
|
|
||||||
def _demandeCaseB(self) -> int:
|
def _demandeCaseB(self) -> int:
|
||||||
"""Utilise l'algorithme `Minimax` pour jouer le coup du joueur B."""
|
"""Utilise l'algorithme `Minimax` pour jouer le coup du joueur B."""
|
||||||
return self.minimax()
|
return self.minimax(self.robot)[1]
|
||||||
|
|
||||||
def minimax(self) -> list:
|
def _terminerOuEgaliter(self) -> bool:
|
||||||
"""
|
"""Retourne vrai si il y a un gagnant ou si le plateau est plein."""
|
||||||
Fonction Minimax qui décide quel case est la plus intéressante.
|
return not len(self.gagnant) == 0 or self._egalite()
|
||||||
"""
|
|
||||||
return super()._demandeCaseB()
|
def _coupsPossibles(self) -> list:
|
||||||
|
coups = []
|
||||||
|
for i in range(len(self.plateau)):
|
||||||
|
for j in range(len(self.plateau[i])):
|
||||||
|
if not self._caseOccupee(i, j):
|
||||||
|
coups.append(self.plateau[i][j])
|
||||||
|
return coups
|
||||||
|
|
||||||
|
def _ennemi(self, joueur) -> list:
|
||||||
|
if joueur == self.humain:
|
||||||
|
return self.robot
|
||||||
|
return self.humain
|
||||||
|
|
||||||
|
def minimax(self, joueur: str, profondeur: int = 0) -> list:
|
||||||
|
"""Fonction Minimax qui décide quel case est la plus intéressante."""
|
||||||
|
# return super()._demandeCaseB() # On ne change rien au comportement du Morpion
|
||||||
|
if joueur == self.robot:
|
||||||
|
meilleursCas = -10
|
||||||
|
else:
|
||||||
|
meilleursCas = 10
|
||||||
|
if self._terminerOuEgaliter():
|
||||||
|
if self.gagnant == self.humain :
|
||||||
|
return -10 + profondeur, None
|
||||||
|
elif self._egalite():
|
||||||
|
return 0, None
|
||||||
|
elif self.gagnant == self.robot:
|
||||||
|
return 10 - profondeur, None
|
||||||
|
for numero in self._coupsPossibles():
|
||||||
|
self._placementPiece(joueur, numero)
|
||||||
|
valeurEvaluation, _ = self.minimax(self._ennemi(joueur), profondeur + 1)
|
||||||
|
self._placementPiece(numero, numero)
|
||||||
|
if joueur == self.robot:
|
||||||
|
if valeurEvaluation > meilleursCas:
|
||||||
|
meilleursCas, meilleurNumero = valeurEvaluation, numero
|
||||||
|
else:
|
||||||
|
if valeurEvaluation < meilleursCas:
|
||||||
|
meilleursCas, meilleurNumero = valeurEvaluation, numero
|
||||||
|
return meilleursCas, meilleurNumero
|
||||||
|
|
||||||
if __name__ == "__main__": # Si on lance directement le fichier et on s'en sert pas comme module
|
if __name__ == "__main__": # Si on lance directement le fichier et on s'en sert pas comme module
|
||||||
"""
|
"""
|
||||||
|
@ -286,5 +330,6 @@ if __name__ == "__main__": # Si on lance directement le fichier et on s'en sert
|
||||||
else:
|
else:
|
||||||
Minimax(sys.argv[0], sys.argv[1], tuple([int(i) for i in sys.argv[2].split('x')])).jouer() # On spécifie les joueurs et la taille du tableau
|
Minimax(sys.argv[0], sys.argv[1], tuple([int(i) for i in sys.argv[2].split('x')])).jouer() # On spécifie les joueurs et la taille du tableau
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Un argument n'a pas été compris ({e})... Lancement du Morpion avec les paramètres par défaut.")
|
if not "list index out of range" in str(e):
|
||||||
|
print(f"Un argument n'a pas été compris ({e})... Lancement du Morpion avec les paramètres par défaut.")
|
||||||
Minimax().jouer() # On lance la partie à l'instanciation du Morpion
|
Minimax().jouer() # On lance la partie à l'instanciation du Morpion
|
||||||
|
|
Reference in a new issue