126 lines
4 KiB
Python
126 lines
4 KiB
Python
from random import choice, randint
|
|
|
|
|
|
class AI:
|
|
"""Ordinateur pour le jeu de Marienbad"""
|
|
def __init__(self):
|
|
self.row = 0
|
|
self.removed = 0
|
|
|
|
@staticmethod
|
|
def _intToBin(integer):
|
|
"""
|
|
Convertie un nombre en base 10 en base 2
|
|
:param integer: Le nombre à convertir
|
|
:type integer: int
|
|
:return: int
|
|
"""
|
|
return int(bin(integer)[2:])
|
|
|
|
def _calculSomme(self, liste: list):
|
|
"""
|
|
Convertie une liste de décimal en liste de binaire
|
|
Fait la somme des element binaire et la retranscrie en décimal
|
|
:param liste: une liste sur laquelle effectuer l'opération
|
|
:type liste: list
|
|
:return: list
|
|
"""
|
|
array = [self._intToBin(el) for el in liste] # int array => bin array
|
|
S = (list(str(sum(array)))) # Somme en base 2
|
|
S = [int(s) for s in S] # str array => int array
|
|
return S
|
|
|
|
@staticmethod
|
|
def _sommeNIM(liste: list):
|
|
"""
|
|
Remplace chaque digit par 0 si le digit est pair, par 1 si le digit est impair
|
|
:param liste: une liste sur laquelle effectuer l'opération
|
|
:type liste: list
|
|
:return: list
|
|
"""
|
|
array = [str(el % 2) for el in liste]
|
|
return array
|
|
|
|
@staticmethod
|
|
def _isSafe(liste: list):
|
|
"""
|
|
Retourne True si le nombre convertie en binaire vaut zéro.
|
|
Retourne False sinon
|
|
:param liste: une liste sur laquelle effectuer l'opération
|
|
:type liste: list
|
|
:return: int
|
|
"""
|
|
string = ""
|
|
for el in liste:
|
|
string += str(el)
|
|
return int(string, 2) == 0
|
|
|
|
def randomDraw(self, array):
|
|
"""
|
|
Retire une allumette d'une pile au hasard
|
|
:param array: Allumettes
|
|
:type array list
|
|
:return: list
|
|
"""
|
|
rows = [i for i, row in enumerate(array) if row != 0]
|
|
row = choice(rows)
|
|
removed = randint(1, array[row])
|
|
array[row] = array[row] - removed
|
|
self.row = row + 1
|
|
self.removed = removed
|
|
return array
|
|
|
|
def _positionPerdante(self, array, debug=None):
|
|
"""
|
|
Wrapper de méthodes
|
|
:param array: Allumettes
|
|
:type array list
|
|
:param debug: Affiche des info pour le debug
|
|
:type debug bool
|
|
:return: bool
|
|
"""
|
|
S = self._calculSomme(array)
|
|
nimS = self._sommeNIM(S)
|
|
safe = self._isSafe(nimS)
|
|
if debug:
|
|
print("Somme: ", S)
|
|
print("NIM S: ", nimS)
|
|
print("Safe : ", safe)
|
|
return self._isSafe(nimS)
|
|
|
|
def _trouver_position_perdante(self, array: list):
|
|
"""Brut force to find a solution"""
|
|
if self._positionPerdante(array):
|
|
print("Drawing one")
|
|
# Choisi une range au hasard où il reste des allumettes
|
|
return self.randomDraw(array)
|
|
else:
|
|
print("Computing")
|
|
# Tant que position pas perdante pour le joueur
|
|
while not self._positionPerdante(array):
|
|
# Choisi une range au hasard où il reste des allumettes
|
|
rows = [i for i, row in enumerate(array) if row > 0]
|
|
row = choice(rows)
|
|
|
|
# Créer une copie pour le brutforce
|
|
row_backup = array[row]
|
|
removed = 0
|
|
# Brutforce sur chaque rangée jusqu'a trouver une combinaison perdante
|
|
while not self._positionPerdante(array) and array[row] > 0:
|
|
removed += 1
|
|
# On retire une allumette
|
|
array[row] -= 1
|
|
self.row = row + 1
|
|
self.removed = removed
|
|
self.removed = row_backup - array[row]
|
|
|
|
if not self._positionPerdante(array):
|
|
array[row] = row_backup
|
|
|
|
return array
|
|
|
|
def compute(self, liste: list, *args, **kwargs):
|
|
"""Method to call"""
|
|
board = self._trouver_position_perdante(liste)
|
|
print(f"{kwargs['name']}: L{self.row} => {self.removed}")
|
|
return board
|