Merge remote-tracking branch 'origin/main' into refactor-mcts
# Conflicts: # main.py
This commit is contained in:
@@ -15,13 +15,13 @@ from typing import Dict
|
|||||||
class Limit:
|
class Limit:
|
||||||
""" Class to determine when to stop searching for moves """
|
""" Class to determine when to stop searching for moves """
|
||||||
|
|
||||||
time: float|None
|
time: float | None
|
||||||
""" Search for `time` seconds """
|
""" Search for `time` seconds """
|
||||||
|
|
||||||
nodes: int|None
|
nodes: int | None
|
||||||
""" Search for a limited number of `nodes`"""
|
""" Search for a limited number of `nodes`"""
|
||||||
|
|
||||||
def __init__(self, time: float|None = None, nodes: int|None = None):
|
def __init__(self, time: float | None = None, nodes: int | None = None):
|
||||||
self.time = time
|
self.time = time
|
||||||
self.nodes = nodes
|
self.nodes = nodes
|
||||||
|
|
||||||
@@ -44,9 +44,15 @@ class Limit:
|
|||||||
|
|
||||||
def _run_time(self, func, *args, **kwargs):
|
def _run_time(self, func, *args, **kwargs):
|
||||||
start = time.perf_counter_ns()
|
start = time.perf_counter_ns()
|
||||||
while (time.perf_counter_ns()-start)/1e9 < self.time:
|
while (time.perf_counter_ns() - start) / 1e9 < self.time:
|
||||||
func(*args, **kwargs)
|
func(*args, **kwargs)
|
||||||
|
|
||||||
|
def translate_to_engine_limit(self) -> chess.engine.Limit:
|
||||||
|
if self.nodes:
|
||||||
|
return chess.engine.Limit(nodes=self.nodes)
|
||||||
|
elif self.time:
|
||||||
|
return chess.engine.Limit(time=self.time)
|
||||||
|
|
||||||
|
|
||||||
class Engine(ABC):
|
class Engine(ABC):
|
||||||
board: chess.Board
|
board: chess.Board
|
||||||
@@ -56,7 +62,7 @@ class Engine(ABC):
|
|||||||
strategy: IStrategy
|
strategy: IStrategy
|
||||||
"""The strategy used to pick moves when simulating games."""
|
"""The strategy used to pick moves when simulating games."""
|
||||||
|
|
||||||
def __init__(self, board: chess.Board, color: chess.Color, strategy: IStrategy):
|
def __init__(self, board: chess.Board, color: chess.Color, strategy: IStrategy | None):
|
||||||
self.board = board
|
self.board = board
|
||||||
self.color = color
|
self.color = color
|
||||||
self.strategy = strategy
|
self.strategy = strategy
|
||||||
@@ -138,3 +144,29 @@ class RandomEngine(Engine):
|
|||||||
def play(self, board: chess.Board, limit: Limit) -> chess.engine.PlayResult:
|
def play(self, board: chess.Board, limit: Limit) -> chess.engine.PlayResult:
|
||||||
move = random.choice(list(board.legal_moves))
|
move = random.choice(list(board.legal_moves))
|
||||||
return chess.engine.PlayResult(move=move, ponder=None)
|
return chess.engine.PlayResult(move=move, ponder=None)
|
||||||
|
|
||||||
|
|
||||||
|
class StockFishEngine(Engine):
|
||||||
|
def __init__(self, board: chess.Board, color: chess, path="../stockfish/stockfish-ubuntu-x86-64-avx2"):
|
||||||
|
super().__init__(board, color, None)
|
||||||
|
self.stockfish = chess.engine.SimpleEngine.popen_uci(path)
|
||||||
|
|
||||||
|
def play(self, board: chess.Board, limit: Limit) -> chess.engine.PlayResult:
|
||||||
|
return self.stockfish.play(board, limit.translate_to_engine_limit())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_name() -> str:
|
||||||
|
return "Stockfish"
|
||||||
|
|
||||||
|
|
||||||
|
class Lc0Engine(Engine):
|
||||||
|
def __init__(self, board: chess.Board, color: chess, path="../lc0/lc0"):
|
||||||
|
super().__init__(board, color, None)
|
||||||
|
self.lc0 = chess.engine.SimpleEngine.popen_uci(path)
|
||||||
|
|
||||||
|
def play(self, board: chess.Board, limit: Limit) -> chess.engine.PlayResult:
|
||||||
|
return self.lc0.play(board, limit.translate_to_engine_limit())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_name() -> str:
|
||||||
|
return "Lc0"
|
||||||
|
|||||||
93
chesspp/engine_factory.py
Normal file
93
chesspp/engine_factory.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from chesspp.engine import *
|
||||||
|
from chesspp.lc0_strategy import Lc0Strategy
|
||||||
|
from chesspp.random_strategy import RandomStrategy
|
||||||
|
from chesspp.stockfish_strategy import StockFishStrategy
|
||||||
|
from chesspp.random_stockfish_strategy import RandomStockfishStrategy
|
||||||
|
from chesspp.pesto_strategy import PestoStrategy
|
||||||
|
from chesspp.i_strategy import IStrategy
|
||||||
|
import chess
|
||||||
|
|
||||||
|
|
||||||
|
class EngineEnum(Enum):
|
||||||
|
ClassicMcts = 0
|
||||||
|
BayesianMcts = 1
|
||||||
|
Stockfish = 2
|
||||||
|
Lc0 = 3
|
||||||
|
Random = 4
|
||||||
|
|
||||||
|
|
||||||
|
class StrategyEnum(Enum):
|
||||||
|
Stockfish = 0
|
||||||
|
Lc0 = 1
|
||||||
|
Random = 2
|
||||||
|
RandomStockfish = 3
|
||||||
|
Pestos = 4
|
||||||
|
|
||||||
|
|
||||||
|
class EngineFactory:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_engine(engine_name: EngineEnum, strategy_name: StrategyEnum, color: chess.Color, stockfish_path: str, lc0_path: str, rollout_depth: int = 4) -> Engine:
|
||||||
|
match strategy_name:
|
||||||
|
case StrategyEnum.Stockfish:
|
||||||
|
strategy = EngineFactory._get_stockfish_strategy(stockfish_path, rollout_depth)
|
||||||
|
case StrategyEnum.Lc0:
|
||||||
|
strategy = EngineFactory._get_lc0_strategy(lc0_path, rollout_depth)
|
||||||
|
case StrategyEnum.Random:
|
||||||
|
strategy = EngineFactory._get_random_strategy(rollout_depth)
|
||||||
|
case StrategyEnum.RandomStockfish:
|
||||||
|
strategy = EngineFactory._get_random_stockfish_strategy(stockfish_path, rollout_depth)
|
||||||
|
case StrategyEnum.Pestos:
|
||||||
|
strategy = EngineFactory._get_pesto_strategy(rollout_depth)
|
||||||
|
|
||||||
|
match engine_name:
|
||||||
|
case EngineEnum.ClassicMcts:
|
||||||
|
return EngineFactory.classic_mcts(color, strategy)
|
||||||
|
|
||||||
|
case EngineEnum.BayesianMcts:
|
||||||
|
return EngineFactory.bayesian_mcts(color, strategy)
|
||||||
|
|
||||||
|
case EngineEnum.Stockfish:
|
||||||
|
return EngineFactory.stockfish_engine(color, stockfish_path)
|
||||||
|
|
||||||
|
case EngineEnum.Lc0:
|
||||||
|
return EngineFactory.lc0_engine(color, lc0_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stockfish_engine(color: chess.Color, engine_path: str, board: chess.Board | None = chess.Board()) -> Engine:
|
||||||
|
return StockFishEngine(board, color, engine_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lc0_engine(color: chess.Color, engine_path: str, board: chess.Board | None = chess.Board()) -> Engine:
|
||||||
|
return Lc0Engine(board, color, engine_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def bayesian_mcts(color: chess.Color, strategy: IStrategy, board: chess.Board | None = chess.Board()) -> Engine:
|
||||||
|
return BayesMctsEngine(board, color, strategy)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def classic_mcts(color: chess.Color, strategy: IStrategy, board: chess.Board | None = chess.Board()) -> Engine:
|
||||||
|
return ClassicMctsEngine(board, color, strategy)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_random_strategy(rollout_depth: int) -> IStrategy:
|
||||||
|
return RandomStrategy(random.Random(), rollout_depth)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_stockfish_strategy(engine_path: str, rollout_depth: int) -> IStrategy:
|
||||||
|
return StockFishStrategy(engine_path, rollout_depth)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_random_stockfish_strategy(engine_path: str, rollout_depth: int) -> IStrategy:
|
||||||
|
return RandomStockfishStrategy(rollout_depth, engine_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_lc0_strategy(engine_path: str, rollout_depth: int) -> IStrategy:
|
||||||
|
return Lc0Strategy(engine_path, rollout_depth)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_pesto_strategy(rollout_depth: int) -> IStrategy:
|
||||||
|
return PestoStrategy(rollout_depth)
|
||||||
|
|
||||||
@@ -173,30 +173,38 @@ def score_manual(board: chess.Board) -> int:
|
|||||||
return score
|
return score
|
||||||
|
|
||||||
|
|
||||||
def score_stockfish(board: chess.Board, stockfish: chess.engine.SimpleEngine | None = None) -> int:
|
def score_stockfish(board: chess.Board, stockfish: chess.engine.SimpleEngine | None = None,
|
||||||
|
limit: chess.engine.Limit = chess.engine.Limit(depth=0)) -> int:
|
||||||
"""
|
"""
|
||||||
Calculate the score of the given board using stockfish
|
Calculate the score of the given board using stockfish
|
||||||
:param board:
|
:param board:
|
||||||
|
:param stockfish:
|
||||||
|
:param limit:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if stockfish is None:
|
if stockfish is None:
|
||||||
engine = chess.engine.SimpleEngine.popen_uci(
|
engine = chess.engine.SimpleEngine.popen_uci(
|
||||||
"/home/luke/projects/pp-project/chess-engine-pp/stockfish/stockfish-ubuntu-x86-64-avx2")
|
"/home/luke/projects/pp-project/chess-engine-pp/stockfish/stockfish-ubuntu-x86-64-avx2")
|
||||||
info = engine.analyse(board, chess.engine.Limit(depth=0))
|
info = engine.analyse(board, limit)
|
||||||
engine.quit()
|
engine.quit()
|
||||||
return info['score'].white().score(mate_score=100_000)
|
return info['score'].white().score(mate_score=100_000)
|
||||||
else:
|
else:
|
||||||
info = stockfish.analyse(board, chess.engine.Limit(depth=0))
|
info = stockfish.analyse(board, limit)
|
||||||
return info['score'].white().score(mate_score=100_000)
|
return info['score'].white().score(mate_score=100_000)
|
||||||
|
|
||||||
|
|
||||||
def score_lc0(board: chess.Board) -> chess.engine.PovScore:
|
def score_lc0(board: chess.Board, lc0: chess.engine.SimpleEngine | None = None,
|
||||||
|
limit: chess.engine.Limit= chess.engine.Limit(depth=0)) -> int:
|
||||||
"""
|
"""
|
||||||
Calculate the score of the given board using lc0
|
Calculate the score of the given board using lc0
|
||||||
:param board:
|
:param board:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
if lc0 is None:
|
||||||
engine = chess.engine.SimpleEngine.popen_uci("/home/luke/projects/pp-project/chess-engine-pp/lc0/lc0")
|
engine = chess.engine.SimpleEngine.popen_uci("/home/luke/projects/pp-project/chess-engine-pp/lc0/lc0")
|
||||||
info = engine.analyse(board, chess.engine.Limit(depth=4))
|
info = engine.analyse(board, limit)
|
||||||
engine.quit()
|
engine.quit()
|
||||||
return info["score"]
|
return info["score"]
|
||||||
|
else:
|
||||||
|
info = lc0.analyse(board, limit)
|
||||||
|
return info['score'].white().score(mate_score=100_000)
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ from abc import ABC, abstractmethod
|
|||||||
import chess
|
import chess
|
||||||
|
|
||||||
|
|
||||||
# TODO extend class
|
|
||||||
class IStrategy(ABC):
|
class IStrategy(ABC):
|
||||||
|
rollout_depth: int
|
||||||
|
|
||||||
|
def __init__(self, rollout_depth: int = 4):
|
||||||
|
self.rollout_depth = rollout_depth
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def pick_next_move(self, board: chess.Board) -> chess.Move:
|
def pick_next_move(self, board: chess.Board) -> chess.Move:
|
||||||
|
|||||||
38
chesspp/lc0_strategy.py
Normal file
38
chesspp/lc0_strategy.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import chess
|
||||||
|
import chess.engine
|
||||||
|
import os
|
||||||
|
|
||||||
|
from chesspp.i_strategy import IStrategy
|
||||||
|
from chesspp.eval import score_lc0
|
||||||
|
|
||||||
|
_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class Lc0Strategy(IStrategy):
|
||||||
|
def __init__(self, path="lc0/lc0", rollout_depth: int = 4,
|
||||||
|
limit: chess.engine.Limit = chess.engine.Limit(depth=4)):
|
||||||
|
super().__init__(rollout_depth)
|
||||||
|
self._lc0 = None
|
||||||
|
self.path = path
|
||||||
|
self.limit = limit
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self._lc0 is not None:
|
||||||
|
self._lc0.quit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lc0(self) -> chess.engine.SimpleEngine:
|
||||||
|
if self._lc0 is None:
|
||||||
|
self._lc0 = self.lc0 = chess.engine.SimpleEngine.popen_uci(self.path)
|
||||||
|
return self._lc0
|
||||||
|
|
||||||
|
@lc0.setter
|
||||||
|
def lc0(self, value):
|
||||||
|
self._lc0 = value
|
||||||
|
|
||||||
|
def pick_next_move(self, board: chess.Board) -> chess.Move | None:
|
||||||
|
return self.lc0.play(board, self.limit).move
|
||||||
|
|
||||||
|
def analyze_board(self, board: chess.Board) -> int:
|
||||||
|
score = score_lc0(board, self.lc0)
|
||||||
|
return score
|
||||||
339
chesspp/pesto_strategy.py
Normal file
339
chesspp/pesto_strategy.py
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
import chess.engine
|
||||||
|
import chess
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
|
from chesspp.i_strategy import IStrategy
|
||||||
|
import numba
|
||||||
|
|
||||||
|
# Scoring based on PeSTO (Piece-Square Tables Only) Evaluation Functions
|
||||||
|
# https://www.chessprogramming.org/PeSTO%27s_Evaluation_Function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PAWN = 0
|
||||||
|
KNIGHT = 1
|
||||||
|
BISHOP = 2
|
||||||
|
ROOK = 3
|
||||||
|
QUEEN = 4
|
||||||
|
KING = 5
|
||||||
|
|
||||||
|
# board representation
|
||||||
|
WHITE = 0
|
||||||
|
BLACK = 1
|
||||||
|
|
||||||
|
WHITE_PAWN = (2*PAWN + WHITE)
|
||||||
|
BLACK_PAWN = (2*PAWN + BLACK)
|
||||||
|
WHITE_KNIGHT = (2*KNIGHT + WHITE)
|
||||||
|
BLACK_KNIGHT = (2*KNIGHT + BLACK)
|
||||||
|
WHITE_BISHOP = (2*BISHOP + WHITE)
|
||||||
|
BLACK_BISHOP = (2*BISHOP + BLACK)
|
||||||
|
WHITE_ROOK = (2*ROOK + WHITE)
|
||||||
|
BLACK_ROOK = (2*ROOK + BLACK)
|
||||||
|
WHITE_QUEEN = (2*QUEEN + WHITE)
|
||||||
|
BLACK_QUEEN = (2*QUEEN + BLACK)
|
||||||
|
WHITE_KING = (2*KING + WHITE)
|
||||||
|
BLACK_KING = (2*KING + BLACK)
|
||||||
|
EMPTY = (BLACK_KING + 1)
|
||||||
|
|
||||||
|
mg_value = [82, 337, 365, 477, 1025, 0]
|
||||||
|
eg_value = [94, 281, 297, 512, 936, 0]
|
||||||
|
|
||||||
|
|
||||||
|
FLIP = lambda sq: (sq^56)
|
||||||
|
OTHER = lambda side: (side^1)
|
||||||
|
|
||||||
|
|
||||||
|
mg_pawn_table = [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
98, 134, 61, 95, 68, 126, 34, -11,
|
||||||
|
-6, 7, 26, 31, 65, 56, 25, -20,
|
||||||
|
-14, 13, 6, 21, 23, 12, 17, -23,
|
||||||
|
-27, -2, -5, 12, 17, 6, 10, -25,
|
||||||
|
-26, -4, -4, -10, 3, 3, 33, -12,
|
||||||
|
-35, -1, -20, -23, -15, 24, 38, -22,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_pawn_table = [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
178, 173, 158, 134, 147, 132, 165, 187,
|
||||||
|
94, 100, 85, 67, 56, 53, 82, 84,
|
||||||
|
32, 24, 13, 5, -2, 4, 17, 17,
|
||||||
|
13, 9, -3, -7, -7, -8, 3, -1,
|
||||||
|
4, 7, -6, 1, 0, -5, -1, -8,
|
||||||
|
13, 8, 8, 10, 13, 0, 2, -7,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]
|
||||||
|
|
||||||
|
mg_knight_table = [
|
||||||
|
-167, -89, -34, -49, 61, -97, -15, -107,
|
||||||
|
-73, -41, 72, 36, 23, 62, 7, -17,
|
||||||
|
-47, 60, 37, 65, 84, 129, 73, 44,
|
||||||
|
-9, 17, 19, 53, 37, 69, 18, 22,
|
||||||
|
-13, 4, 16, 13, 28, 19, 21, -8,
|
||||||
|
-23, -9, 12, 10, 19, 17, 25, -16,
|
||||||
|
-29, -53, -12, -3, -1, 18, -14, -19,
|
||||||
|
-105, -21, -58, -33, -17, -28, -19, -23,
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_knight_table = [
|
||||||
|
-58, -38, -13, -28, -31, -27, -63, -99,
|
||||||
|
-25, -8, -25, -2, -9, -25, -24, -52,
|
||||||
|
-24, -20, 10, 9, -1, -9, -19, -41,
|
||||||
|
-17, 3, 22, 22, 22, 11, 8, -18,
|
||||||
|
-18, -6, 16, 25, 16, 17, 4, -18,
|
||||||
|
-23, -3, -1, 15, 10, -3, -20, -22,
|
||||||
|
-42, -20, -10, -5, -2, -20, -23, -44,
|
||||||
|
-29, -51, -23, -15, -22, -18, -50, -64,
|
||||||
|
]
|
||||||
|
|
||||||
|
mg_bishop_table = [
|
||||||
|
-29, 4, -82, -37, -25, -42, 7, -8,
|
||||||
|
-26, 16, -18, -13, 30, 59, 18, -47,
|
||||||
|
-16, 37, 43, 40, 35, 50, 37, -2,
|
||||||
|
-4, 5, 19, 50, 37, 37, 7, -2,
|
||||||
|
-6, 13, 13, 26, 34, 12, 10, 4,
|
||||||
|
0, 15, 15, 15, 14, 27, 18, 10,
|
||||||
|
4, 15, 16, 0, 7, 21, 33, 1,
|
||||||
|
-33, -3, -14, -21, -13, -12, -39, -21,
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_bishop_table = [
|
||||||
|
-14, -21, -11, -8, -7, -9, -17, -24,
|
||||||
|
-8, -4, 7, -12, -3, -13, -4, -14,
|
||||||
|
2, -8, 0, -1, -2, 6, 0, 4,
|
||||||
|
-3, 9, 12, 9, 14, 10, 3, 2,
|
||||||
|
-6, 3, 13, 19, 7, 10, -3, -9,
|
||||||
|
-12, -3, 8, 10, 13, 3, -7, -15,
|
||||||
|
-14, -18, -7, -1, 4, -9, -15, -27,
|
||||||
|
-23, -9, -23, -5, -9, -16, -5, -17,
|
||||||
|
]
|
||||||
|
|
||||||
|
mg_rook_table = [
|
||||||
|
32, 42, 32, 51, 63, 9, 31, 43,
|
||||||
|
27, 32, 58, 62, 80, 67, 26, 44,
|
||||||
|
-5, 19, 26, 36, 17, 45, 61, 16,
|
||||||
|
-24, -11, 7, 26, 24, 35, -8, -20,
|
||||||
|
-36, -26, -12, -1, 9, -7, 6, -23,
|
||||||
|
-45, -25, -16, -17, 3, 0, -5, -33,
|
||||||
|
-44, -16, -20, -9, -1, 11, -6, -71,
|
||||||
|
-19, -13, 1, 17, 16, 7, -37, -26,
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_rook_table = [
|
||||||
|
13, 10, 18, 15, 12, 12, 8, 5,
|
||||||
|
11, 13, 13, 11, -3, 3, 8, 3,
|
||||||
|
7, 7, 7, 5, 4, -3, -5, -3,
|
||||||
|
4, 3, 13, 1, 2, 1, -1, 2,
|
||||||
|
3, 5, 8, 4, -5, -6, -8, -11,
|
||||||
|
-4, 0, -5, -1, -7, -12, -8, -16,
|
||||||
|
-6, -6, 0, 2, -9, -9, -11, -3,
|
||||||
|
-9, 2, 3, -1, -5, -13, 4, -20,
|
||||||
|
]
|
||||||
|
|
||||||
|
mg_queen_table = [
|
||||||
|
-28, 0, 29, 12, 59, 44, 43, 45,
|
||||||
|
-24, -39, -5, 1, -16, 57, 28, 54,
|
||||||
|
-13, -17, 7, 8, 29, 56, 47, 57,
|
||||||
|
-27, -27, -16, -16, -1, 17, -2, 1,
|
||||||
|
-9, -26, -9, -10, -2, -4, 3, -3,
|
||||||
|
-14, 2, -11, -2, -5, 2, 14, 5,
|
||||||
|
-35, -8, 11, 2, 8, 15, -3, 1,
|
||||||
|
-1, -18, -9, 10, -15, -25, -31, -50,
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_queen_table = [
|
||||||
|
-9, 22, 22, 27, 27, 19, 10, 20,
|
||||||
|
-17, 20, 32, 41, 58, 25, 30, 0,
|
||||||
|
-20, 6, 9, 49, 47, 35, 19, 9,
|
||||||
|
3, 22, 24, 45, 57, 40, 57, 36,
|
||||||
|
-18, 28, 19, 47, 31, 34, 39, 23,
|
||||||
|
-16, -27, 15, 6, 9, 17, 10, 5,
|
||||||
|
-22, -23, -30, -16, -16, -23, -36, -32,
|
||||||
|
-33, -28, -22, -43, -5, -32, -20, -41,
|
||||||
|
]
|
||||||
|
|
||||||
|
mg_king_table = [
|
||||||
|
-65, 23, 16, -15, -56, -34, 2, 13,
|
||||||
|
29, -1, -20, -7, -8, -4, -38, -29,
|
||||||
|
-9, 24, 2, -16, -20, 6, 22, -22,
|
||||||
|
-17, -20, -12, -27, -30, -25, -14, -36,
|
||||||
|
-49, -1, -27, -39, -46, -44, -33, -51,
|
||||||
|
-14, -14, -22, -46, -44, -30, -15, -27,
|
||||||
|
1, 7, -8, -64, -43, -16, 9, 8,
|
||||||
|
-15, 36, 12, -54, 8, -28, 24, 14,
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_king_table = [
|
||||||
|
-74, -35, -18, -18, -11, 15, 4, -17,
|
||||||
|
-12, 17, 14, 17, 17, 38, 23, 11,
|
||||||
|
10, 17, 23, 15, 20, 45, 44, 13,
|
||||||
|
-8, 22, 24, 27, 26, 33, 26, 3,
|
||||||
|
-18, -4, 21, 24, 27, 23, 9, -11,
|
||||||
|
-19, -3, 11, 21, 23, 16, 7, -9,
|
||||||
|
-27, -11, 4, 13, 14, 4, -5, -17,
|
||||||
|
-53, -34, -21, -11, -28, -14, -24, -43
|
||||||
|
]
|
||||||
|
|
||||||
|
mg_pesto_table = [
|
||||||
|
mg_pawn_table,
|
||||||
|
mg_knight_table,
|
||||||
|
mg_bishop_table,
|
||||||
|
mg_rook_table,
|
||||||
|
mg_queen_table,
|
||||||
|
mg_king_table
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_pesto_table = [
|
||||||
|
eg_pawn_table,
|
||||||
|
eg_knight_table,
|
||||||
|
eg_bishop_table,
|
||||||
|
eg_rook_table,
|
||||||
|
eg_queen_table,
|
||||||
|
eg_king_table
|
||||||
|
]
|
||||||
|
|
||||||
|
gamephaseInc = [0,0,1,1,1,1,2,2,4,4,0,0]
|
||||||
|
|
||||||
|
mg_table = [
|
||||||
|
[82, 82, 82, 82, 82, 82, 82, 82, 180, 216, 143, 177, 150, 208, 116, 71, 76, 89, 108, 113, 147, 138, 107, 62, 68, 95, 88, 103, 105, 94, 99, 59, 55, 80, 77, 94, 99, 88, 92, 57, 56, 78, 78, 72, 85, 85, 115, 70, 47, 81, 62, 59, 67, 106, 120, 60, 82, 82, 82, 82, 82, 82, 82, 82, ],
|
||||||
|
[82, 82, 82, 82, 82, 82, 82, 82, 47, 81, 62, 59, 67, 106, 120, 60, 56, 78, 78, 72, 85, 85, 115, 70, 55, 80, 77, 94, 99, 88, 92, 57, 68, 95, 88, 103, 105, 94, 99, 59, 76, 89, 108, 113, 147, 138, 107, 62, 180, 216, 143, 177, 150, 208, 116, 71, 82, 82, 82, 82, 82, 82, 82, 82, ],
|
||||||
|
[170, 248, 303, 288, 398, 240, 322, 230, 264, 296, 409, 373, 360, 399, 344, 320, 290, 397, 374, 402, 421, 466, 410, 381, 328, 354, 356, 390, 374, 406, 355, 359, 324, 341, 353, 350, 365, 356, 358, 329, 314, 328, 349, 347, 356, 354, 362, 321, 308, 284, 325, 334, 336, 355, 323, 318, 232, 316, 279, 304, 320, 309, 318, 314, ],
|
||||||
|
[232, 316, 279, 304, 320, 309, 318, 314, 308, 284, 325, 334, 336, 355, 323, 318, 314, 328, 349, 347, 356, 354, 362, 321, 324, 341, 353, 350, 365, 356, 358, 329, 328, 354, 356, 390, 374, 406, 355, 359, 290, 397, 374, 402, 421, 466, 410, 381, 264, 296, 409, 373, 360, 399, 344, 320, 170, 248, 303, 288, 398, 240, 322, 230, ],
|
||||||
|
[336, 369, 283, 328, 340, 323, 372, 357, 339, 381, 347, 352, 395, 424, 383, 318, 349, 402, 408, 405, 400, 415, 402, 363, 361, 370, 384, 415, 402, 402, 372, 363, 359, 378, 378, 391, 399, 377, 375, 369, 365, 380, 380, 380, 379, 392, 383, 375, 369, 380, 381, 365, 372, 386, 398, 366, 332, 362, 351, 344, 352, 353, 326, 344, ],
|
||||||
|
[332, 362, 351, 344, 352, 353, 326, 344, 369, 380, 381, 365, 372, 386, 398, 366, 365, 380, 380, 380, 379, 392, 383, 375, 359, 378, 378, 391, 399, 377, 375, 369, 361, 370, 384, 415, 402, 402, 372, 363, 349, 402, 408, 405, 400, 415, 402, 363, 339, 381, 347, 352, 395, 424, 383, 318, 336, 369, 283, 328, 340, 323, 372, 357, ],
|
||||||
|
[509, 519, 509, 528, 540, 486, 508, 520, 504, 509, 535, 539, 557, 544, 503, 521, 472, 496, 503, 513, 494, 522, 538, 493, 453, 466, 484, 503, 501, 512, 469, 457, 441, 451, 465, 476, 486, 470, 483, 454, 432, 452, 461, 460, 480, 477, 472, 444, 433, 461, 457, 468, 476, 488, 471, 406, 458, 464, 478, 494, 493, 484, 440, 451, ],
|
||||||
|
[458, 464, 478, 494, 493, 484, 440, 451, 433, 461, 457, 468, 476, 488, 471, 406, 432, 452, 461, 460, 480, 477, 472, 444, 441, 451, 465, 476, 486, 470, 483, 454, 453, 466, 484, 503, 501, 512, 469, 457, 472, 496, 503, 513, 494, 522, 538, 493, 504, 509, 535, 539, 557, 544, 503, 521, 509, 519, 509, 528, 540, 486, 508, 520, ],
|
||||||
|
[997, 1025, 1054, 1037, 1084, 1069, 1068, 1070, 1001, 986, 1020, 1026, 1009, 1082, 1053, 1079, 1012, 1008, 1032, 1033, 1054, 1081, 1072, 1082, 998, 998, 1009, 1009, 1024, 1042, 1023, 1026, 1016, 999, 1016, 1015, 1023, 1021, 1028, 1022, 1011, 1027, 1014, 1023, 1020, 1027, 1039, 1030, 990, 1017, 1036, 1027, 1033, 1040, 1022, 1026, 1024, 1007, 1016, 1035, 1010, 1000, 994, 975, ],
|
||||||
|
[1024, 1007, 1016, 1035, 1010, 1000, 994, 975, 990, 1017, 1036, 1027, 1033, 1040, 1022, 1026, 1011, 1027, 1014, 1023, 1020, 1027, 1039, 1030, 1016, 999, 1016, 1015, 1023, 1021, 1028, 1022, 998, 998, 1009, 1009, 1024, 1042, 1023, 1026, 1012, 1008, 1032, 1033, 1054, 1081, 1072, 1082, 1001, 986, 1020, 1026, 1009, 1082, 1053, 1079, 997, 1025, 1054, 1037, 1084, 1069, 1068, 1070, ],
|
||||||
|
[-65, 23, 16, -15, -56, -34, 2, 13, 29, -1, -20, -7, -8, -4, -38, -29, -9, 24, 2, -16, -20, 6, 22, -22, -17, -20, -12, -27, -30, -25, -14, -36, -49, -1, -27, -39, -46, -44, -33, -51, -14, -14, -22, -46, -44, -30, -15, -27, 1, 7, -8, -64, -43, -16, 9, 8, -15, 36, 12, -54, 8, -28, 24, 14, ],
|
||||||
|
[-15, 36, 12, -54, 8, -28, 24, 14, 1, 7, -8, -64, -43, -16, 9, 8, -14, -14, -22, -46, -44, -30, -15, -27, -49, -1, -27, -39, -46, -44, -33, -51, -17, -20, -12, -27, -30, -25, -14, -36, -9, 24, 2, -16, -20, 6, 22, -22, 29, -1, -20, -7, -8, -4, -38, -29, -65, 23, 16, -15, -56, -34, 2, 13, ]
|
||||||
|
]
|
||||||
|
|
||||||
|
eg_table = [
|
||||||
|
[94, 94, 94, 94, 94, 94, 94, 94, 272, 267, 252, 228, 241, 226, 259, 281, 188, 194, 179, 161, 150, 147, 176, 178, 126, 118, 107, 99, 92, 98, 111, 111, 107, 103, 91, 87, 87, 86, 97, 93, 98, 101, 88, 95, 94, 89, 93, 86, 107, 102, 102, 104, 107, 94, 96, 87, 94, 94, 94, 94, 94, 94, 94, 94, ],
|
||||||
|
[94, 94, 94, 94, 94, 94, 94, 94, 107, 102, 102, 104, 107, 94, 96, 87, 98, 101, 88, 95, 94, 89, 93, 86, 107, 103, 91, 87, 87, 86, 97, 93, 126, 118, 107, 99, 92, 98, 111, 111, 188, 194, 179, 161, 150, 147, 176, 178, 272, 267, 252, 228, 241, 226, 259, 281, 94, 94, 94, 94, 94, 94, 94, 94, ],
|
||||||
|
[223, 243, 268, 253, 250, 254, 218, 182, 256, 273, 256, 279, 272, 256, 257, 229, 257, 261, 291, 290, 280, 272, 262, 240, 264, 284, 303, 303, 303, 292, 289, 263, 263, 275, 297, 306, 297, 298, 285, 263, 258, 278, 280, 296, 291, 278, 261, 259, 239, 261, 271, 276, 279, 261, 258, 237, 252, 230, 258, 266, 259, 263, 231, 217, ],
|
||||||
|
[252, 230, 258, 266, 259, 263, 231, 217, 239, 261, 271, 276, 279, 261, 258, 237, 258, 278, 280, 296, 291, 278, 261, 259, 263, 275, 297, 306, 297, 298, 285, 263, 264, 284, 303, 303, 303, 292, 289, 263, 257, 261, 291, 290, 280, 272, 262, 240, 256, 273, 256, 279, 272, 256, 257, 229, 223, 243, 268, 253, 250, 254, 218, 182, ],
|
||||||
|
[283, 276, 286, 289, 290, 288, 280, 273, 289, 293, 304, 285, 294, 284, 293, 283, 299, 289, 297, 296, 295, 303, 297, 301, 294, 306, 309, 306, 311, 307, 300, 299, 291, 300, 310, 316, 304, 307, 294, 288, 285, 294, 305, 307, 310, 300, 290, 282, 283, 279, 290, 296, 301, 288, 282, 270, 274, 288, 274, 292, 288, 281, 292, 280, ],
|
||||||
|
[274, 288, 274, 292, 288, 281, 292, 280, 283, 279, 290, 296, 301, 288, 282, 270, 285, 294, 305, 307, 310, 300, 290, 282, 291, 300, 310, 316, 304, 307, 294, 288, 294, 306, 309, 306, 311, 307, 300, 299, 299, 289, 297, 296, 295, 303, 297, 301, 289, 293, 304, 285, 294, 284, 293, 283, 283, 276, 286, 289, 290, 288, 280, 273, ],
|
||||||
|
[525, 522, 530, 527, 524, 524, 520, 517, 523, 525, 525, 523, 509, 515, 520, 515, 519, 519, 519, 517, 516, 509, 507, 509, 516, 515, 525, 513, 514, 513, 511, 514, 515, 517, 520, 516, 507, 506, 504, 501, 508, 512, 507, 511, 505, 500, 504, 496, 506, 506, 512, 514, 503, 503, 501, 509, 503, 514, 515, 511, 507, 499, 516, 492, ],
|
||||||
|
[503, 514, 515, 511, 507, 499, 516, 492, 506, 506, 512, 514, 503, 503, 501, 509, 508, 512, 507, 511, 505, 500, 504, 496, 515, 517, 520, 516, 507, 506, 504, 501, 516, 515, 525, 513, 514, 513, 511, 514, 519, 519, 519, 517, 516, 509, 507, 509, 523, 525, 525, 523, 509, 515, 520, 515, 525, 522, 530, 527, 524, 524, 520, 517, ],
|
||||||
|
[927, 958, 958, 963, 963, 955, 946, 956, 919, 956, 968, 977, 994, 961, 966, 936, 916, 942, 945, 985, 983, 971, 955, 945, 939, 958, 960, 981, 993, 976, 993, 972, 918, 964, 955, 983, 967, 970, 975, 959, 920, 909, 951, 942, 945, 953, 946, 941, 914, 913, 906, 920, 920, 913, 900, 904, 903, 908, 914, 893, 931, 904, 916, 895, ],
|
||||||
|
[903, 908, 914, 893, 931, 904, 916, 895, 914, 913, 906, 920, 920, 913, 900, 904, 920, 909, 951, 942, 945, 953, 946, 941, 918, 964, 955, 983, 967, 970, 975, 959, 939, 958, 960, 981, 993, 976, 993, 972, 916, 942, 945, 985, 983, 971, 955, 945, 919, 956, 968, 977, 994, 961, 966, 936, 927, 958, 958, 963, 963, 955, 946, 956, ],
|
||||||
|
[-74, -35, -18, -18, -11, 15, 4, -17, -12, 17, 14, 17, 17, 38, 23, 11, 10, 17, 23, 15, 20, 45, 44, 13, -8, 22, 24, 27, 26, 33, 26, 3, -18, -4, 21, 24, 27, 23, 9, -11, -19, -3, 11, 21, 23, 16, 7, -9, -27, -11, 4, 13, 14, 4, -5, -17, -53, -34, -21, -11, -28, -14, -24, -43, ],
|
||||||
|
[-53, -34, -21, -11, -28, -14, -24, -43, -27, -11, 4, 13, 14, 4, -5, -17, -19, -3, 11, 21, 23, 16, 7, -9, -18, -4, 21, 24, 27, 23, 9, -11, -8, 22, 24, 27, 26, 33, 26, 3, 10, 17, 23, 15, 20, 45, 44, 13, -12, 17, 14, 17, 17, 38, 23, 11, -74, -35, -18, -18, -11, 15, 4, -17, ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def score(board: chess.Board) -> int:
|
||||||
|
mg = [0, 0]
|
||||||
|
eg = [0, 0]
|
||||||
|
game_phase = 0
|
||||||
|
|
||||||
|
# evaluate each piece
|
||||||
|
for sq in range(64):
|
||||||
|
pc = board.piece_at(sq)
|
||||||
|
if pc is not None:
|
||||||
|
color = 0 if pc.color == chess.WHITE else 1
|
||||||
|
piece_index = (pc.piece_type-1)*2 + color
|
||||||
|
mg[color] += mg_table[piece_index][sq]
|
||||||
|
eg[color] += eg_table[piece_index][sq]
|
||||||
|
game_phase += gamephaseInc[piece_index]
|
||||||
|
|
||||||
|
# tapered eval
|
||||||
|
side2move = 0 if board.turn == chess.WHITE else 1
|
||||||
|
mg_score = mg[side2move] - mg[OTHER(side2move)]
|
||||||
|
eg_score = eg[side2move] - eg[OTHER(side2move)]
|
||||||
|
mg_phase = game_phase
|
||||||
|
if mg_phase > 24:
|
||||||
|
# in case of early promotion
|
||||||
|
mg_phase = 24
|
||||||
|
eg_phase = 24 - mg_phase
|
||||||
|
return (mg_score * mg_phase + eg_score * eg_phase) // 24
|
||||||
|
|
||||||
|
flip = [
|
||||||
|
56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55,
|
||||||
|
40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39,
|
||||||
|
24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23,
|
||||||
|
8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _init_tables():
|
||||||
|
global mg_table, eg_table
|
||||||
|
|
||||||
|
for i in range(64):
|
||||||
|
mg_table[WHITE_PAWN][i] = mg_pawn_table[i]
|
||||||
|
mg_table[BLACK_PAWN][i] = mg_pawn_table[flip[i]]
|
||||||
|
mg_table[WHITE_KNIGHT][i] = mg_knight_table[i]
|
||||||
|
mg_table[BLACK_KNIGHT][i] = mg_knight_table[flip[i]]
|
||||||
|
mg_table[WHITE_BISHOP][i] = mg_bishop_table[i]
|
||||||
|
mg_table[BLACK_BISHOP][i] = mg_bishop_table[flip[i]]
|
||||||
|
mg_table[WHITE_ROOK][i] = mg_rook_table[i]
|
||||||
|
mg_table[BLACK_ROOK][i] = mg_rook_table[flip[i]]
|
||||||
|
mg_table[WHITE_QUEEN][i] = mg_queen_table[i]
|
||||||
|
mg_table[BLACK_QUEEN][i] = mg_queen_table[flip[i]]
|
||||||
|
mg_table[WHITE_KING][i] = mg_king_table[i]
|
||||||
|
mg_table[BLACK_KING][i] = mg_knight_table[flip[i]]
|
||||||
|
|
||||||
|
eg_table[WHITE_PAWN][i] = eg_pawn_table[i]
|
||||||
|
eg_table[BLACK_PAWN][i] = eg_pawn_table[flip[i]]
|
||||||
|
eg_table[WHITE_KNIGHT][i] = eg_knight_table[i]
|
||||||
|
eg_table[BLACK_KNIGHT][i] = eg_knight_table[flip[i]]
|
||||||
|
eg_table[WHITE_BISHOP][i] = eg_bishop_table[i]
|
||||||
|
eg_table[BLACK_BISHOP][i] = eg_bishop_table[flip[i]]
|
||||||
|
eg_table[WHITE_ROOK][i] = eg_rook_table[i]
|
||||||
|
eg_table[BLACK_ROOK][i] = eg_rook_table[flip[i]]
|
||||||
|
eg_table[WHITE_QUEEN][i] = eg_queen_table[i]
|
||||||
|
eg_table[BLACK_QUEEN][i] = eg_queen_table[flip[i]]
|
||||||
|
eg_table[WHITE_KING][i] = eg_king_table[i]
|
||||||
|
eg_table[BLACK_KING][i] = eg_knight_table[flip[i]]
|
||||||
|
|
||||||
|
for piece in range(6):
|
||||||
|
for field in range(64):
|
||||||
|
mg_table[piece][field] += mg_value[piece]
|
||||||
|
eg_table[piece][field] += eg_value[piece]
|
||||||
|
|
||||||
|
_init_tables()
|
||||||
|
|
||||||
|
class PestoStrategy(IStrategy):
|
||||||
|
def __init__(self, rollout_depth: int = 4):
|
||||||
|
super().__init__(rollout_depth)
|
||||||
|
|
||||||
|
def pick_next_move(self, board: chess.Board) -> chess.Move | None:
|
||||||
|
def score_move(move: chess.Move):
|
||||||
|
bc = board.copy(stack=False)
|
||||||
|
bc.push(move)
|
||||||
|
return move, score(bc)
|
||||||
|
|
||||||
|
moves = [score_move(move) for move in board.legal_moves]
|
||||||
|
##print(board.turn, [m[1] for m in moves])
|
||||||
|
if board.turn != chess.WHITE:
|
||||||
|
best_move = max(moves, key=lambda m: m[1])
|
||||||
|
else:
|
||||||
|
best_move = min(moves, key=lambda m: m[1])
|
||||||
|
#print(best_move)
|
||||||
|
return best_move[0]
|
||||||
|
|
||||||
|
def analyze_board(self, board: chess.Board) -> int:
|
||||||
|
return score(board)
|
||||||
|
|
||||||
|
#print("WHITE_PAWN", WHITE_PAWN)
|
||||||
|
#print("BLACK_PAWN", BLACK_PAWN)
|
||||||
|
#print("WHITE_KNIGHT", WHITE_KNIGHT)
|
||||||
|
#print("BLACK_KNIGHT", BLACK_KNIGHT)
|
||||||
|
#print("WHITE_BISHOP", WHITE_BISHOP)
|
||||||
|
#print("BLACK_BISHOP", BLACK_BISHOP)
|
||||||
|
#print("WHITE_ROOK", WHITE_ROOK)
|
||||||
|
#print("BLACK_ROOK", BLACK_ROOK)
|
||||||
|
#print("WHITE_QUEEN", WHITE_QUEEN)
|
||||||
|
#print("BLACK_QUEEN", BLACK_QUEEN)
|
||||||
|
#print("WHITE_KING", WHITE_KING)
|
||||||
|
#print("BLACK_KING", BLACK_KING)
|
||||||
|
#print("EMPTY", EMPTY)
|
||||||
|
|
||||||
36
chesspp/random_stockfish_strategy.py
Normal file
36
chesspp/random_stockfish_strategy.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
import chess
|
||||||
|
import chess.engine
|
||||||
|
|
||||||
|
from chesspp.i_strategy import IStrategy
|
||||||
|
from chesspp.eval import score_stockfish
|
||||||
|
|
||||||
|
|
||||||
|
class RandomStockfishStrategy(IStrategy):
|
||||||
|
def __init__(self, rollout_depth: int, path="../stockfish/stockfish-windows-x86-64-avx2",
|
||||||
|
random_seed: random.Random = random.Random()) -> None:
|
||||||
|
super().__init__(rollout_depth)
|
||||||
|
self._stockfish = None
|
||||||
|
self.path = path
|
||||||
|
self.random_seed = random_seed
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self._stockfish is not None:
|
||||||
|
self._stockfish.quit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stockfish(self) -> chess.engine.SimpleEngine:
|
||||||
|
if self._stockfish is None:
|
||||||
|
self._stockfish = self.stockfish = chess.engine.SimpleEngine.popen_uci(self.path)
|
||||||
|
return self._stockfish
|
||||||
|
|
||||||
|
@stockfish.setter
|
||||||
|
def stockfish(self, stockfish):
|
||||||
|
self._stockfish = stockfish
|
||||||
|
|
||||||
|
def pick_next_move(self, board: chess.Board) -> chess.Move:
|
||||||
|
return self.random_seed.choice(list(board.legal_moves))
|
||||||
|
|
||||||
|
def analyze_board(self, board: chess.Board) -> int:
|
||||||
|
return score_stockfish(board, self.stockfish)
|
||||||
@@ -5,7 +5,8 @@ from chesspp.eval import score_manual
|
|||||||
|
|
||||||
|
|
||||||
class RandomStrategy(IStrategy):
|
class RandomStrategy(IStrategy):
|
||||||
def __init__(self, random_state: random.Random):
|
def __init__(self, random_state: random.Random, rollout_depth: int = 4):
|
||||||
|
super().__init__(rollout_depth)
|
||||||
self.random_state = random_state
|
self.random_state = random_state
|
||||||
|
|
||||||
def pick_next_move(self, board: chess.Board) -> chess.Move | None:
|
def pick_next_move(self, board: chess.Board) -> chess.Move | None:
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import chess.pgn
|
|||||||
from typing import Tuple, List
|
from typing import Tuple, List
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from chesspp.i_strategy import IStrategy
|
|
||||||
|
|
||||||
|
from chesspp.engine_factory import StrategyEnum, EngineFactory, EngineEnum
|
||||||
from chesspp.engine import Engine, Limit
|
from chesspp.engine import Engine, Limit
|
||||||
|
|
||||||
|
|
||||||
@@ -36,32 +36,43 @@ def simulate_game(white: Engine, black: Engine, limit: Limit, board: chess.Board
|
|||||||
|
|
||||||
|
|
||||||
class Evaluation:
|
class Evaluation:
|
||||||
def __init__(self, engine_a: Engine.__class__, strategy_a, engine_b: Engine.__class__, strategy_b, limit: Limit):
|
def __init__(self, engine_a: EngineEnum, strategy_a, engine_b: EngineEnum, strategy_b, limit: Limit,
|
||||||
|
stockfish_path: str, lc0_path: str):
|
||||||
self.engine_a = engine_a
|
self.engine_a = engine_a
|
||||||
self.strategy_a = strategy_a
|
self.strategy_a = strategy_a
|
||||||
self.engine_b = engine_b
|
self.engine_b = engine_b
|
||||||
self.strategy_b = strategy_b
|
self.strategy_b = strategy_b
|
||||||
|
self.stockfish_path = stockfish_path
|
||||||
|
self.lc0_path = lc0_path
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
|
|
||||||
def run(self, n_games=100, proc=mp.cpu_count()) -> List[EvaluationResult]:
|
def run(self, n_games=100, proc=mp.cpu_count()) -> List[EvaluationResult]:
|
||||||
proc = min(proc, mp.cpu_count())
|
proc = min(proc, mp.cpu_count())
|
||||||
|
arg = (self.engine_a, self.strategy_a, self.engine_b, self.strategy_b, self.limit, self.stockfish_path, self.lc0_path)
|
||||||
|
if proc > 1:
|
||||||
with mp.Pool(proc) as pool:
|
with mp.Pool(proc) as pool:
|
||||||
args = [(self.engine_a, self.strategy_a, self.engine_b, self.strategy_b, self.limit) for i in range(n_games)]
|
args = [arg for i in range(n_games)]
|
||||||
return pool.map(Evaluation._test_simulate, args)
|
return pool.map(Evaluation._test_simulate, args)
|
||||||
|
return [
|
||||||
|
Evaluation._test_simulate(arg)
|
||||||
|
for _ in range(n_games)
|
||||||
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _test_simulate(arg: Tuple[Engine.__class__, IStrategy, Engine.__class__, IStrategy, Limit]) -> EvaluationResult:
|
def _test_simulate(arg: Tuple[EngineEnum, StrategyEnum, EngineEnum, StrategyEnum, Limit, str, str]) -> EvaluationResult:
|
||||||
engine_a, strategy_a, engine_b, strategy_b, limit = arg
|
engine_a, strategy_a, engine_b, strategy_b, limit, stockfish_path, lc0_path = arg
|
||||||
flip_engines = bool(random.getrandbits(1))
|
flip_engines = bool(random.getrandbits(1))
|
||||||
|
|
||||||
board = chess.Board()
|
|
||||||
|
|
||||||
if flip_engines:
|
if flip_engines:
|
||||||
black, white = engine_a(board.copy(), chess.BLACK, strategy_a), engine_b(board.copy(), chess.WHITE, strategy_b)
|
black, white = EngineFactory.create_engine(engine_a, strategy_a, chess.BLACK,
|
||||||
|
stockfish_path, lc0_path), EngineFactory.create_engine(
|
||||||
|
engine_b, strategy_b, chess.WHITE, stockfish_path, lc0_path)
|
||||||
else:
|
else:
|
||||||
white, black = engine_a(board.copy(), chess.WHITE, strategy_a), engine_b(board.copy(), chess.BLACK, strategy_b)
|
white, black = EngineFactory.create_engine(engine_a, strategy_a, chess.WHITE,
|
||||||
|
stockfish_path, lc0_path), EngineFactory.create_engine(
|
||||||
|
engine_b, strategy_b, chess.BLACK, stockfish_path, lc0_path)
|
||||||
|
|
||||||
game = simulate_game(white, black, limit, board)
|
game = simulate_game(white, black, limit, chess.Board())
|
||||||
winner = game.end().board().outcome().winner
|
winner = game.end().board().outcome().winner
|
||||||
|
|
||||||
result = Winner.Draw
|
result = Winner.Draw
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import os
|
|
||||||
import chess
|
import chess
|
||||||
from chesspp.i_strategy import IStrategy
|
from chesspp.i_strategy import IStrategy
|
||||||
from chesspp.eval import score_stockfish
|
from chesspp.eval import score_stockfish
|
||||||
import chess.engine
|
import chess.engine
|
||||||
|
|
||||||
_DIR = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
|
|
||||||
|
|
||||||
class StockFishStrategy(IStrategy):
|
class StockFishStrategy(IStrategy):
|
||||||
|
|
||||||
def __init__(self, path="../stockfish/stockfish-windows-x86-64-avx2"):
|
def __init__(self, path="../stockfish/stockfish-windows-x86-64-avx2", rollout_depth: int = 4,
|
||||||
|
limit: chess.engine.Limit = chess.engine.Limit(depth=4)):
|
||||||
|
super().__init__(rollout_depth)
|
||||||
self._stockfish = None
|
self._stockfish = None
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.limit = limit
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if self._stockfish is not None:
|
if self._stockfish is not None:
|
||||||
@@ -20,8 +20,7 @@ class StockFishStrategy(IStrategy):
|
|||||||
@property
|
@property
|
||||||
def stockfish(self) -> chess.engine.SimpleEngine:
|
def stockfish(self) -> chess.engine.SimpleEngine:
|
||||||
if self._stockfish is None:
|
if self._stockfish is None:
|
||||||
self._stockfish = self.stockfish = chess.engine.SimpleEngine.popen_uci(
|
self._stockfish = self.stockfish = chess.engine.SimpleEngine.popen_uci(self.path)
|
||||||
os.path.join(_DIR, self.path))
|
|
||||||
return self._stockfish
|
return self._stockfish
|
||||||
|
|
||||||
@stockfish.setter
|
@stockfish.setter
|
||||||
@@ -29,7 +28,7 @@ class StockFishStrategy(IStrategy):
|
|||||||
self._stockfish = stockfish
|
self._stockfish = stockfish
|
||||||
|
|
||||||
def pick_next_move(self, board: chess.Board) -> chess.Move | None:
|
def pick_next_move(self, board: chess.Board) -> chess.Move | None:
|
||||||
return self.stockfish.play(board, chess.engine.Limit(depth=4)).move
|
return self.stockfish.play(board, self.limit).move
|
||||||
|
|
||||||
def analyze_board(self, board: chess.Board) -> int:
|
def analyze_board(self, board: chess.Board) -> int:
|
||||||
return score_stockfish(board, self.stockfish)
|
return score_stockfish(board, self.stockfish)
|
||||||
|
|||||||
@@ -1,26 +1,37 @@
|
|||||||
import math
|
import math
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import torch.distributions as dist
|
import torch.distributions as dist
|
||||||
from torch import exp
|
from torch import exp
|
||||||
|
|
||||||
F1: dict[float, float] = {}
|
total_count = 0
|
||||||
F2: dict[float, float] = {}
|
calculation_count = 0
|
||||||
CDF: dict[float, float] = {}
|
|
||||||
lookup_count = 0
|
|
||||||
|
|
||||||
|
|
||||||
def get_lookup_count():
|
def get_lookup_count():
|
||||||
global lookup_count
|
global total_count, calculation_count
|
||||||
return lookup_count
|
return total_count - calculation_count
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def calc_cdf(alpha: float) -> tuple[float, float, float]:
|
||||||
|
"""
|
||||||
|
Returns the calculated CDF and parameters f1,f2 from the input alpha
|
||||||
|
"""
|
||||||
|
global calculation_count
|
||||||
|
calculation_count += 1
|
||||||
|
|
||||||
|
normal = dist.Normal(0, 1)
|
||||||
|
cdf_alpha = normal.cdf(torch.tensor(alpha)).item()
|
||||||
|
pdf_alpha = exp(normal.log_prob(torch.tensor(alpha))).item()
|
||||||
|
f1 = alpha * cdf_alpha + pdf_alpha
|
||||||
|
f2 = alpha ** 2 * cdf_alpha * (1 - cdf_alpha) + (
|
||||||
|
1 - 2 * cdf_alpha) * alpha * pdf_alpha - pdf_alpha ** 2
|
||||||
|
return cdf_alpha, f1, f2
|
||||||
|
|
||||||
|
|
||||||
def max_gaussian(mu1, sigma1, mu2, sigma2) -> tuple[float, float]:
|
def max_gaussian(mu1, sigma1, mu2, sigma2) -> tuple[float, float]:
|
||||||
global lookup_count
|
|
||||||
global F1
|
|
||||||
global F2
|
|
||||||
global CDF
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Returns the combined max gaussian of two Gaussians represented by mu1, sigma1, mu2, simga2
|
Returns the combined max gaussian of two Gaussians represented by mu1, sigma1, mu2, simga2
|
||||||
:param mu1: mu of the first Gaussian
|
:param mu1: mu of the first Gaussian
|
||||||
@@ -29,39 +40,22 @@ def max_gaussian(mu1, sigma1, mu2, sigma2) -> tuple[float, float]:
|
|||||||
:param sigma2: sigma of the second Gaussian
|
:param sigma2: sigma of the second Gaussian
|
||||||
:return: mu and sigma maximized
|
:return: mu and sigma maximized
|
||||||
"""
|
"""
|
||||||
|
global total_count
|
||||||
|
total_count += 1
|
||||||
|
|
||||||
# we assume independence of the two gaussians
|
# we assume independence of the two gaussians
|
||||||
#print(mu1, sigma1, mu2, sigma2)
|
# print(mu1, sigma1, mu2, sigma2)
|
||||||
normal = dist.Normal(0, 1)
|
# normal = dist.Normal(0, 1)
|
||||||
sigma_m = math.sqrt(sigma1 ** 2 + sigma2 ** 2)
|
sigma_m = math.sqrt(sigma1 ** 2 + sigma2 ** 2)
|
||||||
alpha = (mu1 - mu2) / sigma_m
|
|
||||||
|
|
||||||
if alpha in CDF:
|
# round to two significant digits to enable float lookup
|
||||||
cdf_alpha = CDF[alpha]
|
alpha = round((mu1 - mu2) / sigma_m, 2)
|
||||||
lookup_count += 1
|
|
||||||
else:
|
|
||||||
cdf_alpha = normal.cdf(torch.tensor(alpha)).item()
|
|
||||||
CDF[alpha] = cdf_alpha
|
|
||||||
|
|
||||||
pdf_alpha = exp(normal.log_prob(torch.tensor(alpha))).item()
|
cdf_alpha, f1_alpha, f2_alpha = calc_cdf(alpha)
|
||||||
|
|
||||||
if alpha in F1:
|
|
||||||
f1_alpha = F1[alpha]
|
|
||||||
lookup_count += 1
|
|
||||||
else:
|
|
||||||
f1_alpha = alpha * cdf_alpha + pdf_alpha
|
|
||||||
F1[alpha] = f1_alpha
|
|
||||||
|
|
||||||
if alpha in F2:
|
|
||||||
f2_alpha = F2[alpha]
|
|
||||||
lookup_count += 1
|
|
||||||
else:
|
|
||||||
f2_alpha = alpha ** 2 * cdf_alpha * (1 - cdf_alpha) + (
|
|
||||||
1 - 2 * cdf_alpha) * alpha * pdf_alpha - pdf_alpha ** 2
|
|
||||||
F2[alpha] = f2_alpha
|
|
||||||
|
|
||||||
mu = mu2 + sigma_m * f1_alpha
|
mu = mu2 + sigma_m * f1_alpha
|
||||||
sigma = math.sqrt(sigma2 ** 2 + (sigma1 ** 2 - sigma2 ** 2) * cdf_alpha + sigma_m ** 2 * f2_alpha)
|
sigma = math.sqrt(sigma2 ** 2 + (sigma1 ** 2 - sigma2 ** 2) * cdf_alpha + sigma_m ** 2 * f2_alpha)
|
||||||
#sigma = math.sqrt((mu1**2 + sigma1**2) * cdf_alpha + (mu2**2 + sigma2**2) * (1 - cdf_alpha) + (mu1 + mu2) * sigma_m * pdf_alpha - mu**2)
|
# sigma = math.sqrt((mu1**2 + sigma1**2) * cdf_alpha + (mu2**2 + sigma2**2) * (1 - cdf_alpha) + (mu1 + mu2) * sigma_m * pdf_alpha - mu**2)
|
||||||
|
|
||||||
return mu, sigma
|
return mu, sigma
|
||||||
|
|
||||||
@@ -85,20 +79,20 @@ def min_gaussian(mu1, sigma1, mu2, sigma2) -> tuple[float, float]:
|
|||||||
pdf_alpha_neg = exp(normal.log_prob(torch.tensor(-alpha))).item()
|
pdf_alpha_neg = exp(normal.log_prob(torch.tensor(-alpha))).item()
|
||||||
|
|
||||||
mu = mu1 * (1 - cdf_alpha) + mu2 * cdf_alpha - pdf_alpha_neg * sigma_m
|
mu = mu1 * (1 - cdf_alpha) + mu2 * cdf_alpha - pdf_alpha_neg * sigma_m
|
||||||
sigma = math.sqrt((mu1**2 + sigma1**2) * (1 - cdf_alpha) + (mu2**2 + sigma2**2) * cdf_alpha - (mu1 + mu2) * sigma_m * pdf_alpha - mu**2)
|
sigma = math.sqrt((mu1 ** 2 + sigma1 ** 2) * (1 - cdf_alpha) + (mu2 ** 2 + sigma2 ** 2) * cdf_alpha - (
|
||||||
|
mu1 + mu2) * sigma_m * pdf_alpha - mu ** 2)
|
||||||
return mu, sigma
|
return mu, sigma
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print(mu1, sigma1, mu2, sigma2)
|
print(mu1, sigma1, mu2, sigma2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def beta_mean(alpha, beta):
|
def beta_mean(alpha, beta):
|
||||||
return alpha / (alpha + beta)
|
return alpha / (alpha + beta)
|
||||||
|
|
||||||
|
|
||||||
def beta_std(alpha, beta):
|
def beta_std(alpha, beta):
|
||||||
try:
|
try:
|
||||||
return math.sqrt((alpha * beta) / ((alpha * beta)**2 * (alpha + beta + 1)))
|
return math.sqrt((alpha * beta) / ((alpha * beta) ** 2 * (alpha + beta + 1)))
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
print(alpha, beta)
|
print(alpha, beta)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ from aiohttp import web
|
|||||||
|
|
||||||
import chess
|
import chess
|
||||||
from chesspp import engine
|
from chesspp import engine
|
||||||
|
from chesspp.engine_factory import EngineFactory
|
||||||
from chesspp.stockfish_strategy import StockFishStrategy
|
from chesspp.stockfish_strategy import StockFishStrategy
|
||||||
|
from chesspp.pesto_strategy import PestoStrategy
|
||||||
|
|
||||||
_DIR = os.path.abspath(os.path.dirname(__file__))
|
_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
_DATA_DIR = os.path.abspath(os.path.join(_DIR, "static_data"))
|
_DATA_DIR = os.path.abspath(os.path.join(_DIR, "static_data"))
|
||||||
@@ -23,12 +25,7 @@ def load_index() -> str:
|
|||||||
|
|
||||||
class Simulate:
|
class Simulate:
|
||||||
""" Run a simulation of two engines"""
|
""" Run a simulation of two engines"""
|
||||||
def __init__(self, engine_white=None, engine_black=None):
|
def __init__(self, engine_white, engine_black):
|
||||||
if engine_white is None:
|
|
||||||
engine_white = engine.ClassicMctsEngine(chess.WHITE)
|
|
||||||
if engine_black is None:
|
|
||||||
engine_black = engine.ClassicMctsEngine(chess.BLACK)
|
|
||||||
|
|
||||||
self.white = engine_white
|
self.white = engine_white
|
||||||
self.black = engine_black
|
self.black = engine_black
|
||||||
|
|
||||||
@@ -44,9 +41,13 @@ class Simulate:
|
|||||||
|
|
||||||
|
|
||||||
class WebInterface:
|
class WebInterface:
|
||||||
def __init__(self, white_engine: engine.Engine.__class__, black_engine: engine.Engine.__class__, limit: engine.Limit):
|
def __init__(self, white_engine, black_engine, strategy1, strategy2, stockfish_path, lc0_path, limit: engine.Limit):
|
||||||
self.white = white_engine
|
self.white = white_engine
|
||||||
self.black = black_engine
|
self.black = black_engine
|
||||||
|
self.strategy1 = strategy1
|
||||||
|
self.strategy2 = strategy2
|
||||||
|
self.stockfish_path = stockfish_path
|
||||||
|
self.lc0_path = lc0_path
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
|
|
||||||
|
|
||||||
@@ -73,8 +74,9 @@ class WebInterface:
|
|||||||
|
|
||||||
async def turns():
|
async def turns():
|
||||||
""" Simulates the game and sends the response to the client """
|
""" Simulates the game and sends the response to the client """
|
||||||
runner = Simulate(self.white(chess.Board(), chess.WHITE, StockFishStrategy()), self.black(
|
white = EngineFactory.create_engine(self.white, self.strategy1, chess.WHITE, self.stockfish_path, self.lc0_path)
|
||||||
chess.Board(), chess.BLACK, StockFishStrategy())).run(self.limit)
|
black = EngineFactory.create_engine(self.black, self.strategy2, chess.BLACK, self.stockfish_path, self.lc0_path)
|
||||||
|
runner = Simulate(white, black).run(self.limit)
|
||||||
def sim():
|
def sim():
|
||||||
return next(runner, None)
|
return next(runner, None)
|
||||||
|
|
||||||
@@ -100,8 +102,3 @@ class WebInterface:
|
|||||||
web.static('/img/chesspieces/wikipedia/', _DATA_DIR),
|
web.static('/img/chesspieces/wikipedia/', _DATA_DIR),
|
||||||
])
|
])
|
||||||
web.run_app(app)
|
web.run_app(app)
|
||||||
|
|
||||||
|
|
||||||
def run_sample():
|
|
||||||
limit = engine.Limit(time=1)
|
|
||||||
WebInterface(engine.BayesMctsEngine, engine.ClassicMctsEngine, limit).run_app()
|
|
||||||
|
|||||||
52
main.py
52
main.py
@@ -1,12 +1,16 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import chess
|
import chess
|
||||||
import chess.engine
|
import chess.engine
|
||||||
import chess.pgn
|
import chess.pgn
|
||||||
|
from chesspp.classic_mcts import ClassicMcts
|
||||||
|
from chesspp.baysian_mcts import BayesianMcts
|
||||||
|
from chesspp.engine_factory import EngineEnum, StrategyEnum
|
||||||
|
from chesspp.random_strategy import RandomStrategy
|
||||||
|
from chesspp.stockfish_strategy import StockFishStrategy
|
||||||
from chesspp import engine
|
from chesspp import engine
|
||||||
from chesspp import simulation, eval
|
from chesspp import simulation, eval
|
||||||
from chesspp import util
|
from chesspp import util
|
||||||
@@ -89,19 +93,10 @@ def analyze_results(moves: dict):
|
|||||||
|
|
||||||
|
|
||||||
def test_evaluation():
|
def test_evaluation():
|
||||||
a, b, s1, s2, n, limit, stockfish_path, proc = read_arguments()
|
a, b, s1, s2, n, limit, stockfish_path, lc0_path, proc = read_arguments()
|
||||||
limit = engine.Limit(time=limit)
|
limit = engine.Limit(time=limit)
|
||||||
if s1 == StockFishStrategy:
|
|
||||||
strat1 = StockFishStrategy(stockfish_path)
|
|
||||||
else:
|
|
||||||
strat1 = s1()
|
|
||||||
|
|
||||||
if s2 == StockFishStrategy:
|
evaluator = simulation.Evaluation(a, s1, b, s2, limit, stockfish_path, lc0_path)
|
||||||
strat2 = StockFishStrategy(stockfish_path)
|
|
||||||
else:
|
|
||||||
strat2 = s1()
|
|
||||||
|
|
||||||
evaluator = simulation.Evaluation(a, strat1, b, strat2, limit)
|
|
||||||
results = evaluator.run(n, proc)
|
results = evaluator.run(n, proc)
|
||||||
games_played = len(results)
|
games_played = len(results)
|
||||||
a_wins = len(list(filter(lambda x: x.winner == simulation.Winner.Engine_A, results)))
|
a_wins = len(list(filter(lambda x: x.winner == simulation.Winner.Engine_A, results)))
|
||||||
@@ -125,20 +120,25 @@ def read_arguments():
|
|||||||
description='Compare two engines by playing multiple games against each other'
|
description='Compare two engines by playing multiple games against each other'
|
||||||
)
|
)
|
||||||
|
|
||||||
engines = {"ClassicMCTS": engine.ClassicMctsEngine, "BayesianMCTS": engine.BayesMctsEngine,
|
engines = {"ClassicMCTS": EngineEnum.ClassicMcts, "BayesianMCTS": EngineEnum.BayesianMcts,
|
||||||
"Random": engine.RandomEngine}
|
"Random": EngineEnum.Random, "Stockfish": EngineEnum.Stockfish, "Lc0": EngineEnum.Lc0}
|
||||||
strategies = {"Random": RandomStrategy, "Stockfish": StockFishStrategy}
|
strategies = {"Random": StrategyEnum.Random, "Stockfish": StrategyEnum.Stockfish, "Lc0": StrategyEnum.Lc0,
|
||||||
|
"RandomStockfish": StrategyEnum.RandomStockfish, "PESTO": StrategyEnum.Pestos}
|
||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
stockfish_default = "../stockfish/stockfish-windows-x86-64-avx2"
|
stockfish_default = "stockfish/stockfish-windows-x86-64-avx2"
|
||||||
|
lc0_default = "lc0/lc0.exe"
|
||||||
else:
|
else:
|
||||||
stockfish_default = "../stockfish/stockfish-ubuntu-x86-64-avx2"
|
stockfish_default = "stockfish/stockfish-ubuntu-x86-64-avx2"
|
||||||
|
lc0_default = "lc0/lc0"
|
||||||
|
|
||||||
parser.add_argument("--proc", default=2, help="Number of processors to use for simulation, default=1")
|
parser.add_argument("--proc", default=2, help="Number of processors to use for simulation, default=1")
|
||||||
parser.add_argument("--time", default=0.5, help="Time limit for each simulation step, default=0.5")
|
parser.add_argument("--time", default=0.5, help="Time limit for each simulation step, default=0.5")
|
||||||
parser.add_argument("-n", default=100, help="Number of games to simulate, default=100")
|
parser.add_argument("-n", default=100, help="Number of games to simulate, default=100")
|
||||||
parser.add_argument("--stockfish", default=stockfish_default,
|
parser.add_argument("--stockfish_path", default=stockfish_default,
|
||||||
help=f"Path for stockfish executable, default='{stockfish_default}'")
|
help=f"Path for engine executable, default='{stockfish_default}'")
|
||||||
|
parser.add_argument("--lc0_path", default=lc0_default,
|
||||||
|
help=f"Path for engine executable, default='{stockfish_default}'")
|
||||||
parser.add_argument("--engine1", "--e1", help="Engine A for the simulation", choices=engines.keys(), required=True)
|
parser.add_argument("--engine1", "--e1", help="Engine A for the simulation", choices=engines.keys(), required=True)
|
||||||
parser.add_argument("--engine2", "--e2", help="Engine B for the simulation", choices=engines.keys(), required=True)
|
parser.add_argument("--engine2", "--e2", help="Engine B for the simulation", choices=engines.keys(), required=True)
|
||||||
parser.add_argument("--strategy1", "--s1", default=list(strategies.keys())[0],
|
parser.add_argument("--strategy1", "--s1", default=list(strategies.keys())[0],
|
||||||
@@ -146,7 +146,7 @@ def read_arguments():
|
|||||||
choices=strategies.keys())
|
choices=strategies.keys())
|
||||||
parser.add_argument("--strategy2", "--s2", default=list(strategies.keys())[0],
|
parser.add_argument("--strategy2", "--s2", default=list(strategies.keys())[0],
|
||||||
help="Strategy for engine B for the rollout",
|
help="Strategy for engine B for the rollout",
|
||||||
choices=strategies)
|
choices=strategies.keys())
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
engine1 = engines[args.engine1]
|
engine1 = engines[args.engine1]
|
||||||
@@ -154,7 +154,10 @@ def read_arguments():
|
|||||||
strategy1 = strategies[args.strategy1]
|
strategy1 = strategies[args.strategy1]
|
||||||
strategy2 = strategies[args.strategy2]
|
strategy2 = strategies[args.strategy2]
|
||||||
|
|
||||||
return engine1, engine2, strategy1, strategy2, int(args.n), float(args.time), args.stockfish, int(args.proc)
|
print(engine1, engine2, strategy1, strategy2, int(args.n), float(args.time), args.stockfish_path, args.lc0_path,
|
||||||
|
int(args.proc))
|
||||||
|
return engine1, engine2, strategy1, strategy2, int(args.n), float(
|
||||||
|
args.time), args.stockfish_path, args.lc0_path, int(args.proc)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -168,3 +171,8 @@ def main():
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
# Note: prevent endless wait on StockFish process
|
||||||
|
# by allowing for cleanup of objects (which closes stockfish)
|
||||||
|
import gc
|
||||||
|
gc.collect()
|
||||||
|
|||||||
6
web.py
6
web.py
@@ -1,7 +1,9 @@
|
|||||||
from chesspp import engine
|
from chesspp import engine
|
||||||
from chesspp import web
|
from chesspp import web
|
||||||
|
from main import read_arguments
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
limit = engine.Limit(time=0.5)
|
engine1, engine2, strategy1, strategy2, n_games, time, stockfish_path, lc0_path, n_proc = read_arguments()
|
||||||
web.WebInterface(engine.BayesMctsEngine, engine.ClassicMctsEngine, limit).run_app()
|
limit = engine.Limit(time=time)
|
||||||
|
web.WebInterface(engine1, engine2, strategy1, strategy2, stockfish_path, lc0_path, limit).run_app()
|
||||||
|
|||||||
Reference in New Issue
Block a user