Compare commits

..

No commits in common. "e506bcdab9b38dd757743676713ab3c442060c10" and "3784faeaed8a67b833f7fc0d001c0240f15b9998" have entirely different histories.

4 changed files with 48 additions and 127 deletions

View file

@ -44,21 +44,16 @@ Voici la liste des options :
* `-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`)
* `--weight`: scores utilisés pour le calcul des heuristiques pour les moteurs
`weight` et `full`.
L'affichage verbeux est activé avec `-V` et les informations de débogage sont
affichée avec l'option `-d`.
## Choix d'implémentation
J'ai avant tout privilégié la personnalisation des différentes paramètres des
différents moteurs composant le jeu.
### Classes PlayerEngine
Définies dans le fichier `./src/classes/Engines.py`, les classes utilisées
ritent de la classe de base `PlayerEngines` :
Définies dans le fichier `./src/classes/Engines.py`, les classes utilisées h
eritent de la classe de base `PlayerEngines` :
```python
class PlayerEngine(Object):
@ -68,38 +63,8 @@ class PlayerEngine(Object):
class MinmaxPlayerEngine(PlayerEngine):
def get_move(board):
class RandomPlayerEngine(PlayerEngine):
class RandomPlayerEngnine(PlayerEngine):
def get_move(board):
```
Il est ainsi plus aisé de tester les moteur dans notre programme de base.
### Classes HeuristicsEngine
Plusieurs classes impémentent plusieurs méthodes pour le calcul de
l'heuristique. Comme nous l'avons vu, les moteurs peuvent être choisis en ligne
de commande et de façon indépendante pour les joueurs blanc et noir.
Trois implementation sond disponibles:
1. `ScoreHeuristicEngine`: l'heuristique se sert du score (comptage des pièces
sur le tableau) via la méthode `Board.heuristique`;
2. `WeightHeuristicEngine`: ici on se sert de la place des pièces sur le
tableau. Chaque emplacement vaut un nombre de points;
3. `FullHeuristicEngine`: c'est la somme de `Board.heuristique()` et du calcul
des poids.
#### Retour sur le calcul des poids.
Afin de définir le poids, je me suis servi de la page *Stratégie et bases de
Reversi* sur le site Cool Math Games ([lien][reversi]). Voici les poids par
ordre d'importance :
1. Les coins représentent les parties les plus importantes;
2. Ensuite vient les bords;
3. Et enfin les coins.
Les poids affectés sont personnalisable via l'options `--weight`, par défaut
nous avons 2, 10 et 25.
[reversi]:https://www.coolmathgames.com/blog/how-to-play-reversi-basics-and-best-strategies

View file

