diff --git a/engine.py b/engine.py index 3affae45..8c07b248 100644 --- a/engine.py +++ b/engine.py @@ -121,3 +121,18 @@ async def close(self) -> None: print("Engine could not be terminated cleanly.") self.transport.close() + + async def send_gameover(self, result: str) -> None: + """Send a custom 'gameover ' UCI extension command to the engine. + + result should be one of: '1-0', '0-1', '1/2-1/2' + This is a non-standard extension — engines that don't handle it will + simply ignore the unknown command. + """ + try: + # python-chess exposes the asyncio SubprocessTransport on the protocol. + # Pipe 0 is stdin of the engine process. + stdin_transport = self.transport.get_pipe_transport(0) + stdin_transport.write(f"gameover {result}\n".encode()) + except Exception: + pass # Never crash BotLi because of a custom engine extension diff --git a/game.py b/game.py index 9c065847..da23d921 100755 --- a/game.py +++ b/game.py @@ -34,6 +34,8 @@ async def run(self) -> None: if info.state["status"] != "started": self._print_result_message(info.state, lichess_game, info) await chatter.send_goodbyes() + if uci_result := self._get_uci_result(info.state): + await lichess_game.engine.send_gameover(uci_result) await lichess_game.close() return @@ -85,6 +87,8 @@ async def run(self) -> None: self._print_result_message(event, lichess_game, info) await chatter.send_goodbyes() + if uci_result := self._get_uci_result(event): + await lichess_game.engine.send_gameover(uci_result) break if has_updated: @@ -186,3 +190,18 @@ def _print_result_message( message = " • ".join([info.id_str, opponents_str, message]) print(f"{message}\n{123 * '‾'}") + + @staticmethod + def _get_uci_result(game_state: dict[str, Any]) -> str | None: + """Convert a Lichess game_state dict to a UCI result string. + Returns None for aborted games (no meaningful result to report). + """ + winner = game_state.get("winner") + status = game_state.get("status", "") + if status in {"aborted", "noStart", "unknownFinish"}: + return None + if winner == "white": + return "1-0" + if winner == "black": + return "0-1" + return "1/2-1/2"