VeloCT_Base / test_bot /test_bot.py
Taperx's picture
Deploy Base model with clean official lichess-bot folder
3014f14
Raw
History Blame Contribute Delete
7.83 kB
"""Test lichess-bot."""
import yaml
import chess
import chess.engine
import threading
import os
import sys
import datetime
import time
import logging
import tempfile
from multiprocessing import Manager
from queue import Queue
import test_bot.lichess
from lib import config
from lib.timer import Timer, seconds
from lib.engine_wrapper import test_suffix
from lib.lichess_types import CONFIG_DICT_TYPE
if "pytest" not in sys.modules:
sys.exit(f"The script {os.path.basename(__file__)} should only be run by pytest.")
from lib import lichess_bot
from test_bot.test_games import scholars_mate
logging_level = logging.DEBUG
testing_log_file_name = None
lichess_bot.logging_configurer(logging_level, testing_log_file_name, True)
logger = logging.getLogger(__name__)
def lichess_org_simulator(move_queue: Queue[chess.Move | None],
board_queue: Queue[chess.Board],
clock_queue: Queue[tuple[datetime.timedelta, datetime.timedelta, datetime.timedelta]],
results: Queue[bool]) -> None:
"""
Run a mocked version of the lichess.org server to provide an opponent for a test. This opponent always plays white.
:param opponent_path: The path to the executable of the opponent. Usually Stockfish.
:param move_queue: An interprocess queue that supplies the moves chosen by the bot being tested.
:param board_queue: An interprocess queue where this function sends the updated board after choosing a move.
:param clock_queue: An interprocess queue where this function sends the updated game clock after choosing a move.
:param results: An interprocess queue where this function sends the result of the game to the testing function.
"""
start_time = seconds(10)
increment = seconds(0.1)
board = chess.Board()
wtime = start_time
btime = start_time
while not board.is_game_over():
move_timer = Timer()
if board.turn == chess.WHITE:
move_count = len(board.move_stack)
engine_move = board.parse_uci(scholars_mate[move_count])
board.push(engine_move)
board_queue.put(board)
clock_queue.put((wtime, btime, increment))
if len(board.move_stack) > 1:
wtime -= move_timer.time_since_reset()
wtime += increment
else:
move_timer = Timer()
while (bot_move := move_queue.get()) is None:
board_queue.put(board)
clock_queue.put((wtime, btime, increment))
move_queue.task_done()
board.push(bot_move)
move_queue.task_done()
if len(board.move_stack) > 2:
btime -= move_timer.time_since_reset()
btime += increment
board_queue.put(board)
clock_queue.put((wtime, btime, increment))
outcome = board.outcome()
results.put(outcome is not None and outcome.winner == chess.BLACK)
def run_bot(raw_config: CONFIG_DICT_TYPE, logging_level: int) -> bool:
"""
Start lichess-bot test with a mocked version of the lichess.org site.
:param raw_config: A dictionary of values to specify the engine to test. This engine will play as white.
:param logging_level: The level of logging to use during the test. Usually logging.DEBUG.
:param opponent_path: The path to the executable that will play the opponent. The opponent plays as black.
"""
config.insert_default_values(raw_config)
CONFIG = config.Configuration(raw_config)
logger.info(lichess_bot.intro())
manager = Manager()
board_queue: Queue[chess.Board] = manager.Queue()
clock_queue: Queue[tuple[datetime.timedelta, datetime.timedelta, datetime.timedelta]] = manager.Queue()
move_queue: Queue[chess.Move | None] = manager.Queue()
li = test_bot.lichess.Lichess(move_queue, board_queue, clock_queue)
user_profile = li.get_profile()
username = user_profile["username"]
if user_profile.get("title") != "BOT":
return False
logger.info(f"Welcome {username}!")
lichess_bot.disable_restart()
results: Queue[bool] = manager.Queue()
thr = threading.Thread(target=lichess_org_simulator, args=[move_queue, board_queue, clock_queue, results])
thr.start()
lichess_bot.start(li, user_profile, CONFIG, logging_level, testing_log_file_name, True, one_game=True)
result = results.get()
results.task_done()
results.join()
board_queue.join()
clock_queue.join()
move_queue.join()
thr.join()
return result
def test_uci() -> None:
"""Test lichess-bot with Stockfish (UCI)."""
with open("./config.yml.default") as file:
CONFIG = yaml.safe_load(file)
with tempfile.TemporaryDirectory() as temp:
CONFIG["token"] = ""
CONFIG["engine"]["dir"] = "test_bot"
CONFIG["engine"]["name"] = "uci_engine.py"
CONFIG["engine"]["interpreter"] = sys.executable
CONFIG["pgn_directory"] = os.path.join(temp, "uci_game_record")
CONFIG["engine"]["uci_options"] = {}
win = run_bot(CONFIG, logging_level)
logger.info("Finished Testing UCI")
assert win
time.sleep(0.1) # Wait for file to be written.
assert os.path.isfile(os.path.join(CONFIG["pgn_directory"],
"bo vs b - zzzzzzzz.pgn"))
def test_xboard() -> None:
"""Test lichess-bot with an XBoard engine."""
with open("./config.yml.default") as file:
CONFIG = yaml.safe_load(file)
with tempfile.TemporaryDirectory() as temp:
CONFIG["token"] = ""
CONFIG["engine"]["dir"] = "test_bot"
CONFIG["engine"]["name"] = "xboard_engine.py"
CONFIG["engine"]["protocol"] = "xboard"
CONFIG["engine"]["interpreter"] = sys.executable
CONFIG["pgn_directory"] = os.path.join(temp, "lc0_game_record")
win = run_bot(CONFIG, logging_level)
logger.info("Finished Testing XBoard")
assert win
time.sleep(0.1) # Wait for file to be written.
assert os.path.isfile(os.path.join(CONFIG["pgn_directory"],
"bo vs b - zzzzzzzz.pgn"))
def test_homemade() -> None:
"""Test lichess-bot with a homemade engine."""
with open("./config.yml.default") as file:
CONFIG = yaml.safe_load(file)
with tempfile.TemporaryDirectory() as temp:
CONFIG["token"] = ""
CONFIG["engine"]["name"] = f"ScholarsMate{test_suffix}"
CONFIG["engine"]["protocol"] = "homemade"
CONFIG["pgn_directory"] = os.path.join(temp, "homemade_game_record")
win = run_bot(CONFIG, logging_level)
logger.info("Finished Testing Homemade")
assert win
time.sleep(0.1) # Wait for file to be written.
assert os.path.isfile(os.path.join(CONFIG["pgn_directory"],
"bo vs b - zzzzzzzz.pgn"))
def test_buggy_engine() -> None:
"""Test lichess-bot with an engine that causes a timeout error within python-chess."""
with open("./config.yml.default") as file:
CONFIG = yaml.safe_load(file)
with tempfile.TemporaryDirectory() as temp:
CONFIG["token"] = ""
CONFIG["engine"]["dir"] = "test_bot"
CONFIG["engine"]["name"] = "buggy_engine.py"
CONFIG["engine"]["interpreter"] = sys.executable
CONFIG["engine"]["uci_options"] = {"go_commands": {"movetime": 100}}
CONFIG["pgn_directory"] = os.path.join(temp, "bug_game_record")
win = run_bot(CONFIG, logging_level)
logger.info("Finished Testing Buggy Engine")
assert win
time.sleep(0.1) # Wait for file to be written.
assert os.path.isfile(os.path.join(CONFIG["pgn_directory"],
"bo vs b - zzzzzzzz.pgn"))