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