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