Skip to content

Commit 3f1d7d4

Browse files
committed
document analysis
1 parent ab70690 commit 3f1d7d4

2 files changed

Lines changed: 131 additions & 39 deletions

File tree

chess/engine.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,25 @@ async def analyse(self, board, limit, *, multipv=None, game=None, searchmoves=No
575575
async def analysis(self, board, limit=None, *, multipv=None, game=None, searchmoves=None, options={}):
576576
"""
577577
Start analysing a position.
578+
579+
:param board: The position to analyse. The entire move stack will be
580+
sent to the engine.
581+
:param limit: Optional. An instance of :class:`~chess.engine.Limit`
582+
that determines when to stop the analysis. Analysis is infinite
583+
by default.
584+
:param multipv: Optional. Analyse multiple root moves.
585+
:param game: Optional. An arbitrary object that identifies the game.
586+
Will automatically clear hashtables if the object is not equal
587+
to the previous game.
588+
:param searchmoves: Optional. Limit analysis to a list of root moves.
589+
:param options: Optional. A dictionary of engine options for the
590+
analysis. The previous configuration will be restored after the
591+
analysis is complete. You can permanently apply a configuration
592+
with :func:`~chess.engine.EngineProtocol.configure()`.
593+
594+
Returns :class:`~chess.engine.AnalysisResult`, a handle that allows
595+
asynchronously iterating over the information sent by the engine
596+
and stopping the the analysis at any time.
578597
"""
579598
raise NotImplementedError
580599

docs/engine.rst

Lines changed: 112 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ with both kinds of engines.
1515

1616
The XBoard implementation is currently only a skeleton.
1717

18-
The preferred way to use the API is in an ``asyncio`` event loop.
18+
The preferred way to use the API is with an
19+
`asyncio <https://docs.python.org/3/library/asyncio.html>`_ event loop.
1920
The examples also show a simple synchronous wrapper
2021
:class:`~chess.engine.SimpleEngine` that automatically spawns an event loop
2122
in the background.
@@ -25,20 +26,24 @@ Playing
2526

2627
Example: Let Stockfish play against itself, 100 milliseconds per move.
2728

28-
>>> import chess.engine
29-
>>>
30-
>>> engine = chess.engine.SimpleEngine.popen_uci("stockfish")
31-
>>>
32-
>>> board = chess.Board()
33-
>>> while not board.is_game_over():
34-
... result = engine.play(board, chess.engine.Limit(movetime=100))
35-
... board.push(result.move)
36-
...
37-
>>> engine.quit()
29+
.. code:: python
30+
31+
import chess
32+
import chess.engine
33+
34+
engine = chess.engine.SimpleEngine.popen_uci("stockfish")
35+
36+
board = chess.Board()
37+
while not board.is_game_over():
38+
result = engine.play(board, chess.engine.Limit(movetime=100))
39+
board.push(result.move)
40+
41+
engine.quit()
3842
3943
.. code:: python
4044
4145
import asyncio
46+
import chess
4247
import chess.engine
4348
4449
async def main():
@@ -80,25 +85,29 @@ Analysing and evaluating a position
8085

8186
Example:
8287

83-
>>> import chess.engine
84-
>>>
85-
>>> engine = chess.engine.SimpleEngine.popen_uci("stockfish")
86-
>>>
87-
>>> board = chess.Board()
88-
>>> info = engine.analyse(board, chess.engine.Limit(movetime=100))
89-
>>> info["score"]
90-
Cp(20)
91-
>>>
92-
>>> board = chess.Board("r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4")
93-
>>> info = engine.analyse(board, chess.engine.Limit(depth=20))
94-
>>> info["score"]
95-
Mate.plus(1)
96-
>>>
97-
>>> engine.quit()
88+
.. code:: python
89+
90+
import chess
91+
import chess.engine
92+
93+
engine = chess.engine.SimpleEngine.popen_uci("stockfish")
94+
95+
board = chess.Board()
96+
info = engine.analyse(board, chess.engine.Limit(movetime=100))
97+
print("Score:", info["score"])
98+
# Score: +20
99+
100+
board = chess.Board("r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4")
101+
info = engine.analyse(board, chess.engine.Limit(depth=20))
102+
print("Score:", info["score"])
103+
# Score: #1
104+
105+
engine.quit()
98106
99107
.. code:: python
100108
101109
import asyncio
110+
import chess
102111
import chess.engine
103112
104113
async def main():
@@ -107,10 +116,12 @@ Mate.plus(1)
107116
board = chess.Board()
108117
info = await engine.analyse(board, chess.engine.Limit(movetime=100))
109118
print(info["score"])
119+
# Score: +20
110120
111121
board = chess.Board("r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4")
112122
info = await engine.analyse(board, chess.engine.Limit(depth=20))
113123
print(info["score"])
124+
# Score: #1
114125
115126
await engine.quit()
116127
@@ -123,6 +134,67 @@ Mate.plus(1)
123134
.. autoclass:: chess.engine.Score
124135
:members:
125136

137+
Indefinite or infinite analysis
138+
-------------------------------
139+
140+
Example: Stream information from the engine and stop on an arbitrary condition.
141+
142+
.. code:: python
143+
144+
import chess
145+
import chess.engine
146+
147+
engine = chess.engine.SimpleEngine.popen_uci("stockfish")
148+
149+
with engine.analysis(chess.Board()) as analysis:
150+
for info in analysis:
151+
print(info.get("score"), info.get("pv"))
152+
153+
# Unusual stop condition.
154+
if info.get("hashfull", 0) > 900:
155+
break
156+
157+
engine.quit()
158+
159+
.. code:: python
160+
161+
import asyncio
162+
import chess
163+
import chess.engine
164+
165+
async def main():
166+
transport, engine = chess.engine.popen_uci("stockfish")
167+
168+
analysis = await engine.analysis(chess.Board())
169+
with analysis:
170+
async for info in analysis:
171+
print(info.get("score"), info.get("pv"))
172+
173+
# Unusual stop condition.
174+
if info.get("hashfull", 0) > 900:
175+
break
176+
177+
await engine.quit()
178+
179+
chess.engine.setup_event_loop()
180+
asyncio.run(main())
181+
182+
.. autoclass:: chess.engine.EngineProtocol
183+
:members: analysis
184+
185+
.. autoclass:: chess.engine.AnalysisResult
186+
:members:
187+
188+
.. py:attribute:: info
189+
190+
A dictionary of aggregated information sent by the engine. This is
191+
actually an alias for ``multipv[0]``.
192+
193+
.. py:attribute:: multipv
194+
195+
A list of dictionaries with aggregated information sent by the engine.
196+
One item for each root move.
197+
126198
Options
127199
-------
128200

@@ -152,6 +224,7 @@ Option(name='Hash', type='spin', default=16, min=1, max=131072, var=[])
152224
153225
# Check available options.
154226
print(engine.options["Hash"])
227+
# Option(name='Hash', type='spin', default=16, min=1, max=131072, var=[])
155228
156229
# Set an option.
157230
await engine.configure({"Hash": 32})
@@ -211,6 +284,19 @@ Option(name='Hash', type='spin', default=16, min=1, max=131072, var=[])
211284
212285
A list of allowed string values for a *combo* option.
213286

287+
Logging
288+
-------
289+
290+
Communication is logged with debug level on a logger named ``chess.engine``.
291+
Debug logs are useful while troubleshooting or in bug reports.
292+
293+
.. code:: python
294+
295+
import logging
296+
297+
# Enable debug logging.
298+
logging.basicConfig(level=logging.DEBUG)
299+
214300
Reference
215301
---------
216302

@@ -229,19 +315,6 @@ Reference
229315

230316
.. autoclass:: chess.engine.XBoardProtocol
231317

232-
.. autoclass:: chess.engine.AnalysisResult
233-
:members:
234-
235-
.. py:attribute:: info
236-
237-
A dictionary of aggregated information sent by the engine. This is
238-
actually an alias for ``multipv[0]``.
239-
240-
.. py:attribute:: multipv
241-
242-
A list of dictionaries with aggregated information sent by the engine.
243-
One item for each root move.
244-
245318
.. autoclass:: chess.engine.SimpleEngine
246319
:members:
247320

0 commit comments

Comments
 (0)