First commit: minmax and alphabeta implemented

This commit is contained in:
Yorick Barbanneau 2023-12-14 21:58:35 +01:00
commit 45abd8c7eb
7 changed files with 593 additions and 0 deletions

187
src/classes/Engines.py Normal file
View file

@ -0,0 +1,187 @@
import random, math
class PlayerEngine:
def __init__(self, logger, options: dict = {}):
# init logger do display informations
self.logger = logger
self.options = options
self.logger.info("Init engine {}".format(self.__class__.__name__))
def get_move(self, board):
self.logger.info("engine: {} - player:{}".format(
self.__class__.__name__,
self.get_player_name(board._nextPlayer)
))
@staticmethod
def get_player_name(player):
return 'White (O)' if player is 2 else 'Black (X)'
class RandomPlayerEngine(PlayerEngine):
def get_move(self, board):
super().get_move(board)
moves = board.legal_moves()
return random.choice(moves)
class HumanPlayerEngine(PlayerEngine):
def get_move(self, board):
super()
move = None
while move is None:
user_input = input("Please enter player {} move: ".format(
self.get_player_name(board._nextPlayer)
))
move = self.validate_input(user_input, board, player)
print("{}".format(move))
return move
@staticmethod
def validate_input(input, board):
if input == 'print':
print("\n{}".format(board.__str__))
return None
if input == 'help':
print('{}'.format(board.legal_moves()))
return None
if len(input) != 2:
return None
x = int(input[0])
y = int(input[1])
if not board.is_valid_move(int(player), x, y):
return None
return [board._nextPlayer, x, y]
class MinmaxPlayerEngine(PlayerEngine):
def get_move(self, board):
super().get_move(board)
value = -math.inf
nodes = 1
leafs = 0
move = ''
for m in board.legal_moves():
board.push(m)
v, n, l = self.checkMinMax(board, False, self.options['depth'] - 1)
if v > value:
value = v
move = m
self.logger.debug("found a better move: {} (heuristic:{})".format(
move,
value
))
nodes += n
leafs += l
board.pop()
self.logger.debug("Tree statistics:\n\tnodes:{}\n\tleafs:{}".format(
nodes,
leafs
))
return move
def checkMinMax(self, board, friend_move:bool, depth :int = 2):
nodes = 1
leafs = 0
move = ''
if depth == 0:
leafs +=1
return board.heuristique(), nodes, leafs
if friend_move:
value = -math.inf
for m in board.legal_moves():
board.push(m)
v, n, l = self.checkMinMax(board, False, depth - 1)
if v > value:
value = v
nodes += n
leafs += l
board.pop()
else:
value = math.inf
for m in board.legal_moves():
board.push(m)
v, n, l = self.checkMinMax(board, True, depth - 1)
if v < value:
value = v
board.pop();
nodes += n
leafs += l
return value, nodes, leafs
class AlphabetaPlayerEngine(PlayerEngine):
def get_move(self, board):
super().get_move(board)
alpha = -math.inf
beta = math.inf
nodes = 1
leafs = 0
move = []
value = -math.inf
for m in board.legal_moves():
board.push(m)
v, n, l = self.checkAlphaBeta(board, False, self.options['depth'] - 1, alpha, beta)
board.pop()
alpha = max(alpha,v)
nodes += n
leafs += l
if alpha >= value:
value = alpha
move = m
self.logger.debug("found a better move: {} (heuristic:{})".format(
move,
alpha
))
self.logger.debug("Tree statistics:\n\tnodes:{}\n\tleafs:{}".format(
nodes,
leafs
))
return move
def checkAlphaBeta(self, board, friend_move : bool, depth, alpha, beta):
nodes = 1
leafs = 0
if depth == 0 :
leafs +=1
return board.heuristique(), nodes, leafs
if friend_move:
value = -math.inf
for m in board.legal_moves():
board.push(m)
v, n, l = self.checkAlphaBeta(board, False, depth - 1, alpha, beta)
board.pop()
alpha = max(value,v)
nodes += n
leafs += l
if alpha >= beta:
self.logger.debug("Alpha pruning - alpha:{} / beta:{}".format(
alpha,
beta
))
return beta, nodes, leafs
return alpha, nodes, leafs
else:
value = math.inf
for m in board.legal_moves():
board.push(m)
v, n, l = self.checkAlphaBeta(board, True, depth - 1, alpha, beta)
board.pop();
beta = min(beta,v)
nodes += n
leafs += l
if alpha >= beta:
self.logger.debug("Beta pruning - alpha:{} / beta:{}".format(
alpha,
beta
))
return alpha, nodes, leafs
return beta, nodes, leafs