@ -1,11 +1,10 @@
import random, math
from .Heuristic import ReversiHeuristic
class PlayerEngine:
def __init__(self, player, logger, heuristic, options: dict = {}):
def __init__(self, logger, options: dict = {}):
# init logger do display informations
self.player = player
self.logger = logger
self.heuristic = heuristic
self.options = options
self.logger.info("Init engine {}, options:{}".format(
self.__class__.__name__,
@ -15,7 +14,7 @@ class PlayerEngine:
def get_move(self, board):
self.logger.info("engine: {} - player:{}".format(
self.__class__.__name__,
self.get_player_name(self.player)
self.get_player_name(board._nextPlayer)
))
@staticmethod
@ -34,7 +33,7 @@ class HumanPlayerEngine(PlayerEngine):
move = None
while move is None:
user_input = input("Please enter player {} move, `print` to display board and `help` possible moves : ".format(
self.get_player_name(self.player)
self.get_player_name(board._nextPlayer)
))
move = self.validate_input(user_input, board)
return move
@ -92,7 +91,11 @@ class MinmaxPlayerEngine(PlayerEngine):
move = ''
if depth == 0:
leafs +=1
return self.heuristic.get(board, self.player), 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
@ -154,7 +157,11 @@ class AlphabetaPlayerEngine(PlayerEngine):
leafs = 0
if depth == 0 :
leafs +=1
return self.heuristic.get(board, self.player), nodes, leafs
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

View file

@ -1,59 +1,38 @@
class HeuristicEngine:
def __init__(self, logger, options):
class ReversiHeuristic():
def __init__(self, logger, weight = [1, 2, 10, 20]):
self.logger = logger
self.options = options
self.logger.info("Heuristic engine {}, options:{}".format(
self.__class__.__name__,
self.options
))
self.weight = weight
def get():
raise NotImplementedError
class ScoreHeuristicEngine(HeuristicEngine):
def get(self, board, player):
return board.heuristique(player)
class WeightHeuristicEngine(HeuristicEngine):
def get(self, board, player):
score = self.get_weight(board, player)
return score
def get_weight(self, board, player):
score = 0
def get(self, board):
size = board.get_board_size()
weights = self._get_weight_array(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] == player:
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_array(self, size):
def _get_weight(self, size):
w = [[ 0 for _ in range(size)] for _ in range(size)]
padding = size // 5
center = size // 2
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.options['weight'][2]
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.options['weight'][1]
w[pos_x][pos_y] = self.weight[2]
# Element the center
elif pos_x in range( center - padding, center + padding) and pos_y in range(center - padding, center + padding):
w[pos_x][pos_y] = self.options['weight'][0]
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
class FullHeuristicEngine(WeightHeuristicEngine):
def get(self, board, player):
return self.get_weight(board, player) + board.heuristique(player)

View file

@ -2,7 +2,6 @@
from classes.Reversi import Board
from classes.Engines import RandomPlayerEngine, HumanPlayerEngine, MinmaxPlayerEngine, AlphabetaPlayerEngine
from classes.Heuristic import ScoreHeuristicEngine, WeightHeuristicEngine, FullHeuristicEngine
import logging as log
import argparse as arg
from classes.CustomFormater import CustomFormatter
@ -13,7 +12,7 @@ Function to parse command line arguments
"""
def parse_aguments():
engines_choices=['random', 'human', 'minmax', 'alphabeta']
heuristic_choices=['score', 'weight', 'full']
heuristic_choices=['weight',]
parser = arg.ArgumentParser('Playing Reversi with (virtual) friend')
parser.add_argument('-we', '--white-engine',
@ -42,21 +41,14 @@ def parse_aguments():
parser.add_argument('-bh', '--black-heuristic-engine',
help='Black player heutistic engine',
choices= heuristic_choices,
default='score',
choices=['board', 'weight'],
default='board',
)
parser.add_argument('-wh', '--white-heuristic-engine',
help='White player heutistic engine',
choices=heuristic_choices,
default='score',
)
parser.add_argument('--weight',
help='Weight table for weight based heuristic engines',
type=int,
nargs=3,
default=[2,10,25]
choices=['board', 'weight'],
default='board',
)
debug_group = parser.add_mutually_exclusive_group()
@ -73,17 +65,12 @@ def parse_aguments():
Main Function
"""
if __name__ == '__main__':
player_engines = {
engines = {
"random": RandomPlayerEngine,
"human": HumanPlayerEngine,
"minmax": MinmaxPlayerEngine,
"alphabeta": AlphabetaPlayerEngine,
}
heuristic_engine = {
"score": ScoreHeuristicEngine,
"weight": WeightHeuristicEngine,
"full": FullHeuristicEngine,
}
print("Stating PyReverso...")
args = parse_aguments()
logger = log.getLogger()
@ -106,31 +93,14 @@ if __name__ == '__main__':
args.black_engine,
args.white_engine
))
logger.debug("Weight value {}".format( args.weight ))
wplayer = player_engines[args.white_engine](
game._WHITE,
logger,
heuristic_engine[args.white_heuristic_engine](
logger, {
'weight': args.weight
}
),
{
'depth': args.white_depth_exploration,
}
)
bplayer = player_engines[args.black_engine](
game._BLACK,
logger,
heuristic_engine[args.black_heuristic_engine](
logger, {
'weight': args.weight
}
),
{
'depth': args.black_depth_exploration,
}
)
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)