@@ -1246,6 +1246,27 @@ async def analysis(self, board: chess.Board, limit: Optional[Limit] = None, *, m
12461246 and stopping the analysis at any time.
12471247 """
12481248
1249+ @abc .abstractmethod
1250+ async def send_game_result (self , board : chess .Board , winner : Optional [Color ] = None , game_ending : Optional [str ] = None , game_complete : bool = True ) -> None :
1251+ """
1252+ Sends the engine the result of the game.
1253+
1254+ UCI engines only recieve the final position of the board. XBoard engines
1255+ receive the final moves and a line of the form "result <winner> {<ending>}",
1256+ where <winner> is one of "1-0", "0-1", "1/2-1/2", or "*" to indicate white
1257+ won, black won, draw, or adjournment, respectively.
1258+
1259+ :param board: The final state of the board.
1260+ :param winner: Optional. Specify the winner of the game. This is useful
1261+ if the result of the game is not evident from the board--e.g., time
1262+ forfeiture or draw by agreement.
1263+ :param game_ending: Optional. Text describing the reason for the game
1264+ ending. Similarly to the winner paramter, this overrides any game
1265+ result derivable from the board.
1266+ :param game_complete: Optional. Whether the game is complete or may resume
1267+ at a later time.
1268+ """
1269+
12491270 @abc .abstractmethod
12501271 async def quit (self ) -> None :
12511272 """Asks the engine to shut down."""
@@ -1797,6 +1818,15 @@ def engine_terminated(self, engine: UciProtocol, exc: Exception) -> None:
17971818
17981819 return await self .communicate (UciAnalysisCommand )
17991820
1821+ async def send_game_result (self , board : chess .Board , winner : Optional [Color ] = None , game_ending : Optional [str ] = None , game_complete : bool = True ) -> None :
1822+ class UciGameResultCommand (BaseCommand [UciProtocol , None ]):
1823+ def start (self , engine : UciProtocol ) -> None :
1824+ engine ._position (board ) # Send final board position
1825+ self .result .set_result (None )
1826+ self .set_finished ()
1827+
1828+ return await self .communicate (UciGameResultCommand )
1829+
18001830 async def quit (self ) -> None :
18011831 self .send_line ("quit" )
18021832 await asyncio .shield (self .returncode )
@@ -2477,6 +2507,38 @@ def _opponent_configuration(self, *, opponent: Optional[Opponent] = None, engine
24772507 async def send_opponent_information (self , * , opponent : Optional [Opponent ] = None , engine_rating : Optional [int ] = None ) -> None :
24782508 return await self .configure (self ._opponent_configuration (opponent = opponent , engine_rating = engine_rating ))
24792509
2510+ async def send_game_result (self , board : chess .Board , winner : Optional [Color ] = None , game_ending : Optional [str ] = None , game_complete : bool = True ) -> None :
2511+ class XBoardGameResultCommand (BaseCommand [XBoardProtocol , None ]):
2512+ def start (self , engine : XBoardProtocol ) -> None :
2513+ engine ._new (board , engine .game , {}) # Send final moves to engine.
2514+
2515+ outcome = board .outcome (claim_draw = True )
2516+
2517+ if not game_complete :
2518+ result = "*"
2519+ ending = game_ending or "Game adjourned"
2520+ elif winner is not None or game_ending :
2521+ result = "1-0" if winner == chess .WHITE else ("0-1" if winner == chess .BLACK else "1/2-1/2" )
2522+ ending = game_ending or ""
2523+ elif outcome is not None and outcome .winner is not None :
2524+ result = outcome .result ()
2525+ winning_color = "White" if outcome .winner == chess .WHITE else "Black"
2526+ is_checkmate = outcome .termination == chess .Termination .CHECKMATE
2527+ ending = f"{ winning_color } { 'mates' if is_checkmate else 'variant win' } "
2528+ elif outcome is not None :
2529+ result = outcome .result ()
2530+ ending = outcome .termination .name .capitalize ().replace ("_" , " " )
2531+ else :
2532+ result = "*"
2533+ ending = ""
2534+
2535+ ending_text = f"{{{ ending } }}" if ending else ""
2536+ engine .send_line (f"result { result } { ending_text } " .strip ())
2537+ self .result .set_result (None )
2538+ self .set_finished ()
2539+
2540+ return await self .communicate (XBoardGameResultCommand )
2541+
24802542 async def quit (self ) -> None :
24812543 self .send_line ("quit" )
24822544 await asyncio .shield (self .returncode )
@@ -2904,6 +2966,12 @@ def analysis(self, board: chess.Board, limit: Optional[Limit] = None, *, multipv
29042966 future = asyncio .run_coroutine_threadsafe (coro , self .protocol .loop )
29052967 return SimpleAnalysisResult (self , future .result ())
29062968
2969+ def send_game_result (self , board : chess .Board , winner : Optional [Color ] = None , game_ending : Optional [str ] = None , game_complete : bool = True ) -> None :
2970+ with self ._not_shut_down ():
2971+ coro = asyncio .wait_for (self .protocol .send_game_result (board , winner , game_ending , game_complete ), self .timeout )
2972+ future = asyncio .run_coroutine_threadsafe (coro , self .protocol .loop )
2973+ return future .result ()
2974+
29072975 def quit (self ) -> None :
29082976 with self ._not_shut_down ():
29092977 coro = asyncio .wait_for (self .protocol .quit (), self .timeout )
0 commit comments