First commit: minmax and alphabeta implemented
This commit is contained in:
commit
45abd8c7eb
7 changed files with 593 additions and 0 deletions
187
src/classes/Engines.py
Normal file
187
src/classes/Engines.py
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue