Add classes and method documentation

This commit is contained in:
Yorick Barbanneau 2023-12-21 00:30:08 +01:00
parent d2ada8734f
commit f927aa1462
2 changed files with 169 additions and 10 deletions

View file

@ -1,6 +1,17 @@
import random, math, time, signal
"""
Base player engine
"""
class PlayerEngine:
"""
init
@param player: black or white player
@param logger: loggig object (display verbose / debug messages
@param heuristic: HeuristicClass object to calculate tree heuristic
@param options: hashtable containing options
"""
def __init__(self, player, logger, heuristic, options: dict = {}):
# init logger do display informations
self.player = player
@ -13,29 +24,65 @@ class PlayerEngine:
self.options
))
"""
get move
@param board: Board
"""
def get_move(self, board):
self.logger.info("engine: {} - player:{}".format(
self.__class__.__name__,
self.get_player_name(self.player)
))
"""
Get possibles player move an apply a Random.shuffle on it (if needed)
@param none
@return: array
"""
def get_player_moves(self, board):
moves = board.legal_moves()
if self.options['randomize_moves'] is True:
random.shuffle(moves)
return moves
"""
Get player name based on his number
@param player: int
@return: string
"""
@staticmethod
def get_player_name(player):
return 'White (O)' if player == 2 else 'Black (X)'
"""
Random game engine
"""
class RandomPlayerEngine(PlayerEngine):
"""
Get move return a random move based on Board.legal_moves
@param player: int
@return: array
"""
def get_move(self, board):
super().get_move(board)
moves = board.legal_moves()
return random.choice(moves)
"""
Human player engine.
"""
class HumanPlayerEngine(PlayerEngine):
"""
Get move return a move based on user input
@param board: Board
@return: array
"""
def get_move(self, board):
super()
move = None
@ -46,6 +93,12 @@ class HumanPlayerEngine(PlayerEngine):
move = self.validate_input(user_input, board)
return move
"""
Validate user input an verify than the move is possible
@param input: string
@return: array
"""
@staticmethod
def validate_input(input, board):
if input == 'print':
@ -66,13 +119,31 @@ class HumanPlayerEngine(PlayerEngine):
return [board._nextPlayer, x, y]
"""
MinMax player engine
"""
class MinmaxPlayerEngine(PlayerEngine):
"""
Get move based on minmax algorithm
@param board: Board
@return: array
"""
def get_move(self, board):
super().get_move(board)
move, score = self._call(board, self.options['depth'])
return move
"""
First part of the minmax algorithm, it get the best player move based on
max value
@param board: Board
@param depth: search depth
@return: move and max heuristic
"""
def _call(self, board, depth):
value = -math.inf
nodes = 1
@ -100,6 +171,13 @@ class MinmaxPlayerEngine(PlayerEngine):
))
return move, value
"""
recursive function to apply minmax
@param board: Board
@param friend_move: boolean does function maximise (player turn) or not (opponent turn)
@param depth: search depth
@return: heuristic score, nodes ans leafs processed
"""
def checkMinMax(self, board, friend_move:bool, depth :int = 2):
nodes = 1
leafs = 0
@ -140,6 +218,13 @@ class AlphabetaPlayerEngine(PlayerEngine):
move, heuristic = self._call(board, self.options['depth'])
return move
"""
First part of the alphabeta algorithm, it get the best player move based on
max value
@param board: Board
@param depth: search depth
@return: move and max heuristic
"""
def _call(self, board, depth):
self.logger.debug("Enter AlphaBeta function")
alpha = -math.inf
@ -169,7 +254,13 @@ class AlphabetaPlayerEngine(PlayerEngine):
))
return move, alpha
"""
recursive function to apply alphabeta
@param board: Board
@param friend_move: boolean does function maximise (player turn) or not (opponent turn)
@param depth: search depth
@return: heuristic score, nodes ans leafs processed
"""
def checkAlphaBeta(self, board, friend_move : bool, depth, alpha, beta):
nodes = 1
leafs = 0
@ -206,7 +297,11 @@ class AlphabetaPlayerEngine(PlayerEngine):
class MinmaxDeepeningPlayerEngine(MinmaxPlayerEngine):
"""
Get move based on minmax algorithm with iterative deepening
@param board: Board
@return: array
"""
def get_move(self, board):
super().get_move(board)
self.interrupt_search = False
@ -251,7 +346,11 @@ class MinmaxDeepeningPlayerEngine(MinmaxPlayerEngine):
class AlphaBetaDeepeningPlayerEngine(AlphabetaPlayerEngine):
"""
Get move based on alphabeta algorithm with iterative deepening
@param board: Board
@return: array
"""
def get_move(self, board):
self.interrupt_search = False
@ -284,6 +383,9 @@ class AlphaBetaDeepeningPlayerEngine(AlphabetaPlayerEngine):
))
return move
"""
define an handler for the alarm signal
"""
def alarm_handler(self, signal, frame):
self.logger.debug("Raise SIGALMR Signal")
self.interrupt_search = True

View file

@ -1,4 +1,14 @@
"""
Base class for heuristic object
"""
class HeuristicEngine:
"""
Init method
@param logger: logging object (display verbose and debug messages)
@param options: hashtable contains options like board size or heuristic weight
"""
def __init__(self, logger, options):
self.logger = logger
self.options = options
@ -10,22 +20,52 @@ class HeuristicEngine:
def get():
raise NotImplementedError
"""
Score based heuristic class
"""
class ScoreHeuristicEngine(HeuristicEngine):
"""
Get score
@param board: reversi board object
@return score: int
"""
def get(self, board, player):
return board.heuristique(player)
"""
Weight based heuristic class
"""
class WeightHeuristicEngine(HeuristicEngine):
"""
Init method
@param logger: logging object (display verbose and debug messages)
@param options: hashtable contains options like board size or heuristic weight
"""
def __init__(self, logger, options):
super().__init__(logger, options)
self.weights = self._get_weight_array()
self.logger.debug("{}".format(self.show_weights()))
"""
Get score
@param board: reversi board object
@param player: int concerned player
@return int
"""
def get(self, board, player):
score = self.get_weight(board, player)
return score
"""
Get score based on weight based on a weight-table built on object creation
@param board: reversi board object
@param player: int concerned player
@return int
"""
def get_weight(self, board, player):
score = 0
size = self.options['size']
@ -42,6 +82,11 @@ class WeightHeuristicEngine(HeuristicEngine):
w[pos_x][pos_y] = -self.weights[pos_x][pos_y]
return score
"""
Get weight array calculated with weight given with options
@param : none
@return 2D array
"""
def _get_weight_array(self):
size = self.options['size']
w = [[ 0 for _ in range(size)] for _ in range(size)]
@ -85,7 +130,11 @@ class WeightHeuristicEngine(HeuristicEngine):
return w
"""
Create a "displayable" array of value dor the calculated weight table
@input none
@return string
"""
def show_weights(self):
display = "\n |"
sep = "\n----"
@ -100,8 +149,16 @@ class WeightHeuristicEngine(HeuristicEngine):
display += "\n"
return display
"""
Full heuristic class
"""
class FullHeuristicEngine(WeightHeuristicEngine):
"""
Get score
@param board: reversi board object
@param player: int concerned player
@return int
"""
def get(self, board, player):
return self.get_weight(board, player) + board.heuristique(player)