From 6e2d2c2a7ba85578f1cd2166dec66652cda0a12d Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Fri, 22 Dec 2023 13:08:26 +0100 Subject: [PATCH] Make pep8 happy --- src/classes/Engines.py | 86 ++++++++++++-------- src/classes/Heuristic.py | 55 ++++++++----- src/classes/Reversi.py | 2 +- src/game.py | 169 +++++++++++++++++++++------------------ 4 files changed, 181 insertions(+), 131 deletions(-) diff --git a/src/classes/Engines.py b/src/classes/Engines.py index cd9bc90..697d4a5 100644 --- a/src/classes/Engines.py +++ b/src/classes/Engines.py @@ -1,8 +1,12 @@ -import random, math, time, signal +import random +import math +import signal """ Base player engine """ + + class PlayerEngine: """ @@ -12,6 +16,7 @@ class PlayerEngine: @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 @@ -28,9 +33,10 @@ class PlayerEngine: get move @param board: Board """ + def get_move(self, board): self.logger.info("engine: {} - player:{}".format( - self._get_class_name(), + self._get_class_name(), self._get_player_name(self.player) )) @@ -39,6 +45,7 @@ class PlayerEngine: @param none @return: array """ + def get_player_moves(self, board): moves = board.legal_moves() if self.options['randomize_moves'] is True: @@ -50,6 +57,7 @@ class PlayerEngine: @param player: int @return: string """ + def _get_player_name(self, player): return 'White (O)' if self.player == 2 else 'Black (X)' @@ -63,24 +71,27 @@ class PlayerEngine: def _show_better_move(self, move, heuristic): self.logger.debug(" -> Found a better move: {},{} | heuristic:{}".format( - chr(move[1] + 65),move[2], + chr(move[1] + 65), move[2], heuristic )) def _get_class_name(self): return self.__class__.__name__ + """ Random game engine """ -class RandomPlayerEngine(PlayerEngine): +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() @@ -90,14 +101,16 @@ class RandomPlayerEngine(PlayerEngine): """ Human player engine. """ -class HumanPlayerEngine(PlayerEngine): +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 @@ -114,6 +127,7 @@ class HumanPlayerEngine(PlayerEngine): @param input: string @return: array """ + def validate_input(self, input, board): if input == 'print': print("\n{}".format(board.show_board())) @@ -124,7 +138,7 @@ class HumanPlayerEngine(PlayerEngine): for m in board.legal_moves(): text += " {}{}".format(chr(65+m[1]), m[2]) print(text) - + return None if len(input) != 2: @@ -141,13 +155,14 @@ class HumanPlayerEngine(PlayerEngine): self.logger.error("Invalid input must be [A-J][0-9] (was {})".format(input)) return None - return [board._nextPlayer, x, y] """ MinMax player engine """ + + class MinmaxPlayerEngine(PlayerEngine): @@ -156,12 +171,12 @@ class MinmaxPlayerEngine(PlayerEngine): @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 @@ -169,6 +184,7 @@ class MinmaxPlayerEngine(PlayerEngine): @param depth: search depth @return: move and max heuristic """ + def _call(self, board, depth): value = -math.inf nodes = 1 @@ -194,12 +210,12 @@ class MinmaxPlayerEngine(PlayerEngine): @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 + @return: heuristic score, nodes ans leafs processed """ - def checkMinMax(self, board, friend_move:bool, depth :int = 2): + + def checkMinMax(self, board, friend_move: bool, depth: int = 2): nodes = 1 leafs = 0 - move = [] if depth == 0 or board.is_game_over() or self.interrupt_search: leafs +=1 return self.heuristic.get(board, self.player), nodes, leafs @@ -209,11 +225,11 @@ class MinmaxPlayerEngine(PlayerEngine): moves = self.get_player_moves(board) for m in moves: board.push(m) - v, n, l = self.checkMinMax(board, False, depth - 1) + v, n, le = self.checkMinMax(board, False, depth - 1) if v > value: value = v nodes += n - leafs += l + leafs += le board.pop() else: @@ -221,14 +237,15 @@ class MinmaxPlayerEngine(PlayerEngine): moves = self.get_player_moves(board) for m in moves: board.push(m) - v, n, l = self.checkMinMax(board, True, depth - 1) + v, n, le = self.checkMinMax(board, True, depth - 1) if v < value: value = v - board.pop(); + board.pop() nodes += n - leafs += l + leafs += le return value, nodes, leafs + class AlphabetaPlayerEngine(PlayerEngine): def get_move(self, board): @@ -243,6 +260,7 @@ class AlphabetaPlayerEngine(PlayerEngine): @param depth: search depth @return: move and max heuristic """ + def _call(self, board, depth): self.logger.debug("Enter AlphaBeta function") alpha = -math.inf @@ -253,11 +271,11 @@ class AlphabetaPlayerEngine(PlayerEngine): moves = self.get_player_moves(board) for m in moves: board.push(m) - value, n, l = self.checkAlphaBeta(board, False, depth - 1, alpha, beta) + value, n, le = self.checkAlphaBeta(board, False, depth - 1, alpha, beta) board.pop() nodes += n - leafs += l - if value >= alpha: + leafs += le + if value >= alpha: alpha = value move = m self._show_better_move(move, alpha) @@ -268,15 +286,17 @@ class AlphabetaPlayerEngine(PlayerEngine): """ recursive function to apply alphabeta @param board: Board - @param friend_move: boolean does function maximise (player turn) or not (opponent turn) + @param friend_move: boolean does function maximise (player turn) or + not (opponent turn) @param depth: search depth - @return: heuristic score, nodes ans leafs processed + @return: heuristic score, nodes ans leafs processed """ - def checkAlphaBeta(self, board, friend_move : bool, depth, alpha, beta): + + def checkAlphaBeta(self, board, friend_move: bool, depth, alpha, beta): nodes = 1 leafs = 0 if depth == 0 or board.is_game_over() or self.interrupt_search: - leafs +=1 + leafs += 1 return self.heuristic.get(board, self.player), nodes, leafs if friend_move: @@ -284,11 +304,11 @@ class AlphabetaPlayerEngine(PlayerEngine): moves = self.get_player_moves(board) for m in moves: board.push(m) - v, n, l = self.checkAlphaBeta(board, False, depth - 1, alpha, beta) + v, n, le = self.checkAlphaBeta(board, False, depth - 1, alpha, beta) board.pop() - alpha = max(alpha,v) + alpha = max(alpha, v) nodes += n - leafs += l + leafs += le if alpha >= beta: return beta, nodes, leafs return alpha, nodes, leafs @@ -297,11 +317,11 @@ class AlphabetaPlayerEngine(PlayerEngine): moves = self.get_player_moves(board) for m in moves: board.push(m) - v, n, l = self.checkAlphaBeta(board, True, depth - 1, alpha, beta) - board.pop(); + v, n, le = self.checkAlphaBeta(board, True, depth - 1, alpha, beta) + board.pop() beta = min(beta, v) nodes += n - leafs += l + leafs += le if alpha >= beta: return alpha, nodes, leafs return beta, nodes, leafs @@ -313,6 +333,7 @@ class MinmaxDeepeningPlayerEngine(MinmaxPlayerEngine): @param board: Board @return: array """ + def get_move(self, board): super().get_move(board) self.interrupt_search = False @@ -325,7 +346,7 @@ class MinmaxDeepeningPlayerEngine(MinmaxPlayerEngine): # We can go deeper than blank place in our board, then we must get # numbers of avaible place - max_depth = (board.get_board_size()**2) - ( + max_depth = (board.get_board_size()**2) - ( board.get_nb_pieces()[0] + board.get_nb_pieces()[1]) depth = self.options['depth'] if self.options['depth'] <= max_depth else max_depth @@ -357,7 +378,6 @@ class MinmaxDeepeningPlayerEngine(MinmaxPlayerEngine): self.interrupt_search = True - class AlphaBetaDeepeningPlayerEngine(AlphabetaPlayerEngine): """ @@ -365,6 +385,7 @@ class AlphaBetaDeepeningPlayerEngine(AlphabetaPlayerEngine): @param board: Board @return: array """ + def get_move(self, board): self.interrupt_search = False @@ -376,7 +397,7 @@ class AlphaBetaDeepeningPlayerEngine(AlphabetaPlayerEngine): # We can go deeper than blank place in our board, then we must get # numbers of avaible place - max_depth = (board.get_board_size()**2) - ( + max_depth = (board.get_board_size()**2) - ( board.get_nb_pieces()[0] + board.get_nb_pieces()[1]) depth = self.options['depth'] if self.options['depth'] <= max_depth else max_depth @@ -402,6 +423,7 @@ class AlphaBetaDeepeningPlayerEngine(AlphabetaPlayerEngine): """ define an handler for the alarm signal """ + def alarm_handler(self, signal, frame): self.logger.debug("Raise SIGALMR Signal") self.interrupt_search = True diff --git a/src/classes/Heuristic.py b/src/classes/Heuristic.py index 215f6b9..6d71790 100644 --- a/src/classes/Heuristic.py +++ b/src/classes/Heuristic.py @@ -1,7 +1,8 @@ - """ Base class for heuristic object """ + + class HeuristicEngine: """ @@ -9,6 +10,7 @@ class HeuristicEngine: @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 @@ -20,9 +22,12 @@ class HeuristicEngine: def get(): raise NotImplementedError + """ Score based heuristic class """ + + class ScoreHeuristicEngine(HeuristicEngine): """ @@ -30,12 +35,15 @@ class ScoreHeuristicEngine(HeuristicEngine): @param board: reversi board object @return score: int """ + def get(self, board, player): return board.heuristique(player) """ Weight based heuristic class """ + + class WeightHeuristicEngine(HeuristicEngine): """ @@ -43,22 +51,22 @@ class WeightHeuristicEngine(HeuristicEngine): @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 @@ -66,13 +74,14 @@ class WeightHeuristicEngine(HeuristicEngine): @param player: int concerned player @return int """ + def get_weight(self, board, player): score = 0 size = self.options['size'] - w = [[ 0 for _ in range(size)] for _ in range(size)] + w = [[0 for _ in range(size)] for _ in range(size)] for pos_x in range(self.options['size']): for pos_y in range(self.options['size']): - p = board._board[pos_x][pos_y] + p = board._board[pos_x][pos_y] if p == player: score += self.weights[pos_x][pos_y] w[pos_x][pos_y] = self.weights[pos_x][pos_y] @@ -81,15 +90,16 @@ class WeightHeuristicEngine(HeuristicEngine): score -= self.weights[pos_x][pos_y] 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)] + w = [[0 for _ in range(size)] for _ in range(size)] padding = size // 5 center = size // 2 full_range = range(self.options['size']) @@ -99,20 +109,20 @@ class WeightHeuristicEngine(HeuristicEngine): for pos_x in full_range: # Elements in the corner - if pos_x in [0, size -1] and pos_y in [0,size - 1]: + if pos_x in [0, size -1] and pos_y in [0, size - 1]: w[pos_x][pos_y] = self.options['weight'][3] - + # corners are a bad place! elif (pos_x in [0, size - 1] and pos_y in [size - 2, 1]) or \ - (pos_x in [1, size -2] and pos_y in [0, size - 1]): + (pos_x in [1, size - 2] and pos_y in [0, size - 1]): w[pos_x][pos_y] = self.options['weight'][0] - + # in diagonale of the corner too elif pos_x in [size - 2, 1] and pos_y in [size - 2, 1]: w[pos_x][pos_y] = int(self.options['weight'][0] * 1.5) - - elif pos_x in [1,size - 2] and pos_y in range(2, size - 2) or \ - pos_y in [1,size - 2] and pos_x in range(2, size - 2) : + + elif pos_x in [1, size - 2] and pos_y in range(2, size - 2) or \ + pos_y in [1, size - 2] and pos_x in range(2, size - 2): w[pos_x][pos_y] = int(self.options['weight'][0] * 0.75) # center border : cool but not so... @@ -121,13 +131,13 @@ class WeightHeuristicEngine(HeuristicEngine): w[pos_x][pos_y] = int(self.options['weight'][2] // 1.25) # Elements on the border - elif pos_x in [0, size -1] or pos_y in [0, size -1]: + elif pos_x in [0, size - 1] or pos_y in [0, size - 1]: w[pos_x][pos_y] = self.options['weight'][2] - + # Element the center elif pos_x in center_range and pos_y in center_range: w[pos_x][pos_y] = self.options['weight'][1] - + return w """ @@ -135,12 +145,13 @@ class WeightHeuristicEngine(HeuristicEngine): @input none @return string """ + def show_weights(self): display = "\n |" - sep = "\n----" + sep = "\n----" for x in range(self.options['size']): display += "{:^3}|".format(x) - sep += '----' + sep += '----' display += sep + "\n" for x in range(self.options['size']): display += "{:^3}|".format(str(x)) @@ -149,16 +160,20 @@ 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) diff --git a/src/classes/Reversi.py b/src/classes/Reversi.py index 1a65e62..9305247 100644 --- a/src/classes/Reversi.py +++ b/src/classes/Reversi.py @@ -227,7 +227,7 @@ class Board: display += " {} |".format(str(x)) for y in range(self.get_board_size()): display += " {} |".format(self._piece2str(self._board[x][y])) - display += "\n"#+sep+"\n" + display += "\n" return display + sep + '\n' def __str__(self): diff --git a/src/game.py b/src/game.py index 61ce231..7fadcef 100755 --- a/src/game.py +++ b/src/game.py @@ -12,96 +12,109 @@ from classes.CustomFormater import CustomFormatter """ Function to parse command line arguments """ + + def parse_aguments(): - engines_choices=['random', 'human', 'minmax', 'alphabeta', 'id_minmax', 'id_alphabeta'] - heuristic_choices=['score', 'weight', 'full'] + engines_choices = ['random', 'human', 'minmax', 'alphabeta', 'id_minmax', 'id_alphabeta'] + heuristic_choices = ['score', 'weight', 'full'] parser = arg.ArgumentParser('Playing Reversi with (virtual) friend') - parser.add_argument('-we', '--white-engine', - choices=engines_choices, - help='white player engine (random)', - default='random' - ) + parser.add_argument('-we', + '--white-engine', + choices=engines_choices, + help='white player engine (random)', + default='random' + ) - parser.add_argument('-be', '--black-engine', - choices=engines_choices, - help='black player engine (random)', - default='random' - ) + parser.add_argument('-be', + '--black-engine', + choices=engines_choices, + help='black player engine (random)', + default='random' + ) - parser.add_argument('-bd', '--black-depth-exploration', - help='Black player exploration depth (minmax or alphabeta engine)', - type=int, - default=3, - ) + parser.add_argument('-bd', + '--black-depth-exploration', + help='Black player exploration depth (minmax or alphabeta engine)', + type=int, + default=3, + ) - parser.add_argument('-wd', '--white-depth-exploration', - help='White player exploration depth (minmax or alphabeta engine)', - type=int, - default=3, - ) + parser.add_argument('-wd', + '--white-depth-exploration', + help='White player exploration depth (minmax or alphabeta engine)', + type=int, + default=3, + ) - parser.add_argument('-bh', '--black-heuristic-engine', - help='Black player heutistic engine', - choices= heuristic_choices, - default='score', - ) + parser.add_argument('-bh', + '--black-heuristic-engine', + help='Black player heutistic engine', + choices= heuristic_choices, + default='score', + ) - parser.add_argument('-wh', '--white-heuristic-engine', - help='White player heutistic engine', - choices=heuristic_choices, - default='score', - ) + parser.add_argument('-wh', + '--white-heuristic-engine', + help='White player heutistic engine', + choices=heuristic_choices, + default='score', + ) - parser.add_argument('-br', '--black-randomize-moves', - help='Apply a random function on moves list before explore the game tree - black player', - type=bool, - default=True, - ) + parser.add_argument('-br', + '--black-randomize-moves', + help='Apply a random function on moves list before explore the game tree - black player', + type=bool, + default=True, + ) - parser.add_argument('-wr', '--white-randomize-moves', - help='Apply a random function on moves list before explore the game tree - white player', - type=bool, - default=True, - ) + parser.add_argument('-wr', + '--white-randomize-moves', + help='Apply a random function on moves list before explore the game tree - white player', + type=bool, + default=True, + ) - parser.add_argument('-bt', '--black-player-deepening-time', - help='Time interval in seconds for Iterative Deepening - black player', - type=int, - default=10, - ) + parser.add_argument('-bt', + '--black-player-deepening-time', + help='Time interval in seconds for Iterative Deepening - black player', + type=int, + default=10, + ) - parser.add_argument('-wt', '--white-player-deepening-time', - help='Time interval in seconds for Iterative Deepening - black player', - type=int, - default=10, - ) + parser.add_argument('-wt', + '--white-player-deepening-time', + help='Time interval in seconds for Iterative Deepening - black player', + type=int, + default=10, + ) parser.add_argument('--weight', - help='Weight table for weight based heuristic engines', - type=int, - nargs=4, - default=[-5, 2, 10,25] - ) + help='Weight table for weight based heuristic engines', + type=int, + nargs=4, + default=[-5, 2, 10,25] + ) - parser.add_argument('-r', '--recursions', - help='Number parties to play', - type=int, - default=1 - ) + parser.add_argument('-r', + '--recursions', + help='Number parties to play', + type=int, + default=1 + ) parser.add_argument('--show-weights-table', - help='Display weight table used in \'weight\' and \'full\' heuristic calculation and exit', - action='store_true', - ) + help='Display weight table used in \'weight\' and \'full\' heuristic calculation and exit', + action='store_true', + ) debug_group = parser.add_mutually_exclusive_group() debug_group.add_argument('-V', '--verbose', - help='Verbose output', - action='store_true') + help='Verbose output', + action='store_true') debug_group.add_argument('-d', '--debug', - help='Activate debug mode', - action='store_true') + help='Activate debug mode', + action='store_true') return parser.parse_args() @@ -129,11 +142,11 @@ if __name__ == '__main__': tty_handler = log.StreamHandler() tty_handler.setFormatter(CustomFormatter()) logger.addHandler(tty_handler) - + # IT shoud be better implemented but no time to make it clean if args.show_weights_table: print("{}".format( - heuristic_engine['weight'](logger,{ + heuristic_engine['weight'](logger, { 'weight': args.weight, 'size': 10 }).show_weights() @@ -144,7 +157,7 @@ if __name__ == '__main__': if args.verbose is True: logger.setLevel(log.INFO) logger.info('VERBOSE mode activated') - + if args.debug is True: logger.setLevel(log.DEBUG) logger.debug('DEBUG mode activated') @@ -153,14 +166,14 @@ if __name__ == '__main__': args.black_engine, args.white_engine )) - logger.debug("Weight value {}".format( args.weight )) + logger.debug("Weight value {}".format(args.weight)) wplayer = player_engines[args.white_engine]( game._WHITE, - logger, + logger, heuristic_engine[args.white_heuristic_engine]( - logger, { + logger, { 'weight': args.weight, - 'size': game.get_board_size() + 'size': game.get_board_size() } ), { @@ -173,7 +186,7 @@ if __name__ == '__main__': game._BLACK, logger, heuristic_engine[args.black_heuristic_engine]( - logger, { + logger, { 'weight': args.weight, 'size': game.get_board_size() } @@ -205,7 +218,7 @@ if __name__ == '__main__': move[2] )) game.push(move) - + parties.append([recursions, game._nbBLACK, black_time, game._nbWHITE, white_time]) score = game._nbBLACK - game._nbWHITE if score == 0: