From 1c039106d9746b33cc670c9f7309dfdaf3de6637 Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Fri, 15 Dec 2023 00:24:28 +0100 Subject: [PATCH 1/3] Add weight calculation heuristic --- src/classes/Engines.py | 14 ++++++++++++-- src/classes/Heuristic.py | 38 ++++++++++++++++++++++++++++++++++++++ src/game.py | 10 +++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/classes/Heuristic.py diff --git a/src/classes/Engines.py b/src/classes/Engines.py index c3b4ea5..8e96283 100644 --- a/src/classes/Engines.py +++ b/src/classes/Engines.py @@ -1,4 +1,5 @@ import random, math +from .Heuristic import ReversiHeuristic class PlayerEngine: def __init__(self, logger, options: dict = {}): @@ -88,7 +89,11 @@ class MinmaxPlayerEngine(PlayerEngine): move = '' if depth == 0: leafs +=1 - return board.heuristique(), nodes, leafs + if self.options['heuristic'] == 'weight': + score = ReversiHeuristic(board).get() + else: + score = board.heuristique() + return score, nodes, leafs if friend_move: value = -math.inf @@ -150,7 +155,12 @@ class AlphabetaPlayerEngine(PlayerEngine): leafs = 0 if depth == 0 : leafs +=1 - return board.heuristique(), nodes, leafs + self.logger.debug("option: {}".format(self.options)) + if self.options['heuristic'] == 'weight': + score = ReversiHeuristic(self.logger).get(board) + else: + score = board.heuristique() + return score, nodes, leafs if friend_move: value = -math.inf diff --git a/src/classes/Heuristic.py b/src/classes/Heuristic.py new file mode 100644 index 0000000..13ae8ee --- /dev/null +++ b/src/classes/Heuristic.py @@ -0,0 +1,38 @@ +class ReversiHeuristic(): + def __init__(self, logger, weight = [1, 2, 10, 20]): + self.logger = logger + self.weight = weight + + def get(self, board): + size = board.get_board_size() + score = 0 + weights = self._get_weight(size) + for pos_x in range(size): + for pos_y in range(size): + if board._board[pos_x][pos_y] == board._nextPlayer: + score += weights[pos_x][pos_y] + else: + score -= weights[pos_x][pos_y] + return score + + def _get_weight(self, size): + w = [[ 0 for _ in range(size)] for _ in range(size)] + + for pos_y in range(size): + for pos_x in range(size): + + # Elements in the corner + if pos_x in [0, size -1] and pos_y in [0,size - 1]: + w[pos_x][pos_y] = self.weight[3] + + # Elements on the border + elif pos_x in [0, size -1] or pos_y in [0, size -1]: + w[pos_x][pos_y] = self.weight[2] + + # Element the center + elif pos_x in range( int(size // 2 - 2), size // 2 + 2) and pos_y in range( size // 2 - 2, size // 2 + 2): + w[pos_x][pos_y] = self.weight[1] + + else: + w[pos_x][pos_y] = self.weight[0] + return w diff --git a/src/game.py b/src/game.py index 027ab8d..f503be9 100755 --- a/src/game.py +++ b/src/game.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 + from classes.Reversi import Board from classes.Engines import RandomPlayerEngine, HumanPlayerEngine, MinmaxPlayerEngine, AlphabetaPlayerEngine import logging as log @@ -31,6 +32,12 @@ def parse_aguments(): default=3, ) + parser.add_argument('-H', '--heuristic', + help='Define heutistic engine', + choices=['board', 'weight'], + default='board', + ) + debug_group = parser.add_mutually_exclusive_group() debug_group.add_argument('-V', '--verbose', help='Verbose output', @@ -75,7 +82,8 @@ if __name__ == '__main__': args.white_engine )) options = { - 'depth': args.depth + 'depth': args.depth, + 'heuristic': args.heuristic } wplayer = engines[args.white_engine](logger, options) bplayer = engines[args.black_engine](logger, options) From c3ac347364cd55e80c63b7fd7b2908ec8b5262de Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Fri, 15 Dec 2023 00:46:42 +0100 Subject: [PATCH 2/3] Rework options, now can be defined per player --- src/classes/Engines.py | 6 ++++-- src/game.py | 40 +++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/classes/Engines.py b/src/classes/Engines.py index 8e96283..1c9afed 100644 --- a/src/classes/Engines.py +++ b/src/classes/Engines.py @@ -6,7 +6,10 @@ class PlayerEngine: # init logger do display informations self.logger = logger self.options = options - self.logger.info("Init engine {}".format(self.__class__.__name__)) + self.logger.info("Init engine {}, options:{}".format( + self.__class__.__name__, + self.options + )) def get_move(self, board): self.logger.info("engine: {} - player:{}".format( @@ -155,7 +158,6 @@ class AlphabetaPlayerEngine(PlayerEngine): leafs = 0 if depth == 0 : leafs +=1 - self.logger.debug("option: {}".format(self.options)) if self.options['heuristic'] == 'weight': score = ReversiHeuristic(self.logger).get(board) else: diff --git a/src/game.py b/src/game.py index f503be9..7db3514 100755 --- a/src/game.py +++ b/src/game.py @@ -12,28 +12,41 @@ Function to parse command line arguments """ def parse_aguments(): engines_choices=['random', 'human', 'minmax', 'alphabeta'] + heuristic_choices=['weight',] parser = arg.ArgumentParser('Playing Reversi with (virtual) friend') - parser.add_argument('-w', '--white-engine', + parser.add_argument('-we', '--white-engine', choices=engines_choices, help='white player engine (random)', default='random' ) - parser.add_argument('-b', '--black-engine', + parser.add_argument('-be', '--black-engine', choices=engines_choices, help='black player engine (random)', default='random' ) - parser.add_argument('-D', '--depth', - help='Minmax exploration depth', + parser.add_argument('-bd', '--black-depth-exploration', + help='Black player exploration depth (minmax or alphabeta engine)', type=int, default=3, ) - parser.add_argument('-H', '--heuristic', - help='Define heutistic engine', + 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=['board', 'weight'], + default='board', + ) + + parser.add_argument('-wh', '--white-heuristic-engine', + help='White player heutistic engine', choices=['board', 'weight'], default='board', ) @@ -61,7 +74,6 @@ if __name__ == '__main__': print("Stating PyReverso...") args = parse_aguments() logger = log.getLogger() - print(args) # Create handler for streaming to tty (stderr / stdout) tty_handler = log.StreamHandler() tty_handler.setFormatter(CustomFormatter()) @@ -81,12 +93,14 @@ if __name__ == '__main__': args.black_engine, args.white_engine )) - options = { - 'depth': args.depth, - 'heuristic': args.heuristic - } - wplayer = engines[args.white_engine](logger, options) - bplayer = engines[args.black_engine](logger, options) + wplayer = engines[args.white_engine](logger, { + 'depth': args.white_depth_exploration, + 'heuristic': args.white_heuristic_engine + }) + bplayer = engines[args.black_engine](logger, { + 'depth': args.black_depth_exploration, + 'heuristic': args.black_heuristic_engine + }) while ( not game.is_game_over()): if game._nextPlayer == 1: move = bplayer.get_move(game) From e49e737ec0634e79db1ef76987acbfe37a6be7b7 Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Fri, 15 Dec 2023 00:57:02 +0100 Subject: [PATCH 3/3] Update documentation --- README.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 44b9f12..bcae4fa 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,34 @@ au programme via la commande `./game.py -h`. Voici quelques exemple de lancement: ```shell -# Lancement de base: les deux joueurs jouent avec le moteur aléatoire: +# Lancement de base: les deux joueurs jouent avec le moteur aléatoire et les +options de base: ./game.py # joueur noir humain et joueur blanc MinMax avec une profondeur de 5 -./game.py -b human -w minmax --depth 5 +./game.py -be human -we minmax --white-depth-exploration 5 ``` +Voici la liste des options : + + * `-be` | `--black-player-engine`: moteur utilisé par le joueur avec les pions + noirs + * `-we` | `--white-player-engine`: moteur utilisé par le joueur avec les pions + blancs + * `-bd` | `--black-depth-exploration`: niveau d'eploration de l'arbre de jeu + pour le joueur au pions noirs, valable pour les moteurs `minmax` et + `alphabeta` + * `-wd` | `--white-depth-exploration`: niveau d'eploration de l'arbre de jeu + pour le joueur au pions noirs, valable pour les moteurs `minmax` et + `alphabeta` + * `-bh` | `--black-heuristic-engine`: moteur heuristique utilisé pour + l'exploration de l'arbre de jeu du joueur noir (valable pour les moteur de + jeu `minmax` et `alphabeta`) + * `-wh` | `--black-heuristic-engine`: moteur heuristique utilisé pour + l'exploration de l'arbre de jeu du joueur blanc (valable pour les moteur de + jeu `minmax` et `alphabeta`) + +L'affichage verbeux est activé avec `-V` et les informations de débogage sont +affichée avec l'option `-d`. ## Choix d'implémentation