Skip to content

Commit 714f672

Browse files
authored
Merge pull request #1003 from MarkZH/xboard-clock-update
Add optional identifier to Limit instances
2 parents 58a54f1 + 40e633b commit 714f672

2 files changed

Lines changed: 38 additions & 8 deletions

File tree

chess/engine.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,14 @@ class Limit:
332332
*white_clock* and *black_clock* are, then it is sudden death.
333333
"""
334334

335+
clock_id: object = None
336+
"""
337+
An identifier to use with XBoard engines to signal that the time
338+
control has changed. When this field changes, Xboard engines are
339+
sent level or st commands as appropriate. Otherwise, only time
340+
and otim commands are sent to update the engine's clock.
341+
"""
342+
335343
def __repr__(self) -> str:
336344
# Like default __repr__, but without None values.
337345
return "{}({})".format(
@@ -2013,6 +2021,7 @@ def __init__(self) -> None:
20132021
self.target_config: Dict[str, ConfigValue] = {}
20142022
self.board = chess.Board()
20152023
self.game: object = None
2024+
self.clock_id: object = None
20162025
self.first_game = True
20172026

20182027
async def initialize(self) -> None:
@@ -2214,10 +2223,9 @@ def start(self, engine: XBoardProtocol) -> None:
22142223
# Limit or time control.
22152224
clock = limit.white_clock if board.turn else limit.black_clock
22162225
increment = limit.white_inc if board.turn else limit.black_inc
2217-
if limit.remaining_moves or clock is not None or increment is not None:
2218-
base_mins, base_secs = divmod(int(clock or 0), 60)
2219-
engine.send_line(f"level {limit.remaining_moves or 0} {base_mins}:{base_secs:02d} {increment or 0}")
2220-
2226+
if limit.clock_id is None or limit.clock_id != engine.clock_id:
2227+
self._send_time_control(engine, clock, increment)
2228+
engine.clock_id = limit.clock_id
22212229
if limit.nodes is not None:
22222230
if limit.time is not None or limit.white_clock is not None or limit.black_clock is not None or increment is not None:
22232231
raise EngineError("xboard does not support mixing node limits with time limits")
@@ -2229,8 +2237,6 @@ def start(self, engine: XBoardProtocol) -> None:
22292237

22302238
engine.send_line("nps 1")
22312239
engine.send_line(f"st {max(1, int(limit.nodes))}")
2232-
if limit.time is not None:
2233-
engine.send_line(f"st {max(0.01, limit.time)}")
22342240
if limit.depth is not None:
22352241
engine.send_line(f"sd {max(1, int(limit.depth))}")
22362242
if limit.white_clock is not None:
@@ -2282,6 +2288,13 @@ def line_received(self, engine: XBoardProtocol, line: str) -> None:
22822288
else:
22832289
LOGGER.warning("%s: Unexpected engine output: %r", engine, line)
22842290

2291+
def _send_time_control(self, engine: XBoardProtocol, clock: Optional[float], increment: Optional[float]) -> None:
2292+
if limit.remaining_moves or clock is not None or increment is not None:
2293+
base_mins, base_secs = divmod(int(clock or 0), 60)
2294+
engine.send_line(f"level {limit.remaining_moves or 0} {base_mins}:{base_secs:02d} {increment or 0}")
2295+
if limit.time is not None:
2296+
engine.send_line(f"st {max(0.01, limit.time)}")
2297+
22852298
def _post(self, engine: XBoardProtocol, line: str) -> None:
22862299
if not self.result.done():
22872300
self.play_result.info = _parse_xboard_post(line, engine.board, info)

test.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3714,7 +3714,9 @@ async def main():
37143714
mock.assert_done()
37153715

37163716
limit = chess.engine.Limit(black_clock=65, white_clock=100,
3717-
black_inc=4, white_inc=8)
3717+
black_inc=4, white_inc=8,
3718+
clock_id="xboard level")
3719+
board = chess.Board()
37183720
mock.expect("new")
37193721
mock.expect("force")
37203722
mock.expect("level 0 1:40 8")
@@ -3724,10 +3726,25 @@ async def main():
37243726
mock.expect("easy")
37253727
mock.expect("go", ["move e2e4"])
37263728
mock.expect_ping()
3727-
result = await protocol.play(chess.Board(), limit)
3729+
result = await protocol.play(board, limit)
37283730
self.assertEqual(result.move, chess.Move.from_uci("e2e4"))
37293731
mock.assert_done()
37303732

3733+
board.push(result.move)
3734+
board.push_uci("e7e5")
3735+
3736+
mock.expect("force")
3737+
mock.expect("e7e5")
3738+
mock.expect("time 10000")
3739+
mock.expect("otim 6500")
3740+
mock.expect("nopost")
3741+
mock.expect("easy")
3742+
mock.expect("go", ["move d2d4"])
3743+
mock.expect_ping()
3744+
result = await protocol.play(board, limit)
3745+
self.assertEqual(result.move, chess.Move.from_uci("d2d4"))
3746+
mock.assert_done()
3747+
37313748
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
37323749
asyncio.run(main())
37333750

0 commit comments

Comments
 (0)