From 614bb5acb2be870cab922b0487a510dcbe0e584f Mon Sep 17 00:00:00 2001 From: Nemo D'ACREMONT Date: Thu, 8 May 2025 10:42:34 +0200 Subject: [PATCH] feat: add IDDFS player --- chess/heuristics.py | 21 +----- chess/main.py | 11 +++ chess/player.py | 160 ++++++++++++++++++++++++++------------------ 3 files changed, 106 insertions(+), 86 deletions(-) diff --git a/chess/heuristics.py b/chess/heuristics.py index 861c005..8c5a6ad 100644 --- a/chess/heuristics.py +++ b/chess/heuristics.py @@ -1,26 +1,6 @@ import chess import math -def b(board: chess.Board, color: chess.Color): - piece_values = { - chess.PAWN: 1, - chess.KNIGHT: 3, - chess.BISHOP: 3, - chess.ROOK: 5, - chess.QUEEN: 9, - chess.KING: 0 - } - - score = 0 - for square in chess.SQUARES: - piece = board.piece_at(square) - if piece is not None: - if piece.color == chess.WHITE: - score += piece_values[piece.piece_type] - else: - score -= piece_values[piece.piece_type] - return score - def h_shannon(board: chess.Board, color: chess.Color): piece_values = { chess.PAWN: 1, @@ -42,6 +22,7 @@ def h_shannon(board: chess.Board, color: chess.Color): for square in board.piece_map(): piece = board.piece_at(square) + if piece is not None: n = 1 if piece.color == color else -1 acc += n * piece_values[piece.piece_type] diff --git a/chess/main.py b/chess/main.py index b163276..88f37af 100755 --- a/chess/main.py +++ b/chess/main.py @@ -24,14 +24,21 @@ def play(white: player.PlayerInterface, black: player.PlayerInterface): board = chess.Board() white.newGame(chess.WHITE) black.newGame(chess.BLACK) + whiteTime = 0 + blackTime = 0 i = 1 while not board.is_game_over(): try: + st = time.time() if board.turn == chess.WHITE: move = step(board, white, black) + nd = time.time() + whiteTime += (nd - st) else: move = step(board, black, white) + nd = time.time() + blackTime += (nd - st) except: print(board) print(f"{color2str(board.turn)} cheated, end the game") @@ -42,9 +49,12 @@ def play(white: player.PlayerInterface, black: player.PlayerInterface): print(board) print(f"Move {move}") print(f"Turn {i}") + print(f"Time consumed : {nd - st:.5}") i += 1 print(board.outcome()) + print(f"White time : {whiteTime:.5}") + print(f"Black time : {blackTime:.5}") return board @@ -74,6 +84,7 @@ def t_enum(board, depth): if __name__ == "__main__": black = player.RandomPlayer() white = player.AlphaBetaPlayer() + # white = player.IDDFSPlayer() board = play(white, black) print(board) diff --git a/chess/player.py b/chess/player.py index 609e17a..b6a24d5 100644 --- a/chess/player.py +++ b/chess/player.py @@ -1,4 +1,5 @@ from random import randint, choice +import time import math from typing import Callable import chess @@ -125,79 +126,82 @@ class MinMaxPlayer(PlayerInterface): return move +# Returns heuristic, move +def _alphabeta( + 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 = ( + _alphabeta( + 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 = ( + _alphabeta( + 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 + + 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) + _, move = _alphabeta(board, heuristic=heuristic, color=color, depth=depth) return move @@ -214,3 +218,27 @@ class AlphaBetaPlayer(PlayerInterface): ) self.board.push(move) return move + + +def IDDFS( + board: chess.Board, heuristic, color: chess.Color, duration: float, maxdepth=42 +) -> chess.Move: + st = time.time() + depth = 1 + move = chess.Move.null() + while time.time() - st < duration and depth < maxdepth: + move = _alphabeta(board, heuristic, color, alpha=-10, beta=10, depth=depth)[1] + + depth += 1 + return move + + +class IDDFSPlayer(PlayerInterface): + # Returns your player name , as to be displayed during the game + def getPlayerName(self) -> str: + return "AlphaBeta Player" + + def getPlayerMove(self): + move = IDDFS(self.board, h_shannon, self.color, 1) + self.board.push(move) + return move