2024-09-12 11:46:27 +02:00
|
|
|
class Game(int):
|
|
|
|
|
|
|
|
def __init__(self, start):
|
2024-09-17 16:47:14 +02:00
|
|
|
#Game variables
|
2024-09-12 11:46:27 +02:00
|
|
|
self.matches = start # number of matches
|
|
|
|
self.state = 0 #-1 = lost, 0 = draw, +1 = won
|
|
|
|
self.playerTurn = 1 # 1 = player 1 is playing, 2 = player 2 is playing
|
2024-09-17 16:47:14 +02:00
|
|
|
#Algorithm variables
|
|
|
|
self.exploreMaxCalls = 0 # Call counter of exploreMax
|
|
|
|
self.exploreMinCalls = 0 # Call counter of exploreMax
|
|
|
|
self.firstPlayerWinning = False # Result of MinMax
|
2024-09-12 11:46:27 +02:00
|
|
|
|
|
|
|
def miniMax(self):
|
2024-09-17 16:47:14 +02:00
|
|
|
'''
|
|
|
|
`miniMax` function starts the MiniMax algorithm
|
|
|
|
|
|
|
|
Params :
|
|
|
|
self -- the current instance of the game
|
|
|
|
|
|
|
|
Return :
|
|
|
|
-1 = lost or 0 = draw or +1 = won -- depends on the possibility to win of the player
|
|
|
|
'''
|
2024-09-12 11:46:27 +02:00
|
|
|
if(self.playerTurn == 1):
|
|
|
|
res = self.exploreMax(self.matches)
|
|
|
|
if(self.playerTurn == 2):
|
|
|
|
res = self.exploreMin(self.matches)
|
2024-09-17 16:47:14 +02:00
|
|
|
if(res == -1):
|
|
|
|
self.firstPlayerWinning = False
|
|
|
|
else:
|
|
|
|
self.firstPlayerWinning = True
|
2024-09-12 11:46:27 +02:00
|
|
|
return res
|
|
|
|
|
|
|
|
def exploreMax(self, n):
|
2024-09-17 16:47:14 +02:00
|
|
|
'''
|
|
|
|
`exploreMax` function tries to find if a player has possibilities to win the game
|
|
|
|
|
|
|
|
Params :
|
|
|
|
self -- the current instance of the game
|
|
|
|
n -- a number of matches
|
|
|
|
|
|
|
|
Return :
|
|
|
|
-1 = lose or 0 = draw or +1 = win -- depends on the possibility to win of the player
|
|
|
|
'''
|
|
|
|
self.exploreMaxCalls += 1
|
2024-09-12 11:46:27 +02:00
|
|
|
bestVal = -1
|
|
|
|
if(n<=0):
|
|
|
|
return 1
|
|
|
|
for i in range (1,4):
|
|
|
|
res = self.exploreMin(n-i)
|
|
|
|
if(res > bestVal):
|
|
|
|
bestVal = res
|
|
|
|
if(bestVal == 1):
|
|
|
|
break
|
|
|
|
return bestVal
|
|
|
|
|
|
|
|
def exploreMin(self, n):
|
2024-09-17 16:47:14 +02:00
|
|
|
'''
|
|
|
|
`exploreMin` function tries to find if a player has possibilities to win the game
|
|
|
|
|
|
|
|
Params :
|
|
|
|
self -- the current instance of the game
|
|
|
|
n -- a number of matches
|
|
|
|
|
|
|
|
Return :
|
|
|
|
-1 = lose or 0 = draw or +1 = win -- depends on the possibility to win of the player
|
|
|
|
'''
|
|
|
|
self.exploreMinCalls += 1
|
2024-09-12 11:46:27 +02:00
|
|
|
worstVal = 1
|
|
|
|
if(n<=0):
|
|
|
|
return -1
|
|
|
|
for i in range (1,4):
|
|
|
|
res = self.exploreMax(n-i)
|
|
|
|
if(res < worstVal):
|
|
|
|
worstVal = res
|
|
|
|
if(worstVal == -1):
|
|
|
|
break
|
|
|
|
return worstVal
|
2024-09-17 16:47:14 +02:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
'''
|
|
|
|
`__str__` function overrides the default function to get a custom display when printing the game object
|
|
|
|
|
|
|
|
Params :
|
|
|
|
self -- the current instance of the game
|
|
|
|
|
|
|
|
Return :
|
|
|
|
string -- contains all the game and algorithm values
|
|
|
|
'''
|
|
|
|
content = ( "-----------------------\n"
|
|
|
|
"Game and algorithm data\n"
|
|
|
|
"-----------------------\n"
|
|
|
|
"== Game data ==\n"
|
|
|
|
f"Remaining matches : {self.matches}\n"
|
|
|
|
f"Game state : {self.state}\n"
|
|
|
|
f"Actual player : {self.playerTurn}\n"
|
|
|
|
"== Algorithm data ==\n"
|
|
|
|
f"exploreMax calls = {self.exploreMaxCalls}\n"
|
|
|
|
f"exploreMin calls = {self.exploreMinCalls}\n"
|
|
|
|
f"First player winning ? = {self.firstPlayerWinning}\n"
|
|
|
|
)
|
|
|
|
return content
|
2024-09-12 11:46:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
game = Game(5) # Creating Nim game with 5 matches
|
|
|
|
game.miniMax() # Checking if the player 1 is winning
|
2024-09-17 16:47:14 +02:00
|
|
|
print(game) # Displays game and algorithm data
|