Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
|
7a10317630 | ||
|
88ecb3a2e2 | ||
|
0442e04eaa | ||
|
b951a51d82 | ||
|
65ff0bd56f | ||
|
e7a9b2ff0c | ||
|
6a53ef0852 | ||
|
72d874151c | ||
|
7f22bdf78d |
6 changed files with 283 additions and 0 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
# Idea
|
||||||
|
.idea
|
||||||
|
|
||||||
# ---> Python
|
# ---> Python
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|
BIN
marinbad/Marienbad.pdf
Normal file
BIN
marinbad/Marienbad.pdf
Normal file
Binary file not shown.
126
marinbad/ai.py
Normal file
126
marinbad/ai.py
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
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
|
42
marinbad/board.py
Normal file
42
marinbad/board.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class Board:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def show_board(board):
|
||||||
|
"""
|
||||||
|
Display the board o screen
|
||||||
|
:param board: the board
|
||||||
|
:type board list
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
for index, val in enumerate(board):
|
||||||
|
print(f"Row {index + 1}: {val}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def nb_allumettes(board: list):
|
||||||
|
"""
|
||||||
|
Return the number of remaining allumettes
|
||||||
|
:param board: the board
|
||||||
|
:type board list
|
||||||
|
:return: int
|
||||||
|
"""
|
||||||
|
return sum(board)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def retirer(board, row, stick):
|
||||||
|
"""
|
||||||
|
Computer player move on the board
|
||||||
|
:param board: the board
|
||||||
|
:type board list
|
||||||
|
|
||||||
|
:param row: the selected row to remove allumettes from
|
||||||
|
:type row: int
|
||||||
|
|
||||||
|
:param stick: the number of allumettes to remove
|
||||||
|
:type stick: int
|
||||||
|
|
||||||
|
:return: board
|
||||||
|
"""
|
||||||
|
board[int(row) - 1] = board[int(row) - 1] - int(stick)
|
||||||
|
return board
|
79
marinbad/game_numy.py
Normal file
79
marinbad/game_numy.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
from AI import AI
|
||||||
|
from Board import Board
|
||||||
|
from os import system
|
||||||
|
from random import shuffle
|
||||||
|
|
||||||
|
|
||||||
|
def play(board: list, **kwargs):
|
||||||
|
def strArray(array):
|
||||||
|
"""
|
||||||
|
Convert an array to a string array and add one to every value
|
||||||
|
Used to stringigy range() and start value from 1
|
||||||
|
:param array:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return [str(el + 1) for el in array]
|
||||||
|
|
||||||
|
def intable(value):
|
||||||
|
"""
|
||||||
|
Return True if value can be cast to integer
|
||||||
|
:param value: The value to convert in integer
|
||||||
|
:type value str
|
||||||
|
:return: bool
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
int(value)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Filter row input
|
||||||
|
row = "string"
|
||||||
|
strRange = strArray(range(len(board)))
|
||||||
|
while not(intable(row)) or not(row in strRange):
|
||||||
|
row = input(f"Choisissez une rangée entre 1 et {len(board)}: ")
|
||||||
|
row = int(row)
|
||||||
|
|
||||||
|
# Filter remove allumettes
|
||||||
|
remove = "0"
|
||||||
|
while not(intable(remove)) or not("1" <= remove <= str(board[row - 1])):
|
||||||
|
remove = input(f"Choisissez le nombre d'allumettes à retirer entre 1 et {board[row - 1]}: ")
|
||||||
|
remove = int(remove)
|
||||||
|
|
||||||
|
print(f"{kwargs['name']}: L{row} => {remove}")
|
||||||
|
# Return board after player move
|
||||||
|
return game_board.retirer(board, row, remove)
|
||||||
|
|
||||||
|
|
||||||
|
game_board = Board()
|
||||||
|
|
||||||
|
players = [
|
||||||
|
{
|
||||||
|
"name": "Jean Yves",
|
||||||
|
"func": play
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gérald",
|
||||||
|
"func": AI().compute
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
# Choisi le 1er joueur
|
||||||
|
shuffle(players)
|
||||||
|
|
||||||
|
# Creation du plateau de jeu
|
||||||
|
print("Entrez une liste sous cette forme: X Y Z A B C")
|
||||||
|
allumettes = [int(el) for el in input(">>> ").split(" ")]
|
||||||
|
|
||||||
|
print(f"Voici l'ordre des joueurs: {' => '.join([player['name'] for player in players])}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for player in players:
|
||||||
|
game_board.show_board(allumettes)
|
||||||
|
|
||||||
|
allumettes = player["func"](allumettes, name=player["name"])
|
||||||
|
|
||||||
|
if game_board.nb_allumettes(allumettes) == 0:
|
||||||
|
print(f"Le {player['name']} à gagné !!")
|
||||||
|
exit(0)
|
||||||
|
print("")
|
33
player.py
Normal file
33
player.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
def get_row(rows):
|
||||||
|
i = 0
|
||||||
|
while not (i in range(1, rows + 1)):
|
||||||
|
print(f"Sur quel ligne voulez vous jouer ? (un nombre entre 1..{rows}) :")
|
||||||
|
try:
|
||||||
|
i = int(input())
|
||||||
|
except ValueError:
|
||||||
|
i = 0
|
||||||
|
return i - 1
|
||||||
|
|
||||||
|
|
||||||
|
def get_value(values):
|
||||||
|
i = 0
|
||||||
|
while not (i in range(1, values + 1)):
|
||||||
|
print(f"Combien de pièce voulez vous jouer (un nombre entre 1..{values}, c pour annuler) :")
|
||||||
|
try:
|
||||||
|
tmp = input()
|
||||||
|
if tmp == "c":
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
i = int(tmp)
|
||||||
|
except ValueError:
|
||||||
|
i = 0
|
||||||
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
def get_player_move(name, game):
|
||||||
|
print(f"{name} c'est à vous !")
|
||||||
|
value = 0
|
||||||
|
while value == 0:
|
||||||
|
row = get_row(len(game))
|
||||||
|
value = get_value(game[row])
|
||||||
|
return row, value
|
Loading…
Reference in a new issue