from random import randint, choice import math from typing import Callable import chess from heuristics import h_shannon class PlayerInterface: board: chess.Board color: chess.Color # Returns your player name , as to be displayed during the game def getPlayerName(self) -> str: return " Not Defined " # Returns your move. The move must be a valid string of coordinates ("A1", # "D5", ...) on the grid or the special "PASS" move. A couple of two integers , # which are the coordinates of where you want to put your piece on the board . # Coordinates are the coordinates given by the Goban . py method legal_moves (). def getPlayerMove(self) -> chess.Move: return chess.Move.null() # Inform you that the oponent has played this move. You must play it with no # search (just update your local variables to take it into account) def playOpponentMove(self, move): self.board.push(move) # Starts a new game , and give you your color . As defined in Goban . py : color =1 # for BLACK , and color =2 for WHITE def newGame(self, color): self.board = chess.Board() self.color = color # You can get a feedback on the winner # This function gives you the color of the winner def endGame(self, color): pass class RandomPlayer(PlayerInterface): # Returns your player name , as to be displayed during the game def getPlayerName(self) -> str: return "Random Player" def playOpponentMove(self, move): self.board.push(move) def getPlayerMove(self): """ Renvoie un mouvement au hasard sur la liste des mouvements possibles. Pour avoir un choix au hasard, il faut construire explicitement tous les mouvements. Or, generate_legal_moves() nous donne un itérateur. """ move = choice([m for m in self.board.generate_legal_moves()]) self.board.push(move) return move def minmax( board: chess.Board, heuristic: Callable[[chess.Board, chess.Color], float], color: chess.Color, depth: int = 3, ) -> chess.Move: def aux( board: chess.Board, heuristic: Callable[[chess.Board, chess.Color], float], color: chess.Color, depth: int = 3, ) -> tuple[float, chess.Move]: wantMax = board.turn == color if depth == 0 or board.is_game_over(): return heuristic(board, color), board.peek() if wantMax: acc = -math.inf, chess.Move.null() for move in board.generate_legal_moves(): board.push(move) acc = max( acc, ( aux(board, heuristic=heuristic, color=color, depth=depth - 1)[ 0 ], move, ), key=lambda t: t[0], ) board.pop() else: # minimizing player acc = math.inf, chess.Move.null() for move in board.generate_legal_moves(): board.push(move) acc = min( acc, ( aux(board, heuristic=heuristic, color=color, depth=depth - 1)[ 0 ], move, ), key=lambda t: t[0], ) board.pop() return acc _, move = aux(board, heuristic, color, depth) return move class MinMaxPlayer(PlayerInterface): depth = 3 # Returns your player name , as to be displayed during the game def getPlayerName(self) -> str: return "MinMax Player" def getPlayerMove(self): move = minmax( self.board, heuristic=h_shannon, color=self.color, depth=self.depth ) self.board.push(move) return move def alphabeta( board: chess.Board, heuristic: Callable[[chess.Board, chess.Color], float], color: chess.Color, depth: int = 3, ) -> chess.Move: def aux( board: chess.Board, heuristic: Callable[[chess.Board, chess.Color], float], color: chess.Color, alpha=-math.inf, beta=math.inf, depth: int = 3, ) -> tuple[float, chess.Move]: wantMax = board.turn == color if depth == 0 or board.is_game_over(): return heuristic(board, color), board.peek() if wantMax: acc = -math.inf, chess.Move.null() for move in board.generate_legal_moves(): board.push(move) value = ( aux( board, alpha=alpha, beta=beta, heuristic=heuristic, color=color, depth=depth - 1, )[0], move, ) acc = max( acc, value, key=lambda t: t[0], ) board.pop() if acc[0] >= beta: break # beta cutoff alpha = max(alpha, value[0]) else: acc = math.inf, chess.Move.null() for move in board.generate_legal_moves(): board.push(move) value = ( aux( board, alpha=alpha, beta=beta, heuristic=heuristic, color=color, depth=depth - 1, )[0], move, ) acc = min( acc, value, key=lambda t: t[0], ) board.pop() if acc[0] <= alpha: break # alpha cutoff beta = min(beta, value[0]) return acc _, move = aux(board, heuristic=heuristic, color=color, depth=depth) return move class AlphaBetaPlayer(PlayerInterface): depth = 5 # Returns your player name , as to be displayed during the game def getPlayerName(self) -> str: return "AlphaBeta Player" def getPlayerMove(self): move = alphabeta( self.board, heuristic=h_shannon, color=self.color, depth=self.depth ) self.board.push(move) return move