diff --git a/lib/strategies.py b/lib/strategies.py index e2d889edf..5ad9721cb 100644 --- a/lib/strategies.py +++ b/lib/strategies.py @@ -1,95 +1,95 @@ -""" -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) +""" +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)