96 lines
3.5 KiB
Python
96 lines
3.5 KiB
Python
"""
|
|
Some example strategies for people who want to create a custom, homemade bot.
|
|
|
|
With these classes, bot makers will not have to implement the UCI or XBoard interfaces themselves.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
import chess
|
|
from chess.engine import PlayResult, Limit
|
|
import random
|
|
from lib.engine_wrapper import MinimalEngine, MOVE
|
|
from typing import Any
|
|
import logging
|
|
|
|
|
|
# Use this logger variable to print messages to the console or log files.
|
|
# logger.info("message") will always print "message" to the console or log file.
|
|
# logger.debug("message") will only print "message" if verbose logging is enabled.
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ExampleEngine(MinimalEngine):
|
|
"""An example engine that all homemade engines inherit."""
|
|
|
|
pass
|
|
|
|
|
|
# Strategy names and ideas from tom7's excellent eloWorld video
|
|
|
|
class RandomMove(ExampleEngine):
|
|
"""Get a random move."""
|
|
|
|
def search(self, board: chess.Board, *args: Any) -> PlayResult:
|
|
"""Choose a random move."""
|
|
return PlayResult(random.choice(list(board.legal_moves)), None)
|
|
|
|
|
|
class Alphabetical(ExampleEngine):
|
|
"""Get the first move when sorted by san representation."""
|
|
|
|
def search(self, board: chess.Board, *args: Any) -> PlayResult:
|
|
"""Choose the first move alphabetically."""
|
|
moves = list(board.legal_moves)
|
|
moves.sort(key=board.san)
|
|
return PlayResult(moves[0], None)
|
|
|
|
|
|
class FirstMove(ExampleEngine):
|
|
"""Get the first move when sorted by uci representation."""
|
|
|
|
def search(self, board: chess.Board, *args: Any) -> PlayResult:
|
|
"""Choose the first move alphabetically in uci representation."""
|
|
moves = list(board.legal_moves)
|
|
moves.sort(key=str)
|
|
return PlayResult(moves[0], None)
|
|
|
|
|
|
class ComboEngine(ExampleEngine):
|
|
"""
|
|
Get a move using multiple different methods.
|
|
|
|
This engine demonstrates how one can use `time_limit`, `draw_offered`, and `root_moves`.
|
|
"""
|
|
|
|
def search(self, board: chess.Board, time_limit: Limit, ponder: bool, draw_offered: bool, root_moves: MOVE) -> PlayResult:
|
|
"""
|
|
Choose a move using multiple different methods.
|
|
|
|
:param board: The current position.
|
|
:param time_limit: Conditions for how long the engine can search (e.g. we have 10 seconds and search up to depth 10).
|
|
:param ponder: Whether the engine can ponder after playing a move.
|
|
:param draw_offered: Whether the bot was offered a draw.
|
|
:param root_moves: If it is a list, the engine should only play a move that is in `root_moves`.
|
|
:return: The move to play.
|
|
"""
|
|
if isinstance(time_limit.time, int):
|
|
my_time = time_limit.time
|
|
my_inc = 0
|
|
elif board.turn == chess.WHITE:
|
|
my_time = time_limit.white_clock if isinstance(time_limit.white_clock, int) else 0
|
|
my_inc = time_limit.white_inc if isinstance(time_limit.white_inc, int) else 0
|
|
else:
|
|
my_time = time_limit.black_clock if isinstance(time_limit.black_clock, int) else 0
|
|
my_inc = time_limit.black_inc if isinstance(time_limit.black_inc, int) else 0
|
|
|
|
possible_moves = root_moves if isinstance(root_moves, list) else list(board.legal_moves)
|
|
|
|
if my_time / 60 + my_inc > 10:
|
|
# Choose a random move.
|
|
move = random.choice(possible_moves)
|
|
else:
|
|
# Choose the first move alphabetically in uci representation.
|
|
possible_moves.sort(key=str)
|
|
move = possible_moves[0]
|
|
return PlayResult(move, None, draw_offered=draw_offered)
|