python-chess
python-chess
Release 0.28.1
1 Introduction 3
2 Documentation 5
3 Features 7
4 Installing 13
6 Acknowledgements 17
7 License 19
8 Contents 21
8.1 Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
8.2 PGN parsing and writing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
8.3 Polyglot opening book reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
8.4 Gaviota endgame tablebase probing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
8.5 Syzygy endgame tablebase probing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
8.6 UCI/XBoard engine communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
8.7 SVG rendering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
8.8 Variants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
8.9 Changelog for python-chess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Index 89
i
ii
python-chess, Release 0.28.1
Contents 1
python-chess, Release 0.28.1
2 Contents
CHAPTER 1
Introduction
python-chess is a pure Python chess library with move generation, move validation and support for common formats.
This is the Scholar’s mate in python-chess:
>>> board.legal_moves
<LegalMoveGenerator at ... (Nh3, Nf3, Nc3, Na3, h3, g3, f3, e3, d3, c3, ...)>
>>> chess.Move.from_uci("a8a1") in board.legal_moves
False
>>> board.push_san("e4")
Move.from_uci('e2e4')
>>> board.push_san("e5")
Move.from_uci('e7e5')
>>> board.push_san("Qh5")
Move.from_uci('d1h5')
>>> board.push_san("Nc6")
Move.from_uci('b8c6')
>>> board.push_san("Bc4")
Move.from_uci('f1c4')
>>> board.push_san("Nf6")
Move.from_uci('g8f6')
>>> board.push_san("Qxf7")
Move.from_uci('h5f7')
>>> board.is_checkmate()
True
>>> board
Board('r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4')
3
python-chess, Release 0.28.1
4 Chapter 1. Introduction
CHAPTER 2
Documentation
• Core
• PGN parsing and writing
• Polyglot opening book reading
• Gaviota endgame tablebase probing
• Syzygy endgame tablebase probing
• UCI/XBoard engine communication
• Variants
• Changelog
5
python-chess, Release 0.28.1
6 Chapter 2. Documentation
CHAPTER 3
Features
>>> board
7
python-chess, Release 0.28.1
• Chess variants: Standard, Chess960, Suicide, Giveaway, Atomic, King of the Hill, Racing Kings, Horde, Three-
check, Crazyhouse. Variant docs.
• Make and unmake moves.
>>> print(board)
r . b q k b . r
p p p p . Q p p
. . n . . n . .
. . . . p . . .
. . B . P . . .
. . . . . . . .
P P P P . P P P
R N B . K . N R
8 Chapter 3. Features
python-chess, Release 0.28.1
>>> board.is_stalemate()
False
>>> board.is_insufficient_material()
False
>>> board.is_game_over()
True
>>> board.can_claim_threefold_repetition()
False
>>> board.halfmove_clock
0
>>> board.can_claim_fifty_moves()
False
>>> board.can_claim_draw()
False
With the new rules from July 2014, a game ends as a draw (even without a claim) once a fivefold repetition
occurs or if there are 75 moves without a pawn push or capture. Other ways of ending a game take precedence.
>>> board.is_fivefold_repetition()
False
>>> board.is_seventyfive_moves()
False
>>> board.is_check()
True
>>> board.is_attacked_by(chess.WHITE, chess.E8)
True
9
python-chess, Release 0.28.1
'1. e4 e5 2. Nf3'
>>> book.close()
• Reads and writes PGNs. Supports headers, comments, NAGs and a tree of variations. Docs.
>>> import chess.pgn
>>> first_game.headers["White"]
'Molinari'
>>> first_game.headers["Black"]
'Bordais'
>>> first_game.mainline()
<Mainline at ... (1. e4 c5 2. c4 Nc6 3. Ne2 Nf6 4. Nbc3 Nb4 5. g3 Nd3#)>
10 Chapter 3. Features
python-chess, Release 0.28.1
>>> tablebase.close()
>>> engine.quit()
11
python-chess, Release 0.28.1
12 Chapter 3. Features
CHAPTER 4
Installing
13
python-chess, Release 0.28.1
14 Chapter 4. Installing
CHAPTER 5
If you like, let me know if you are creating something intresting with python-chess, for example:
• a stand-alone chess computer based on DGT board – http://www.picochess.org/
• a website to probe Syzygy endgame tablebases – https://syzygy-tables.info/
• deep learning for Crazyhouse – https://github.com/QueensGambit/CrazyAra
• a bridge between Lichess API and chess engines – https://github.com/careless25/lichess-bot
• a command-line PGN annotator – https://github.com/rpdelaney/python-chess-annotator
• an HTTP microservice to render board images – https://github.com/niklasf/web-boardimage
• a JIT compiled chess engine – https://github.com/SamRagusa/Batch-First
• a GUI to play against UCI chess engines – http://johncheetham.com/projects/jcchess/
• teaching Cognitive Science – https://jupyter.brynmawr.edu
15
python-chess, Release 0.28.1
Acknowledgements
Thanks to the Stockfish authors and thanks to Sam Tannous for publishing his approach to avoid rotated bitboards with
direct lookup (PDF) alongside his GPL2+ engine Shatranj. Some move generation ideas are taken from these sources.
Thanks to Ronald de Man for his Syzygy endgame tablebases. The probing code in python-chess is very directly
ported from his C probing code.
17
python-chess, Release 0.28.1
18 Chapter 6. Acknowledgements
CHAPTER 7
License
python-chess is licensed under the GPL 3 (or any later version at your option). Check out LICENSE.txt for the full
text.
19
python-chess, Release 0.28.1
20 Chapter 7. License
CHAPTER 8
Contents
8.1 Core
8.1.1 Colors
chess.PAWN = 1
chess.KNIGHT = 2
chess.BISHOP = 3
chess.ROOK = 4
chess.QUEEN = 5
chess.KING = 6
8.1.3 Squares
chess.A1 = 0
chess.B1 = 1
and so on to
chess.G8 = 62
21
python-chess, Release 0.28.1
chess.H8 = 63
chess.SQUARES = [chess.A1, chess.B1, ..., chess.G8, chess.H8]
chess.SQUARE_NAMES = ['a1', 'b1', ..., 'g8', 'h8']
chess.FILE_NAMES = ['a', 'b', ..., 'g', 'h']
chess.RANK_NAMES = ['1', '2', ..., '7', '8']
chess.square(file_index: int, rank_index: int) → int
Gets a square number by file and rank index.
chess.square_file(square: int) → int
Gets the file index of the square where 0 is the a-file.
chess.square_rank(square: int) → int
Gets the rank index of the square where 0 is the first rank.
chess.square_distance(a: int, b: int) → int
Gets the distance (i.e., the number of king steps) from square a to b.
chess.square_mirror(square: int) → int
Mirrors the square vertically.
8.1.4 Pieces
8.1.5 Moves
class chess.Move(from_square: int, to_square: int, promotion: Optional[int] = None, drop: Op-
tional[int] = None)
Represents a move from a square to a square and possibly the promotion piece type.
Drops and null moves are supported.
from_square
The source square.
to_square
The target square.
22 Chapter 8. Contents
python-chess, Release 0.28.1
promotion
The promotion piece type or one.
drop
The drop piece type or None.
uci() → str
Gets an UCI string for the move.
For example, a move from a7 to a8 would be a7a8 or a7a8q (if the latter is a promotion to a queen).
The UCI representation of a null move is 0000.
classmethod from_uci(uci: str) → chess.Move
Parses an UCI string.
Raises ValueError if the UCI string is invalid.
classmethod null() → chess.Move
Gets a null move.
A null move just passes the turn to the other side (and possibly forfeits en passant capturing). Null moves
evaluate to False in boolean contexts.
8.1.6 Board
8.1. Core 23
python-chess, Release 0.28.1
True
24 Chapter 8. Contents
python-chess, Release 0.28.1
reset() → None
Restores the starting position.
clear() → None
Clears the board.
Resets move stack and move counters. The side to move is white. There are no rooks or kings, so castling
rights are removed.
In order to be in a valid status() at least kings need to be put on the board.
clear_board() → None
Clears the board.
clear_stack() → None
Clears the move stack.
root() → BoardT
Returns a copy of the root position.
remove_piece_at(square: int) → Optional[chess.Piece]
Removes the piece from the given square. Returns the Piece or None if the square was already empty.
set_piece_at(square: int, piece: Optional[chess.Piece], promoted: bool = False) → None
Sets a piece at the given square.
An existing piece is replaced. Setting piece to None is equivalent to remove_piece_at().
is_check() → bool
Returns if the current side to move is in check.
is_into_check(move: chess.Move) → bool
Checks if the given move would leave the king in check or put it into check. The move must be at least
pseudo legal.
was_into_check() → bool
Checks if the king of the other side is attacked. Such a position is not valid and could only be reached by
an illegal move.
is_variant_end() → bool
Checks if the game is over due to a special variant end condition.
Note, for example, that stalemate is not considered a variant-specific end condition (this method will
return False), yet it can have a special result in suicide chess (any of is_variant_loss(),
is_variant_win(), is_variant_draw() might return True).
is_variant_loss() → bool
Checks if a special variant-specific loss condition is fulfilled.
is_variant_win() → bool
Checks if a special variant-specific win condition is fulfilled.
is_variant_draw() → bool
Checks if a special variant-specific drawing condition is fulfilled.
is_game_over(*, claim_draw: bool = False) → bool
Checks if the game is over due to checkmate, stalemate, insufficient material, the
seventyfive-move rule, fivefold repetition or a variant end condition.
The game is not considered to be over by the fifty-move rule or threefold repetition,
unless claim_draw is given. Note that checking the latter can be slow.
result(*, claim_draw: bool = False) → str
Gets the game result.
8.1. Core 25
python-chess, Release 0.28.1
1-0, 0-1 or 1/2-1/2 if the game is over. Otherwise, the result is undetermined: *.
is_checkmate() → bool
Checks if the current position is a checkmate.
is_stalemate() → bool
Checks if the current position is a stalemate.
is_insufficient_material() → bool
Checks if neither side has sufficient winning material (has_insufficient_material()).
has_insufficient_material(color: bool) → bool
Checks if color has insufficient winning material.
This is guaranteed to return False if color can still win the game.
The converse does not necessarily hold: The implementation only looks at the material, including the
colors of bishops, but not considering piece positions. So fortress positions or positions with forced lines
may return False, even though there is no possible winning line.
is_seventyfive_moves() → bool
Since the 1st of July 2014, a game is automatically drawn (without a claim by one of the players) if the
half-move clock since a capture or pawn move is equal to or grather than 150. Other means to end a game
take precedence.
is_fivefold_repetition() → bool
Since the 1st of July 2014 a game is automatically drawn (without a claim by one of the players) if a
position occurs for the fifth time. Originally this had to occur on consecutive alternating moves, but this
has since been revised.
can_claim_draw() → bool
Checks if the side to move can claim a draw by the fifty-move rule or by threefold repetition.
Note that checking the latter can be slow.
can_claim_fifty_moves() → bool
Draw by the fifty-move rule can be claimed once the clock of halfmoves since the last capture or pawn
move becomes equal or greater to 100 and the side to move still has a legal move they can make.
can_claim_threefold_repetition() → bool
Draw by threefold repetition can be claimed if the position on the board occured for the third time or if
such a repetition is reached with one of the possible legal moves.
Note that checking this can be slow: In the worst case scenario every legal move has to be tested and the
entire game has to be replayed because there is no incremental transposition table.
is_repetition(count: int = 3) → bool
Checks if the current position has repeated 3 (or a given number of) times.
Unlike can_claim_threefold_repetition(), this does not consider a repetition that can be
played on the next move.
Note that checking this can be slow: In the worst case the entire game has to be replayed because there is
no incremental transposition table.
push(move: chess.Move) → None
Updates the position with the given move and puts it onto the move stack.
26 Chapter 8. Contents
python-chess, Release 0.28.1
Null moves just increment the move counters, switch turns and forfeit en passant capturing.
Warning Moves are not checked for legality.
pop() → chess.Move
Restores the previous position and returns the last move from the stack.
Raises IndexError if the stack is empty.
peek() → chess.Move
Gets the last move from the move stack.
Raises IndexError if the move stack is empty.
has_pseudo_legal_en_passant() → bool
Checks if there is a pseudo-legal en passant capture.
has_legal_en_passant() → bool
Checks if there is a legal en passant capture.
fen(*, shredder: bool = False, en_passant: str = ’legal’, promoted: Optional[bool] = None) → str
Gets a FEN representation of the position.
A FEN string (e.g., rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1)
consists of the position part board_fen(), the turn, the castling part (castling_rights), the en
passant square (ep_square), the halfmove_clock and the fullmove_number.
Parameters
• shredder – Use castling_shredder_fen() and encode castling rights by the
file of the rook (like HAha) instead of the default castling_xfen() (like KQkq).
• en_passant – By default, only fully legal en passant squares are included
(has_legal_en_passant()). Pass fen to strictly follow the FEN specification (al-
ways include the en passant square after a two-step pawn move) or xfen to follow the
X-FEN specification (has_pseudo_legal_en_passant()).
• promoted – Mark promoted pieces like Q~. By default, this is only enabled in chess
variants where this is relevant.
set_fen(fen: str) → None
Parses a FEN and sets the position from it.
Raises ValueError if the FEN string is invalid.
set_castling_fen(castling_fen: str) → None
Sets castling rights from a string in FEN notation like Qqk.
Raises ValueError if the castling FEN is syntactically invalid.
set_board_fen(fen: str) → None
Parses a FEN and sets the board from it.
Raises ValueError if the FEN string is invalid.
set_piece_map(pieces: Mapping[int, chess.Piece]) → None
Sets up the board from a dictionary of pieces by square index.
8.1. Core 27
python-chess, Release 0.28.1
28 Chapter 8. Contents
python-chess, Release 0.28.1
8.1. Core 29
python-chess, Release 0.28.1
has_chess960_castling_rights() → bool
Checks if there are castling rights that are only possible in Chess960.
status() → chess.Status
Gets a bitmask of possible problems with the position.
Move making, generation and validation are only guaranteed to work on a completely valid board.
STATUS_VALID for a completely valid board.
Otherwise, bitwise combinations of: STATUS_NO_WHITE_KING,
STATUS_NO_BLACK_KING, STATUS_TOO_MANY_KINGS, STATUS_TOO_MANY_WHITE_PAWNS,
STATUS_TOO_MANY_BLACK_PAWNS, STATUS_PAWNS_ON_BACKRANK,
STATUS_TOO_MANY_WHITE_PIECES, STATUS_TOO_MANY_BLACK_PIECES,
STATUS_BAD_CASTLING_RIGHTS, STATUS_INVALID_EP_SQUARE,
STATUS_OPPOSITE_CHECK, STATUS_EMPTY, STATUS_RACE_CHECK, STATUS_RACE_OVER,
STATUS_RACE_MATERIAL.
is_valid() → bool
Checks if the board is valid.
Move making, generation and validation are only guaranteed to work on a completely valid board.
See status() for details.
mirror() → BoardT
Returns a mirrored copy of the board.
The board is mirrored vertically and piece colors are swapped, so that the position is equivalent modulo
color.
copy(*, stack: Union[bool, int] = True) → BoardT
Creates a copy of the board.
Defaults to copying the entire move stack. Alternatively, stack can be False, or an integer to copy a
limited number of moves.
classmethod empty(*, chess960: bool = False) → BoardT
Creates a new empty board. Also see clear().
classmethod from_epd(epd: str, *, chess960: bool = False) → Tuple[BoardT, Dict[str,
Union[None, str, int, float, chess.Move, List[chess.Move]]]]
Creates a new board from an EPD string. See set_epd().
Returns the board and the dictionary of parsed operations as a tuple.
classmethod from_chess960_pos(sharnagl: int) → BoardT
Creates a new board, initialized with a Chess960 starting position.
30 Chapter 8. Contents
python-chess, Release 0.28.1
Returns a set of squares that mask the rank, file or diagonal of the pin. If there is no pin, then a
mask of the entire board is returned.
8.1. Core 31
python-chess, Release 0.28.1
32 Chapter 8. Contents
python-chess, Release 0.28.1
>>> print(squares)
1 . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
1 1 1 1 1 1 1 1
>>> len(squares)
9
>>> bool(squares)
True
8.1. Core 33
python-chess, Release 0.28.1
>>> list(squares)
[0, 1, 2, 3, 4, 5, 6, 7, 56]
Square sets are internally represented by 64-bit integer masks of the included squares. Bitwise operations can
be used to compute unions, intersections and shifts.
>>> int(squares)
72057594037928191
34 Chapter 8. Contents
python-chess, Release 0.28.1
chess.BB_ALL = 0xFFFFFFFFFFFFFFFF
Single squares:
chess.BB_SQUARES = [chess.BB_A1, chess.BB_B1, ..., chess.BB_G8, chess.BB_H8]
Ranks and files:
chess.BB_RANKS = [chess.BB_RANK_1, ..., chess.BB_RANK_8]
chess.BB_FILES = [chess.BB_FILE_A, ..., chess.BB_FILE_H]
Other masks:
chess.BB_LIGHT_SQUARES = 0x55AA55AA55AA55AA
chess.BB_DARK_SQUARES = 0xAA55AA55AA55AA55
chess.BB_BACKRANKS = chess.BB_RANK_1 | chess.BB_RANK_8
chess.BB_CORNERS = chess.BB_A1 | chess.BB_H1 | chess.BB_A8 | chess.BB_H8
chess.BB_CENTER = chess.BB_D4 | chess.BB_E4 | chess.BB_D5 | chess.BB_E5
8.2.1 Parsing
By using text mode, the parser does not need to handle encodings. It is the caller’s responsibility to open the
file with the correct encoding. PGN files are usually ASCII or UTF-8 encoded. So, the following should cover
most relevant cases (ASCII, UTF-8, UTF-8 with BOM).
>>> import io
>>>
>>> pgn = io.StringIO("1. e4 e5 2. Nf3 *")
>>> game = chess.pgn.read_game(pgn)
The end of a game is determined by a completely blank line or the end of the file. (Of course, blank lines in
comments are possible).
According to the PGN standard, at least the usual 7 header tags are required for a valid game. This parser also
handles games without any headers just fine.
The parser is relatively forgiving when it comes to errors. It skips over tokens it can not parse. By default, any
exceptions are logged and collected in Game.errors. This behavior can be overriden.
Returns the parsed game or None if the end of file is reached.
8.2.2 Writing
If you want to export your game with all headers, comments and variations, you can do it like this:
>>> import chess
>>> import chess.pgn
>>>
>>> game = chess.pgn.Game()
>>> game.headers["Event"] = "Example"
>>> node = game.add_variation(chess.Move.from_uci("e2e4"))
>>> node = node.add_variation(chess.Move.from_uci("e7e5"))
>>> node.comment = "Comment"
>>>
>>> print(game)
[Event "Example"]
[Site "?"]
[Date "????.??.??"]
[Round "?"]
[White "?"]
[Black "?"]
[Result "*"]
<BLANKLINE>
1. e4 e5 { Comment } *
Remember that games in files should be separated with extra blank lines.
>>> print(game, file=open("/dev/null", "w"), end="\n\n")
Games are represented as a tree of moves. Each GameNode can have extra information, such as comments. The root
node of a game (Game extends the GameNode) also holds general information, such as game headers.
class chess.pgn.Game(headers: Union[Mapping[str, str], Iterable[Tuple[str, str]], None] = None)
The root node of a game with extra information such as headers and the starting position. Also has all the other
properties and methods of GameNode.
headers
A mapping of headers. By default, the following 7 headers are provided:
36 Chapter 8. Contents
python-chess, Release 0.28.1
errors
A list of errors (such as illegal or ambiguous moves) encountered while parsing the game.
board(*, _cache: bool = False) → chess.Board
Gets the starting position of the game.
Unless the FEN header tag is set, this is the default starting position (for the Variant).
setup(board: Union[chess.Board, str]) → None
Sets up a specific starting position. This sets (or resets) the FEN, SetUp, and Variant header tags.
accept(visitor)
Traverses the game in PGN order using the given visitor. Returns the visitor result.
classmethod from_board(board: chess.Board) → GameT
Creates a game from the move stack of a Board().
classmethod without_tag_roster() → GameT
Creates an empty game without the default 7 tag roster.
class chess.pgn.GameNode
parent
The parent node or None if this is the root node of the game.
move
The move leading to this node or None if this is the root node of the game.
nags = set()
A set of NAGs as integers. NAGs always go behind a move, so the root node of the game will never have
NAGs.
comment = ''
A comment that goes behind the move leading to this node. Comments that occur before any moves are
assigned to the root node.
starting_comment = ''
A comment for the start of a variation. Only nodes that actually start a variation
(starts_variation() checks this) can have a starting comment. The root node can not have a
starting comment.
variations
A list of child nodes.
board(*, _cache: bool = True) → chess.Board
Gets a board with the position of the node.
It’s a copy, so modifying the board will not alter the game.
san() → str
Gets the standard algebraic notation of the move leading to this node. See chess.Board.san().
Do not call this on the root node.
38 Chapter 8. Contents
python-chess, Release 0.28.1
8.2.4 Visitors
end_game() → None
Called at the end of a game.
result()
Called to get the result of the visitor. Defaults to True.
handle_error(error: Exception) → None
Called for encountered errors. Defaults to raising an exception.
The following visitors are readily available.
class chess.pgn.GameBuilder(*, Game=<class ’chess.pgn.Game’>)
Creates a game model. Default visitor for read_game().
handle_error(error: Exception) → None
Populates chess.pgn.Game.errors with encountered errors and logs them.
You can silence the log and handle errors yourself, after parsing:
>>> import chess.pgn
>>> import logging
>>>
>>> logging.getLogger("chess.pgn").setLevel(logging.CRITICAL)
>>>
>>> pgn = open("data/pgn/kasparov-deep-blue-1997.pgn")
>>>
>>> game = chess.pgn.read_game(pgn)
>>> game.errors # List of exceptions
[]
You can also override this method to hook into error handling:
>>> import chess.pgn
>>>
>>> class MyGameBuilder(chess.pgn.GameBuilder):
>>> def handle_error(self, error):
>>> pass # Ignore error
>>>
>>> pgn = open("data/pgn/kasparov-deep-blue-1997.pgn")
>>>
>>> game = chess.pgn.read_game(pgn, Visitor=MyGameBuilder)
result()
Returns the visited Game().
class chess.pgn.HeadersBuilder(*, Headers=<function HeadersBuilder.<lambda>>)
Collects headers into a dictionary.
class chess.pgn.BoardBuilder
Returns the final position of the game. The mainline of the game is on the move stack.
class chess.pgn.SkipVisitor
Skips a game.
class chess.pgn.StringExporter(*, columns: Optional[int] = 80, headers: bool = True, com-
ments: bool = True, variations: bool = True)
Allows exporting a game as a string.
>>> import chess.pgn
>>>
>>> game = chess.pgn.Game()
(continues on next page)
40 Chapter 8. Contents
python-chess, Release 0.28.1
Only columns characters are written per line. If columns is None, then the entire movetext will be on a single
line. This does not affect header tags and comments.
There will be no newline characters at the end of the string.
class chess.pgn.FileExporter(handle: TextIO, *, columns: Optional[int] = 80, headers: bool =
True, comments: bool = True, variations: bool = True)
Acts like a StringExporter, but games are written directly into a text file.
There will always be a blank line after each game. Handling encodings is up to the caller.
8.2.5 NAGs
Numeric anotation glyphs describe moves and positions using standardized codes that are understood by many chess
programs. During PGN parsing, annotations like !, ?, !!, etc., are also converted to NAGs.
chess.pgn.NAG_GOOD_MOVE = 1
A good move. Can also be indicated by ! in PGN notation.
chess.pgn.NAG_MISTAKE = 2
A mistake. Can also be indicated by ? in PGN notation.
chess.pgn.NAG_BRILLIANT_MOVE = 3
A brilliant move. Can also be indicated by !! in PGN notation.
chess.pgn.NAG_BLUNDER = 4
A blunder. Can also be indicated by ?? in PGN notation.
chess.pgn.NAG_SPECULATIVE_MOVE = 5
A speculative move. Can also be indicated by !? in PGN notation.
chess.pgn.NAG_DUBIOUS_MOVE = 6
A dubious move. Can also be indicated by ?! in PGN notation.
8.2.6 Skimming
These functions allow for quickly skimming games without fully parsing them.
chess.pgn.read_headers(handle: TextIO) → Optional[chess.pgn.Headers]
Reads game headers from a PGN file opened in text mode.
Since actually parsing many games from a big file is relatively expensive, this is a better way to look only for
specific games and then seek and parse them later.
This example scans for the first game with Kasparov as the white player.
class chess.polyglot.Entry
An entry from a Polyglot opening book.
key
The Zobrist hash of the position.
42 Chapter 8. Contents
python-chess, Release 0.28.1
raw_move
The raw binary representation of the move. Use move instead.
weight
An integer value that can be used as the weight for this entry.
learn
Another integer value that can be used for extra information.
move
The Move.
class chess.polyglot.MemoryMappedReader(filename: Union[str, bytes])
Maps a Polyglot opening book to memory.
find_all(board: Union[chess.Board, int], *, minimum_weight: int = 1, exclude_moves: Con-
tainer[chess.Move] = ()) → Iterator[chess.polyglot.Entry]
Seeks a specific position and yields corresponding entries.
find(board: Union[chess.Board, int], *, minimum_weight: int = 1, exclude_moves: Con-
tainer[chess.Move] = ()) → chess.polyglot.Entry
Finds the main entry for the given position or Zobrist hash.
The main entry is the (first) entry with the highest weight.
By default, entries with weight 0 are excluded. This is a common way to delete entries from an opening
book without compacting it. Pass minimum_weight 0 to select all entries.
Raises IndexError if no entries are found. Use get() if you prefer to get None instead of
an exception.
choice(board: Union[chess.Board, int], *, minimum_weight: int = 1, ex-
clude_moves: Container[chess.Move] = (), random=<module ’ran-
dom’ from ’/home/docs/checkouts/readthedocs.org/user_builds/python-
chess/envs/latest/lib/python3.7/random.py’>) → chess.polyglot.Entry
Uniformly selects a random entry for the given position.
Raises IndexError if no entries are found.
weighted_choice(board: Union[chess.Board, int], *, exclude_moves: Con-
tainer[chess.Move] = (), random=<module ’random’ from
’/home/docs/checkouts/readthedocs.org/user_builds/python-
chess/envs/latest/lib/python3.7/random.py’>) → chess.polyglot.Entry
Selects a random entry for the given position, distributed by the weights of the entries.
Raises IndexError if no entries are found.
close() → None
Closes the reader.
chess.polyglot.POLYGLOT_RANDOM_ARRAY = [0x9D39247E33776D41, ..., 0xF8D626AAAF278509]
Array of 781 polyglot compatible pseudo random values for Zobrist hashing.
chess.polyglot.zobrist_hash(board: chess.Board, *, _hasher: Callable[[chess.Board], int] =
<chess.polyglot.ZobristHasher object>) → int
Calculates the Polyglot Zobrist hash of the position.
A Zobrist hash is an XOR of pseudo-random values picked from an array. Which values are picked is decided
by features of the position, such as piece positions, castling rights and en passant squares.
Gaviota tablebases provide WDL (win/draw/loss) and DTM (depth to mate) information for all endgame positions
with up to 5 pieces. Positions with castling rights are not included.
chess.gaviota.open_tablebase(directory: str, *, libgtb=None, Library-
Loader=<ctypes.LibraryLoader object>)
→ Union[chess.gaviota.NativeTablebase,
chess.gaviota.PythonTablebase]
Opens a collection of tables for probing.
First native access via the shared library libgtb is tried. You can optionally provide a specific library name or a
library loader. The shared library has global state and caches, so only one instance can be open at a time.
Second, pure Python probing code is tried.
class chess.gaviota.PythonTablebase
Provides access to Gaviota tablebases using pure Python code.
add_directory(directory: Union[str, bytes]) → None
Adds .gtb.cp4 tables from a directory. The relevant files are lazily opened when the tablebase is actually
probed.
probe_dtm(board: chess.Board) → int
Probes for depth to mate information.
The absolute value is the number of half-moves until forced mate (or 0 in drawn positions). The value is
positive if the side to move is winning, otherwise it is negative.
In the example position white to move will get mated in 10 half-moves:
44 Chapter 8. Contents
python-chess, Release 0.28.1
close() → None
Closes all loaded tables.
8.4.1 libgtb
For faster access you can build and install a shared library. Otherwise the pure Python probing code is used.
Syzygy tablebases provide WDL (win/draw/loss) and DTZ (distance to zero) information for all endgame positions
with up to 6 (and experimentally 7) pieces. Positions with castling rights are not included.
chess.syzygy.open_tablebase(directory: Union[str, bytes], *, load_wdl: bool = True,
load_dtz: bool = True, max_fds: Optional[int] = 128, Vari-
antBoard: Type[chess.Board] = <class ’chess.Board’>) →
chess.syzygy.Tablebase
Opens a collection of tables for probing. See Tablebase.
Note: Generally probing requires tablebase files for the specific material composition, as well as tablebase files
with less pieces. This is important because 6-piece and 5-piece files are often distributed seperately, but are both
required for 6-piece positions. Use add_directory() to load tables from additional directories.
WDL DTZ
-2 -100 <= Unconditional loss (assuming 50-move counter is zero), where a zeroing move can
n <= -1 be forced in -n plies.
-1 n < -100 Loss, but draw under the 50-move rule. A zeroing move can be forced in -n plies or
-n - 100 plies (if a later phase is responsible for the blessed loss).
0 0 Draw.
1 100 < n Win, but draw under the 50-move rule. A zeroing move can be forced in n plies or n
- 100 plies (if a later phase is responsible for the cursed win).
2 1 <= n Unconditional win (assuming 50-move counter is zero), where a zeroing move can
<= 100 be forced in n plies.
The return value can be off by one: a return value -n can mean a losing zeroing move in in n + 1 plies and
a return value +n can mean a winning zeroing move in n + 1 plies. This is guaranteed not to happen for
46 Chapter 8. Contents
python-chess, Release 0.28.1
positions exactly on the edge of the 50-move rule, so that (with some care) this never impacts the result of
practical play.
Minmaxing the DTZ values guarantees winning a won position (and drawing a drawn position), because it
makes progress keeping the win in hand. However the lines are not always the most straightforward ways
to win. Engines like Stockfish calculate themselves, checking with DTZ, but only play according to DTZ
if they can not manage on their own.
Probing is thread-safe when done with different board objects and if board objects are not modified during
probing.
Raises KeyError (or specifically chess.syzygy.MissingTableError) if the position
could not be found in the tablebase. Use get_dtz() if you prefer to get None instead of
an exception.
Note that probing corrupted table files is undefined behavior.
close() → None
Closes all loaded tables.
UCI and XBoard are protocols for communicating with chess engines. This module implements an abstraction for
playing moves and analysing positions with both kinds of engines.
The preferred way to use the API is with an asyncio event loop. The examples also show a synchronous wrapper
SimpleEngine that automatically spawns an event loop in the background.
8.6.1 Playing
Example: Let Stockfish play against itself, 100 milliseconds per move.
import chess
import chess.engine
engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish")
board = chess.Board()
while not board.is_game_over():
result = engine.play(board, chess.engine.Limit(time=0.100))
board.push(result.move)
engine.quit()
import asyncio
import chess
(continues on next page)
board = chess.Board()
while not board.is_game_over():
result = await engine.play(board, chess.engine.Limit(time=0.100))
board.push(result.move)
await engine.quit()
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
48 Chapter 8. Contents
python-chess, Release 0.28.1
nodes
Search only a limited number of nodes.
mate
Search for a mate in mate moves.
white_clock
Time in seconds remaining for White.
black_clock
Time in seconds remaining for Black.
white_inc
Fisher increment for White, in seconds.
black_inc
Fisher increment for Black, in seconds.
remaining_moves
Number of moves to the next time control. If this is not set, but white_clock and black_clock are, then it is
sudden death.
class chess.engine.PlayResult(move: Optional[chess.Move], ponder: Optional[chess.Move],
info: Optional[chess.engine.InfoDict] = None, *, draw_offered:
bool = False, resigned: bool = False)
Returned by chess.engine.EngineProtocol.play().
move
The best move accordig to the engine, or None.
ponder
The response that the engine expects after move, or None.
info
A dictionary of extra information sent by the engine. Commonly used keys are: score (a PovScore),
pv (a list of Move objects), depth, seldepth, time (in seconds), nodes, nps, tbhits, multipv.
Others: currmove, currmovenumber, hashfull, cpuload, refutation, currline, ebf
and string.
draw_offered
Whether the engine offered a draw before moving.
resigned
Whether the engine resigned.
Example:
import chess
import chess.engine
engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish")
board = chess.Board()
info = engine.analyse(board, chess.engine.Limit(time=0.100))
print("Score:", info["score"])
# Score: +20
engine.quit()
import asyncio
import chess
import chess.engine
board = chess.Board()
info = await engine.analyse(board, chess.engine.Limit(time=0.100))
print(info["score"])
# Score: +20
board = chess.Board("r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w
˓→ KQkq - 2 4")
info = await engine.analyse(board, chess.engine.Limit(depth=20))
print(info["score"])
# Score: #1
await engine.quit()
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
50 Chapter 8. Contents
python-chess, Release 0.28.1
>>> -Cp(20)
Cp(-20)
>>> -Mate(-4)
Mate(+4)
>>> -Mate(0)
MateGiven
>>> Cp(-300).score()
-300
>>> Mate(5).score() is None
True
>>> Mate(5).score(mate_score=100000)
99995
mate() → Optional[int]
Returns the number of plies to mate, negative if we are getting mated, or None.
Warning This conflates Mate(0) (we lost) and MateGiven (we won) to 0.
is_mate() → bool
Tests if this is a mate score.
Example: Stream information from the engine and stop on an arbitrary condition.
import chess
import chess.engine
engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish")
engine.quit()
import asyncio
import chess
import chess.engine
await engine.quit()
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
52 Chapter 8. Contents
python-chess, Release 0.28.1
• board – The position to analyse. The entire move stack will be sent to the engine.
• limit – Optional. An instance of chess.engine.Limit that determines when to
stop the analysis. Analysis is infinite by default.
• multipv – Optional. Analyse multiple root moves.
• game – Optional. An arbitrary object that identifies the game. Will automatically inform
the engine if the object is not equal to the previous game (e.g. ucinewgame, new).
• info – Selects which information to retrieve from the engine. INFO_NONE,
INFO_BASE (basic information that is trivial to obtain), INFO_SCORE, INFO_PV,
INFO_REFUTATION, INFO_CURRLINE, INFO_ALL or any bitwise combination.
Some overhead is associated with parsing extra information.
• root_moves – Optional. Limit analysis to a list of root moves.
• options – Optional. A dictionary of engine options for the analysis. The previous
configuration will be restored after the analysis is complete. You can permanently apply a
configuration with configure().
Returns AnalysisResult, a handle that allows asynchronously iterating over the information sent by
the engine and stopping the the analysis at any time.
class chess.engine.AnalysisResult(stop: Optional[Callable[[], None]] = None)
Handle to ongoing engine analysis. Returned by chess.engine.EngineProtocol.analysis().
Can be used to asynchronously iterate over information sent by the engine.
Automatically stops the analysis when used as a context manager.
info
A dictionary of aggregated information sent by the engine. This is actually an alias for multipv[0].
multipv
A list of dictionaries with aggregated information sent by the engine. One item for each root move.
stop() → None
Stops the analysis as soon as possible.
empty() → bool
Checks if all information has been consumed.
If the queue is empty, but the analysis is still ongoing, then further information can become available in
the future.
If the queue is not empty, then the next call to get() will return instantly.
coroutine get() → chess.engine.InfoDict
Waits for the next dictionary of information from the engine and returns it.
It might be more convenient to use async for info in analysis: ....
Raises chess.engine.AnalysisComplete if the analysis is complete (or has been
stopped) and all information has been consumed. Use next() if you prefer to get None
instead of an exception.
coroutine wait() → None
Waits until the analysis is complete (or stopped).
8.6.4 Options
import asyncio
import chess.engine
# Set an option.
await engine.configure({"Hash": 32})
# [...]
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
54 Chapter 8. Contents
python-chess, Release 0.28.1
default
The default value of the option.
min
The minimum integer value of a spin option.
max
The maximum integer value of a spin option.
var
A list of allowed string values for a combo option.
is_managed() → bool
Some options are managed automatically: UCI_Chess960, UCI_Variant, MultiPV, Ponder.
8.6.5 Logging
Communication is logged with debug level on a logger named chess.engine. Debug logs are useful while trou-
bleshooting. Please also provide them when submitting bug reports.
import logging
8.6.6 AsyncSSH
EngineProtocol can also be used with AsyncSSH (since 1.16.0) to communicate with an engine on a remote
computer.
import asyncio
import asyncssh
import chess
import chess.engine
await engine.initialize()
asyncio.run(main())
8.6.7 Reference
class chess.engine.EngineError
Runtime error caused by a misbehaving engine or incorrect usage.
class chess.engine.EngineTerminatedError
The engine process exited unexpectedly.
class chess.engine.AnalysisComplete
Raised when analysis is complete, all information has been consumed, but further information was requested.
coroutine chess.engine.popen_uci(command: Union[str, List[str]], *, setpgrp:
bool = False, loop=None, **popen_args) →
Tuple[asyncio.transports.SubprocessTransport,
chess.engine.UciProtocol]
Spawns and initializes an UCI engine.
Parameters
• command – Path of the engine executable, or a list including the path and arguments.
• setpgrp – Open the engine process in a new process group. This will stop signals (such
as keyboard interrupts) from propagating from the parent process. Defaults to False.
• popen_args – Additional arguments for popen. Do not set stdin, stdout, bufsize
or universal_newlines.
Returns a subprocess transport and engine protocol pair.
coroutine chess.engine.popen_xboard(command: Union[str, List[str]], *, setp-
grp: bool = False, **popen_args) → Tu-
ple[asyncio.transports.SubprocessTransport,
chess.engine.XBoardProtocol]
Spawns and initializes an XBoard engine.
Parameters
• command – Path of the engine executable, or a list including the path and arguments.
• setpgrp – Open the engine process in a new process group. This will stop signals (such
as keyboard interrupts) from propagating from the parent process. Defaults to False.
• popen_args – Additional arguments for popen. Do not set stdin, stdout, bufsize
or universal_newlines.
Returns a subprocess transport and engine protocol pair.
class chess.engine.EngineProtocol(*, loop=None)
Protocol for communicating with a chess engine process.
returncode
Future: Exit code of the process.
id
Dictionary of information about the engine. Common keys are name and author.
coroutine initialize() → None
Initializes the engine.
coroutine ping() → None
Pings the engine and waits for a response. Used to ensure the engine is still alive and idle.
coroutine quit() → None
Asks the engine to shut down.
56 Chapter 8. Contents
python-chess, Release 0.28.1
class chess.engine.UciProtocol
An implementation of the Universal Chess Interface protocol.
class chess.engine.XBoardProtocol
An implementation of the XBoard protocol (CECP).
class chess.engine.SimpleEngine(transport: asyncio.transports.SubprocessTransport, protocol:
chess.engine.EngineProtocol, *, timeout: Optional[float] =
10.0)
Synchronous wrapper around a transport and engine protocol pair. Provides the same methods and attributes as
EngineProtocol, with blocking functions instead of coroutines.
You may not concurrently modify objects passed to any of the methods. Other than that SimpleEngine is
thread-safe. When sending a new command to the engine, any previous running command will be cancelled as
soon as possible.
Methods will raise asyncio.TimeoutError if an operation takes timeout seconds longer than expected
(unless timeout is None).
Automatically closes the transport when used as a context manager.
close() → None
Closes the transport and the background event loop as soon as possible.
classmethod popen_uci(command: Union[str, List[str]], *, timeout: Optional[float] = 10.0,
debug: bool = False, setpgrp: bool = False, **popen_args) →
chess.engine.SimpleEngine
Spawns and initializes an UCI engine. Returns a SimpleEngine instance.
classmethod popen_xboard(command: Union[str, List[str]], *, timeout: Optional[float] = 10.0,
debug: bool = False, setpgrp: bool = False, **popen_args) →
chess.engine.SimpleEngine
Spawns and initializes an XBoard engine. Returns a SimpleEngine instance.
class chess.engine.SimpleAnalysisResult(simple_engine: chess.engine.SimpleEngine, inner:
chess.engine.AnalysisResult)
Synchronous wrapper around AnalysisResult. Returned by chess.engine.SimpleEngine.
analysis().
chess.engine.EventLoopPolicy() → None
An event loop policy that ensures the event loop is capable of spawning and watching subprocesses, even when
not running in the main thread.
Windows: Creates a ProactorEventLoop.
Unix: Creates a SelectorEventLoop. Child watchers are thread local. When not running on the main
thread, the default child watchers use relatively slow polling to detect process termination. This does not affect
communication.
The chess.svg module renders SVG Tiny images (mostly for IPython/Jupyter Notebook integration). The piece
images by Colin M.L. Burnett are triple licensed under the GFDL, BSD and GPL.
chess.svg.piece(piece: chess.Piece, size: Optional[int] = None) → str
Renders the given chess.Piece as an SVG image.
8.8 Variants
58 Chapter 8. Contents
python-chess, Release 0.28.1
8.8.1 Chess960
>>> chess.Board(chess960=True)
Board('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', chess960=True)
8.8.2 UCI/XBoard
Multi-Variant Stockfish and other engines have an UCI_Variant option. XBoard engines may declare support for
variants. This is automatically managed.
8.8. Variants 59
python-chess, Release 0.28.1
8.8.3 Syzygy
Syzygy tablebases are available for suicide, giveaway and atomic chess.
Bugfixes:
• The minimum Python version is 3.5.3 (instead of 3.5.0).
• Fix board.is_irreversible() when capturing a rook that had castling rights.
Changes:
• is_en_passant(), is_capture(), is_zeroing(), is_irreversible(), is_castling(), is_kingside_castling() and
is_queenside_castling() now consistently return False for null moves.
• Added chess.engine.InfoDict class with typed shorthands for common keys.
• Support [Variant “3-check”] (from chess.com PGNs).
Changes:
• Dropped support for Python 3.4 (end of life reached).
• chess.polyglot.Entry.move is now a property instead of a method. The raw move is now always decoded in
the context of the position (relevant for castling moves).
• Piece, Move, BaseBoard and Board comparisons no longer support ducktyping.
• FENs sent to engines now always include potential en-passant squares, even if no legal en-passant capture exists.
• Circular SVG arrows now have a circle CSS class.
• Superfluous dashes (-) in EPDs are no longer treated as opcodes.
• Removed GameCreator, HeaderCreator and BoardCreator aliases for {Game,Headers,Board}Builder.
Bugfixes:
• Notation like Kh1 is no longer accepted for castling moves.
• Remove stale files from wheels published on PyPI.
• Parsing Three-Check EPDs with moves was always failing.
• Some methods in chess.variant were returning bool-ish integers, when they should have returned bool.
• chess.engine: Fix line decoding when Windows line-endings arrive seperately in stdout buffer.
• chess.engine: Survive timeout in analysis.
60 Chapter 8. Contents
python-chess, Release 0.28.1
Changes:
• XBoardProtocol will no longer raise an exception when the engine resigned. Instead it sets a new flag PlayRe-
sult.resigned. resigned and draw_offered are keyword-only arguments.
• Renamed chess.pgn.{Game,Header,Board}Creator to {Game,Headers,Board}Builder. Aliases kept in place.
Bugfixes:
• Make XBoardProtocol robust against engines that send a move after claiming a draw or resigning. Thanks
@pascalgeo.
• XBoardProtocol no longer ignores Hint: sent by the engine.
• Fix handling of illegal moves in XBoardProtocol.
• Fix exception when engine is shut down while pondering.
• Fix unhandled internal exception and file descriptor leak when engine initialization fails.
• Fix HordeBoard.status() when black pieces are on the first rank. Thanks @Wisling.
New features:
• Added chess.pgn.Game.builder(), chess.pgn.Headers.builder() and chess.pgn.GameNode.dangling_node() to
simplify subclassing GameNode.
• EngineProtocol.communicate() is now also available in the synchronous API.
Bugfixes:
• chess.engine.XBoardProtocol.play() was searching 100 times longer than intended when us-
ing chess.engine.Limit.time, and searching 100 times more nodes than intended when using
chess.engine.Limit.nodes. Thanks @pascalgeo.
Bugfixes:
• chess.engine.XBoardProtocol.play() was raising KeyError when using time controls with increment or remain-
ing moves. Thanks @pascalgeo.
This is the second release candidate for python-chess 1.0. If you see the need for breaking changes, please speak up
now!
Bugfixes:
• EngineProtocol.analyse(*, multipv) was not passing this argument to the engine and therefore only returned the
first principal variation. Thanks @svangordon.
• chess.svg.board(*, squares): The X symbol on selected squares is now more visible when it overlaps pieces.
Changes:
• FEN/EPD parsing is now more relaxed: Incomplete FENs and EPDs are completed with reasonable defaults
(w - - 0 1). The EPD parser accepts fields with moves in UCI notation (for example the technically invalid bm
g1f3 instead of bm Nf3).
• The PGN parser now skips games with invalid FEN headers and variations after an illegal move (after handling
the error as usual).
New features:
• Added Board.is_repetition(count=3).
• Document that chess.engine.EngineProtocol is compatible with AsyncSSH 1.16.0.
This is the first release candidate for python-chess 1.0. If you see the need for breaking changes, please speak up
now!
Changes:
• chess.engine is now stable and replaces chess.uci and chess.xboard.
• Advanced: EngineProtocol.initialize() is now public for use with custom transports.
• Removed __ne__ implementations (not required since Python 3).
• Assorted documentation and coding-style improvements.
New features:
• Check insufficient material for a specific side: board.has_insufficient_material(color).
• Copy boards with limited stack depth: board.copy(stack=depth).
Bugfixes:
• Properly handle delayed engine errors, for example unsupported options.
Bugfixes:
• chess.engine did not correctly handle Windows-style line endings. Thanks @Bstylestuff.
62 Chapter 8. Contents
python-chess, Release 0.28.1
New features:
• This release introduces a new experimental API for chess engine communication, chess.engine, based on
asyncio. It is intended to eventually replace chess.uci and chess.xboard.
Bugfixes:
• Fixed race condition in LRU-cache of open Syzygy tables. The LRU-cache is enabled by default (max_fds).
• Fix deprecation warning and unclosed file in setup.py. Thanks Mickaël Schoentgen.
Changes:
• chess.pgn.read_game() now ignores BOM at the start of the stream.
• Removed deprecated items.
Bugfixes:
• CrazyhouseBoard.root() and ThreeCheckBoard.root() were not returning the correct pockets and number of
remaining checks, respectively. Thanks @gbtami.
• chess.pgn.skip_game() now correctly skips PGN comments that contain line-breaks and PGN header tag nota-
tion.
Changes:
• Renamed chess.pgn.GameModelCreator to GameCreator. Alias kept in place and will be removed in a future
release.
• Renamed chess.engine to chess._engine. Use re-exports from chess.uci or chess.xboard.
• Renamed Board.stack to Board._stack. Do not use this directly.
• Improved memory usage: Board.legal_moves and Board.pseudo_legal_moves no longer create reference cycles.
PGN visitors can manage headers themselves.
• Removed previously deprecated items.
Features:
• Added chess.pgn.BaseVisitor.visit_board() and chess.pgn.BoardCreator.
Bugfixes:
• Fix chess.Board.set_epd() and chess.Board.from_epd() with semicolon in string operand. Thanks @jdart1.
• chess.pgn.GameNode.uci() was always raising an exception. Also included in v0.24.0.
This release drops support for Python 2. The 0.23.x branch will be maintained for one more month.
Changes:
• Require Python 3.4. Thanks @hugovk.
• No longer using extra pip features: pip install python-chess[engine,gaviota] is now pip install python-chess.
• Various keyword arguments can now be used as keyword arguments only.
• chess.pgn.GameNode.accept() now also visits the move leading to that node.
• chess.pgn.GameModelCreator now requires that begin_game() be called.
• chess.pgn.scan_headers() and chess.pgn.scan_offsets() have been removed. Instead the new functions
chess.pgn.read_headers() and chess.pgn.skip_game() can be used for a similar purpose.
• chess.syzygy: Invalid magic headers now raise IOError. Previously they were only checked in an assertion.
type(board).{tbw_magic,tbz_magic,pawnless_tbw_magic,pawnless_tbz_magic} are now byte literals.
• board.status() constants (STATUS_) are now typed using enum.IntFlag. Values remain unchanged.
• chess.svg.Arrow is no longer a namedtuple.
• chess.PIECE_SYMBOLS[0] and chess.PIECE_NAMES[0] are now None instead of empty strings.
• Performance optimizations:
– chess.pgn.Game.from_board(),
– chess.square_name()
– Replace collections.deque with lists almost everywhere.
• Renamed symbols (aliases will be removed in the next release):
– chess.BB_VOID -> BB_EMPTY
– chess.bswap() -> flip_vertical()
– chess.pgn.GameNode.main_line() -> mainline_moves()
– chess.pgn.GameNode.is_main_line() -> is_mainline()
– chess.variant.BB_HILL -> chess.BB_CENTER
– chess.syzygy.open_tablebases() -> open_tablebase()
– chess.syzygy.Tablebases -> Tablebase
– chess.syzygy.Tablebase.open_directory() -> add_directory()
– chess.gaviota.open_tablebases() -> open_tablebase()
– chess.gaviota.open_tablebases_native() -> open_tablebase_native()
– chess.gaviota.NativeTablebases -> NativeTablebase
– chess.gaviota.PythonTablebases -> PythonTablebase
– chess.gaviota.NativeTablebase.open_directory() -> add_directory()
– chess.gaviota.PythonTablebase.open_directory() -> add_directory()
Bugfixes:
• The PGN parser now gives the visitor a chance to handle unknown chess variants and continue parsing.
• chess.pgn.GameNode.uci() was always raising an exception.
New features:
• chess.SquareSet now extends collections.abc.MutableSet and can be initialized from iterables.
• board.apply_transform(f) and board.transform(f) can apply bitboard transformations to a position. Examples:
chess.flip_{vertical,horizontal,diagonal,anti_diagonal}.
64 Chapter 8. Contents
python-chess, Release 0.28.1
• chess.pgn.GameNode.mainline() iterates over nodes of the mainline. Can also be used with reversed(). Reversal
is now also supported for chess.pgn.GameNode.mainline_moves().
• chess.svg.Arrow(tail, head, color=”#888”) gained an optional color argument.
• chess.pgn.BaseVisitor.parse_san(board, san) is used by parsers and can be overwritten to deal with non-standard
input formats.
• chess.pgn: Visitors can advise the parser to skip games or variations by returning the special value
chess.pgn.SKIP from begin_game(), end_headers() or begin_variation(). This is only a hint. The corresponding
end_game() or end_variation() will still be called.
• Added chess.svg.MARGIN.
Bugfixes:
• chess.SquareSet now correctly handles negative masks. Thanks @hasnul.
• chess.pgn now accepts [Variant “chess 960”] (with the space).
Changes:
• Updated Board.is_fivefold_repetition(). FIDE rules have changed and the repetition no longer needs to occur on
consecutive alternating moves. Thanks @LegionMammal978.
Bugfixes:
• chess.syzygy: Correctly initialize wide DTZ map for experimental 7 piece table KRBBPvKQ.
Bugfixes:
• Fixed ThreeCheckBoard.mirror() and CrazyhouseBoard.mirror(), which were previously resetting remaining
checks and pockets respectively. Thanks @QueensGambit.
Changes:
• Board.move_stack is now guaranteed to be UCI compatible with respect to the representation of castling moves
and board.chess960.
• Drop support for Python 3.3, which is long past end of life.
• chess.uci: The position command now manages UCI_Chess960 and UCI_Variant automatically.
• chess.uci: The position command will now always send the entire history of moves from the root position.
• Various coding style fixes and improvements. Thanks @hugovk.
New features:
• Added Board.root().
Bugfixes:
• Gaviota: Fix Python based Gaviota tablebase probing when there are multiple en passant captures. Thanks
@bjoernholzhauer.
• Syzygy: Fix DTZ for some mate in 1 positions. Similarly to the fix from v0.23.1 this is mostly cosmetic.
• Syzygy: Fix DTZ off-by-one in some 6 piece antichess positions with moves that threaten to force a capture.
This is mostly cosmetic.
Changes:
• Let uci.Engine.position() send history of at least 8 moves if available. Previously it sent only moves that were
relevant for repetition detection. This is mostly useful for Lc0. Once performance issues are solved, a future
version will always send the entire history. Thanks @SashaMN and @Mk-Chan.
• Various documentation fixes and improvements.
New features:
• Added polyglot.MemoryMappedReader.get(board, default=None).
Bugfixes:
• Atomic chess: KNvKN is not insufficient material.
• Crazyhouse: Detect insufficient material. This can not happen unless the game was started with insufficient
material.
Changes:
• Better error messages when parsing info from UCI engine fails.
• Better error message for b.set_board_fen(b.fen()).
New features:
• XBoard: Support pondering. Thanks Manik Charan.
• UCI: Support unofficial info ebf.
Bugfixes:
• Implement 16 bit DTZ mapping, which is required for some of the longest 7 piece endgames.
New features:
• XBoard: Support variant. Thanks gbtami.
66 Chapter 8. Contents
python-chess, Release 0.28.1
Bugfixes:
• XBoard: Handle multiple features and features with spaces. Thanks gbtami.
• XBoard: Ignore debug output prefixed with #. Thanks Dan Ravensloft and Manik Charan.
Bugfixes:
• Fix DTZ in case of mate in 1. This is a cosmetic fix, as the previous behavior was only off by one (which is
allowed by design).
New features:
• Experimental support for 7 piece Syzygy tablebases.
Changes:
• chess.syzygy.filenames() was renamed to tablenames() and gained an optional piece_count=6 argument.
• chess.syzygy.normalize_filename() was renamed to normalize_tablename().
• The undocumented constructors of chess.syzygy.WdlTable and chess.syzygy.DtzTable have been changed.
Bugfixes:
• In standard chess promoted pieces were incorrectly considered as distinguishable from normal pieces with regard
to position equality and threefold repetition. Thanks to kn-sq-tb for reporting.
Changes:
• The PGN game.headers are now a custom mutable mapping that validates the validity of tag names.
• Basic attack and pin methods moved to BaseBoard.
• Documentation fixes and improvements.
New features:
• Added Board.lan() for long algebraic notation.
New features:
• Added Board.mirror(), SquareSet.mirror() and bswap().
• Added chess.pgn.GameNode.accept_subgame().
• XBoard: Added resign, analyze, exit, name, rating, computer, egtpath, pause, resume. Completed option pars-
ing.
Changes:
Changes:
• len(board.legal_moves) replaced by board.legal_moves.count(). Previously list(board.legal_moves) was gen-
erating moves twice, resulting in a considerable slowdown. Thanks to Martin C. Doege for reporting.
• Dropped Python 2.6 support.
• XBoard: offer_draw renamed to draw.
New features:
• XBoard: Added DrawHandler.
Changes:
• chess.svg is now fully SVG Tiny 1.2 compatible. Removed chess.svg.DEFAULT_STYLE which would from now
on be always empty.
Bugfixes:
• Board.set_piece_at() no longer shadows optional promoted argument from BaseBoard.
• Fixed ThreeCheckBoard.is_irreversible() and ThreeCheckBoard._transposition_key().
New features:
• Added Game.without_tag_roster(). chess.pgn.StringExporter() can now handle games without any headers.
• XBoard: white, black, random, nps, otim, undo, remove. Thanks to Manik Charan.
Changes:
• Documentation fixes and tweaks by Boštjan Mejak.
• Changed unicode character for empty squares in Board.unicode().
Release yanked.
68 Chapter 8. Contents
python-chess, Release 0.28.1
Bugfixes:
• Fix arrow positioning on SVG boards.
• Documentation fixes and improvements, making most doctests runnable.
Bugfixes:
• Some XBoard commands were not returning futures.
• Support semicolon comments in PGNs.
Changes:
• Changed FEN and EPD formatting options. It is now possible to include en passant squares in FEN and X-FEN
style, or to include only strictly relevant en passant squares.
• Relax en passant square validation in Board.set_fen().
• Ensure is_en_passant(), is_capture(), is_zeroing() and is_irreversible() strictly return bools.
• Accept Z0 as a null move in PGNs.
New features:
• XBoard: Add memory, core, stop and movenow commands. Abstract post/nopost. Initial FeatureMap support.
Support usermove.
• Added Board.has_pseudo_legal_en_passant().
• Added Board.piece_map().
• Added SquareSet.carry_rippler().
• Factored out some (unstable) low level APIs: BB_CORNERS, _carry_rippler(), _edges().
New features:
• Experimental XBoard engine support. Thanks to Manik Charan and Cash Costello. Expect breaking changes
in future releases.
• Added an undocumented chess.polyglot.ZobristHasher to make Zobrist hashing easier to extend.
Bugfixes:
• Merely pseudo-legal en passant does no longer count for repetitions.
• Fixed repetition detection in Three-Check and Crazyhouse. (Previously check counters and pockets were ig-
nored.)
• Checking moves in Three-Check are now considered as irreversible by ThreeCheckBoard.is_irreversible().
• chess.Move.from_uci(“”) was raising IndexError instead of ValueError. Thanks Jonny Balls.
Changes:
• chess.syzygy.Tablebases constructor no longer supports directly opening a directory. Use
chess.syzygy.open_tablebases().
Changes:
• Support [Variant “fischerandom”] in PGNs for Cutechess compability. Thanks to Steve Maughan for reporting.
Bugfixes:
• chess.gaviota.NativeTablebases.get_dtm() and get_wdl() were missing.
Bugfixes:
• Fixed castling in atomic chess when there is a rank attack.
• The halfmove clock in Crazyhouse is no longer incremented unconditionally. Crazy-
houseBoard.is_zeroing(move) now considers pawn moves and captures as zeroing. Added
Board.is_irreversible(move) that can be used instead.
• Fixed an inconsistency where the chess.pgn tokenizer accepts long algebraic notation but Board.parse_san() did
not.
Changes:
• Added more NAG constants in chess.pgn.
Bugfixes:
• Crazyhouse drops were accepted as pseudo legal (and legal) even if the respective piece was not in the pocket.
• CrazyhouseBoard.pop() was failing to undo en passant moves.
• CrazyhouseBoard.pop() was always returning None.
70 Chapter 8. Contents
python-chess, Release 0.28.1
Bugfixes:
• Fixed Board.parse_uci() for crazyhouse drops. Thanks to Ryan Delaney.
• Fixed AtomicBoard.is_insufficient_material().
• Fixed signature of SuicideBoard.was_into_check().
• Explicitly close input and output streams when a chess.uci.PopenProcess terminates.
• The documentation of Board.attackers() was wrongly stating that en passant capturable pawns are considered
attacked.
Changes:
• chess.SquareSet is no longer hashable (since it is mutable).
• Removed functions and constants deprecated in v0.17.0.
• Dropped gmpy2 and gmpy as optional dependencies. They were no longer improving performance.
• Various tweaks and optimizations for 5% improvement in PGN parsing and perft speed. (Signature of _is_safe
and _ep_skewered changed).
• Rewritten chess.svg.board() using xml.etree. No longer supports pre and post. Use an XML parser if you need
to modify the SVG. Now only inserts actually used piece defintions.
• Untangled UCI process and engine instanciation, changing signatures of constructors and allowing arbitrary
arguments to subprocess.Popen.
• Coding style and documentation improvements.
New features:
• chess.svg.board() now supports arrows. Thanks to @rheber for implementing this feature.
• Let chess.uci.PopenEngine consistently handle Ctrl+C across platforms and Python versions.
chess.uci.popen_engine() now supports a setpgrp keyword argument to start the engine process in a new
process group. Thanks to @dubiousjim.
• Added board.king(color) to find the (royal) king of a given side.
• SVGs now have viewBox and chess.svg.board(size=None) supports and defaults to None (i.e. scaling to the size
of the container).
Changes:
• Rewritten move generator, various performance tweaks, code simplications (500 lines removed) amounting to
doubled PGN parsing and perft speed.
Changes:
• board.move_stack now contains the exact move objects added with Board.push() (instead of normalized copies
for castling moves). This ensures they can be used with Board.variation_san() amongst others.
• board.ep_square is now None instead of 0 for no en passant square.
• chess.svg: Better vector graphics for knights. Thanks to ProgramFox.
• Documentation improvements.
Bugfixes:
• Explosions in atomic chess were not destroying castling rights. Thanks to ProgramFOX for finding this issue.
72 Chapter 8. Contents
python-chess, Release 0.28.1
Bugfixes:
• pin_mask(), pin() and is_pinned() make more sense when already in check. Thanks to Ferdinand Mosca.
New features:
• Variant support: Suicide, Giveaway, Atomic, King of the Hill, Racing Kings, Horde, Three-check, Crazy-
house. chess.Move now supports drops.
• More fine grained dependencies. Use pip install python-chess[uci,gaviota] to install dependencies for the full
feature set.
• Added chess.STATUS_EMPTY and chess.STATUS_ILLEGAL_CHECK.
• The board.promoted mask keeps track of promoted pieces.
• Optionally copy boards without the move stack: board.copy(stack=False).
• examples/bratko_kopec now supports avoid move (am), variants and displays fractional scores immidiately.
Thanks to Daniel Dugovic.
• perft.py rewritten with multi-threading support and moved to examples/perft.
• chess.syzygy.dependencies(), chess.syzygy.all_dependencies() to generate Syzygy tablebase dependencies.
Changes:
• Endgame tablebase probing (Syzygy, Gaviota): probe_wdl() , probe_dtz() and probe_dtm() now raise Key-
Error or MissingTableError instead of returning None. If you prefer getting None in case of an error use
get_wdl(), get_dtz() and get_dtm().
• chess.pgn.BaseVisitor.result() returns True by default and is no longer used by chess.pgn.read_game() if no
game was found.
• Non-fast-forward update of the Git repository to reduce size (old binary test assets removed).
• board.pop() now uses a boardstate stack to undo moves.
• uci.engine.position() will send the move history only until the latest zeroing move.
• Optimize board.clean_castling_rights() and micro-optimizations improving PGN parser performance by around
20%.
• Syzygy tables now directly use the endgame name as hash keys.
• Improve test performance (especially on Travis CI).
• Documentation updates and improvements.
New features:
• Highlight last move and checks when rendering board SVGs.
Bugfixes:
• pgn.Game.errors was not populated as documented. Thanks to Ryan Delaney for reporting.
New features:
• Added pgn.GameNode.add_line() and pgn.GameNode.main_line() which make it easier to work with lists of
moves as variations.
Bugfixes:
• Fix a bug where shift_right() and shift_2_right() were producing integers larger than 64bit when shifting squares
off the board. This is very similar to the bug fixed in v0.15.1. Thanks to piccoloprogrammatore for reporting.
Bugfixes:
• Fix a bug where shift_up_right() and shift_up_left() were producing integers larger than 64bit when shifting
squares off the board.
New features:
• Replaced __html__ with experimental SVG rendering for IPython.
Changes:
• chess.uci.Score no longer has upperbound and lowerbound attributes. Previously these were always False.
• Significant improvements of move generation speed, around 2.3x faster PGN parsing. Removed the follow-
ing internal attributes and methods of the Board class: attacks_valid, attacks_to, attacks_from, _pinned(), at-
tacks_valid_stack, attacks_from_stack, attacks_to_stack, generate_attacks().
• UCI: Do not send isready directly after go. Though allowed by the UCI protocol specification it is just not
nescessary and many engines were having trouble with this.
• Polyglot: Use less memory for uniform random choices from big opening books (reservoir sampling).
• Documentation improvements.
Bugfixes:
• Allow underscores in PGN header tags. Found and fixed by Bajusz Tamás.
New features:
• Added Board.chess960_pos() to identify the Chess960 starting position number of positions.
• Added chess.BB_BACKRANKS and chess.BB_PAWN_ATTACKS.
Bugfixes:
• Backport Bugfix for Syzygy DTZ related to en-passant. See official-stockfish/Stockfish@6e2ca97d93812b2.
Changes:
• Added optional argument max_fds=128 to chess.syzygy.open_tablebases(). An LRU cache is used to keep at
most max_fds files open. This allows using many tables without running out of file descriptors. Previously all
tables were opened at once.
74 Chapter 8. Contents
python-chess, Release 0.28.1
• Syzygy and Gaviota now store absolute tablebase paths, in case you change the working directory of the process.
• The default implementation of chess.uci.InfoHandler.score() will no longer store score bounds in info[“score”],
only real scores.
• Added Board.set_chess960_pos().
• Documentation improvements.
Changes:
• Board.attacker_mask() has been renamed to Board.attackers_mask() for consistency.
• The signature of Board.generate_legal_moves() and Board.generate_pseudo_legal_moves() has been
changed. Previously it was possible to select piece types for selective move generation:
Board.generate_legal_moves(castling=True, pawns=True, knights=True, bishops=True, rooks=True,
queens=True, king=True)
Now it is possible to select arbitrary sets of origin and target squares. to_mask uses the corresponding rook
squares for castling moves.
Board.generate_legal_moves(from_mask=BB_ALL, to_mask=BB)
To generate all knight and queen moves do:
board.generate_legal_moves(board.knights | board.queens)
To generate only castling moves use:
Board.generate_castling_moves(from_mask=BB_ALL, to_mask=BB_ALL)
• Additional hardening has been added on top of the bugfix from v0.13.3. Diagonal skewers on the last double
pawn move are now handled correctly, even though such positions can not be reached with a sequence of legal
moves.
• chess.syzygy now uses the more efficient selective move generation.
New features:
• The following move generation methods have been added: Board.generate_pseudo_legal_ep(from_mask=BB_ALL,
to_mask=BB_ALL), Board.generate_legal_ep(from_mask=BB_ALL, to_mask=BB_ALL),
Board.generate_pseudo_legal_captures(from_mask=BB_ALL, to_mask=BB_ALL),
Board.generate_legal_captures(from_mask=BB_ALL, to_mask=BB_ALL).
This is a bugfix release for a move generation bug. Other than the bugfix itself there are only minimal fully back-
wardscompatible changes. You should update immediately.
Bugfixes:
• When capturing en passant, both the capturer and the captured pawn disappear from the fourth or fifth rank. If
those pawns were covering a horizontal attack on the king, then capturing en passant should not have been legal.
Board.generate_legal_moves() and Board.is_into_check() have been fixed.
The same principle applies for diagonal skewers, but nothing has been done in this release: If the last double
pawn move covers a diagonal attack, then the king would have already been in check.
v0.14.0 adds additional hardening for all cases. It is recommended you upgrade to v0.14.0 as soon as you can
deal with the non-backwards compatible changes.
Changes:
• chess.uci now uses subprocess32 if applicable (and available). Additionally a lock is used to work around a race
condition in Python 2, that can occur when spawning engines from multiple threads at the same time.
• Consistently handle tabs in UCI engine output.
Changes:
• chess.syzygy.open_tablebases() now raises if the given directory does not exist.
• Allow visitors to handle invalid FEN tags in PGNs.
• Gaviota tablebase probing fails faster for piece counts > 5.
Minor new features:
• Added chess.pgn.Game.from_board().
Changes:
• Missing SetUp tags in PGNs are ignored.
• Incompatible comparisons on chess.Piece, chess.Move, chess.Board and chess.SquareSet now return NotImple-
mented instead of False.
Minor new features:
• Factored out basic board operations to chess.BaseBoard. This is inherited by chess.Board and extended with the
usual move generation features.
• Added optional claim_draw argument to chess.Base.is_game_over().
• Added chess.Board.result(claim_draw=False).
• Allow chess.Board.set_piece_at(square, None).
• Added chess.SquareSet.from_square(square).
• chess.pgn.Game.export() and chess.pgn.GameNode.export() have been removed and replaced with a new visitor
concept.
• chess.pgn.read_game() no longer takes an error_handler argument. Errors are now logged. Use the new visitor
concept to change this behaviour.
Bugfixes:
• Context manager support for pure Python Gaviota probing code. Various documentation fixes for Gaviota
probing. Thanks to Jürgen Précour for reporting.
76 Chapter 8. Contents
python-chess, Release 0.28.1
• PGN variation start comments for variations on the very first move were assigned to the game. Thanks to Norbert
Räcke for reporting.
Bugfixes:
• Another en passant related Bugfix for pure Python Gaviota tablebase probing.
New features:
• Added pgn.GameNode.is_end().
Changes:
• Big speedup for pgn module. Boards are cached less agressively. Board move stacks are copied faster.
• Added tox.ini to specify test suite and flake8 options.
Bugfixes:
• Some invalid castling rights were silently ignored by Board.set_fen(). Now it is ensured information is stored
for retrieval using Board.status().
Bugfixes:
• Some Gaviota probe results were incorrect for positions where black could capture en passant.
Changes:
• Robust handling of invalid castling rights. You can also use the new method Board.clean_castling_rights() to
get the subset of strictly valid castling rights.
New features:
• Python 2.6 support. Patch by vdbergh.
• Pure Python Gaviota tablebase probing. Thanks to Jean-Noël Avila.
Bugfixes:
• syzygy.Tablebases.probe_dtz() has was giving wrong results for some positions with possible en passant captur-
ing. This was found and fixed upstream: https://github.com/official-stockfish/Stockfish/issues/394.
• Ignore extra spaces in UCI info lines, as for example sent by the Hakkapeliitta engine. Thanks to Jürgen Précour
for reporting.
Changes:
• Chess960 support and the representation of castling moves has been changed.
The constructor of board has a new chess960 argument, defaulting to False: Board(fen=STARTING_FEN,
chess960=False). That property is available as Board.chess960.
In Chess960 mode the behaviour is as in the previous release. Castling moves are represented as a king move to
the corresponding rook square.
In the default standard chess mode castling moves are represented with the standard UCI notation, e.g. e1g1 for
king-side castling.
Board.uci(move, chess960=None) creates UCI representations for moves. Unlike Move.uci() it can convert them
in the context of the current position.
Board.has_chess960_castling_rights() has been added to test for castling rights that are impossible in standard
chess.
The modules chess.polyglot, chess.pgn and chess.uci will transparently handle both modes.
• In a previous release Board.fen() has been changed to only display an en passant square if a legal en passant
move is indeed possible. This has now also been adapted for Board.shredder_fen() and Board.epd().
New features:
• Get individual FEN components: Board.board_fen(), Board.castling_xfen(), Board.castling_shredder_fen().
• Use Board.has_legal_en_passant() to test if a position has a legal en passant move.
• Make repr(board.legal_moves) human readable.
Bugfixes:
• Fix use-after-free in Gaviota tablebase initialization.
New dependencies:
• If you are using Python < 3.2 you have to install futures in order to use the chess.uci module.
Changes:
• There are big changes in the UCI module. Most notably in async mode multiple commands can be executed at
the same time (e.g. go infinite and then stop or go ponder and then ponderhit).
go infinite and go ponder will now wait for a result, i.e. you may have to call stop or ponderhit from a different
thread or run the commands asynchronously.
stop and ponderhit no longer have a result.
78 Chapter 8. Contents
python-chess, Release 0.28.1
• The values of the color constants chess.WHITE and chess.BLACK have been changed. Previously WHITE was
0, BLACK was 1. Now WHITE is True, BLACK is False. The recommended way to invert color is using not
color.
• The pseudo piece type chess.NONE has been removed in favor of just using None.
• Changed the Board(fen) constructor. If the optional fen argument is not given behavior did not change. However
if None is passed explicitly an empty board is created. Previously the starting position would have been set up.
• Board.fen() will now only show completely legal en passant squares.
• Board.set_piece_at() and Board.remove_piece_at() will now clear the move stack, because the old moves may
not be valid in the changed position.
• Board.parse_uci() and Board.push_uci() will now accept null moves.
• Changed shebangs from #!/usr/bin/python to #!/usr/bin/env python for better virtualenv support.
• Removed unused game data files from repository.
Bugfixes:
• PGN: Prefer the game result from the game termination marker over * in the header. These should be identical
in standard compliant PGNs. Thanks to Skyler Dawson for reporting this.
• Polyglot: minimum_weight for find(), find_all() and choice() was not respected.
• Polyglot: Negative indexing of opening books was raising IndexError.
• Various documentation fixes and improvements.
New features:
• Experimental probing of Gaviota tablebases via libgtb.
• New methods to construct boards:
>>> chess.Board.empty()
Board('8/8/8/8/8/8/8/8 w - - 0 1')
>>> board
Board('4k3/8/8/8/8/8/8/4K3 b - - 13 17')
>>> ops
{'fmvn': 17, 'hmvc': 13}
• Added Board.copy() and hooks to let the copy module to the right thing.
• Added Board.has_castling_rights(color), Board.has_kingside_castling_rights(color) and
Board.has_queenside_castling_rights(color).
• Added Board.clear_stack().
• Support common set operations on chess.SquareSet().
Bugfixes:
• UCI module could not handle castling ponder moves. Thanks to Marco Belli for reporting.
• The initial move number in PGNs was missing, if black was to move in the starting position. Thanks to Jürgen
Précour for reporting.
• Detect more impossible en passant squares in Board.status(). There already was a requirement for a pawn on
the fifth rank. Now the sixth and seventh rank must be empty, additionally. We do not do further retrograde
analysis, because these are the only cases affecting move generation.
Bugfixes:
• The initial move number in PGNs was missing, if black was to move in the starting position. Thanks to Jürgen
Précour for reporting.
• Detect more impossible en passant squares in Board.status(). There already was a requirement for a pawn on
the fifth rank. Now the sixth and seventh rank must be empty, additionally. We do not do further retrograde
analysis, because these are the only cases affecting move generation.
This is a big update with quite a few breaking changes. Carefully review the changes before upgrading. It’s no
problem if you can not update right now. The 0.8.x branch still gets bugfixes.
Incompatible changes:
• Removed castling right constants. Castling rights are now represented as a bitmask of the rook square. For
example:
Castling moves were previously encoded as the corresponding king movement in UCI, e.g. e1f1 for white king-
side castling. Now castling moves are encoded as a move to the corresponding rook square (UCI_Chess960-
style), e.g. e1a1.
You may use the new methods Board.uci(move, chess960=True), Board.parse_uci(uci) and Board.push_uci(uci)
to handle this transparently.
The uci module takes care of converting moves when communicating with an engine that is not in UCI_Chess960
mode.
• The get_entries_for_position(board) method of polyglot opening book readers has been changed to
find_all(board, minimum_weight=1). By default entries with weight 0 are excluded.
• The Board.pieces lookup list has been removed.
• In 0.8.1 the spelling of repetition (was repitition) was fixed. can_claim_threefold_repetition() and
is_fivefold_repetition() are the affected method names. Aliases are now removed.
• Board.set_epd() will now interpret bm, am as a list of moves for the current position and pv as a variation
(represented by a list of moves). Thanks to Jordan Bray for reporting this.
• Removed uci.InfoHandler.pre_bestmove() and uci.InfoHandler.post_bestmove().
• uci.InfoHandler().info[“score”] is now relative to multipv. Use
80 Chapter 8. Contents
python-chess, Release 0.28.1
• Clear uci.InfoHandler() dictionary at the start of new searches (new on_go()), not at the end of searches.
• Renamed PseudoLegalMoveGenerator.bitboard and LegalMoveGenerator.bitboard to PseudoLegalMoveGen-
erator.board and LegalMoveGenerator.board, respectively.
• Scripts removed.
• Python 3.2 compability dropped. Use Python 3.3 or higher. Python 2.7 support is not affected.
New features:
• Introduced Chess960 support. Board(fen) and Board.set_fen(fen) now support X-FENs. Added
Board.shredder_fen(). Board.status(allow_chess960=True) has an optional argument allowing to insist on stan-
dard chess castling rules. Added Board.is_valid(allow_chess960=True).
• Improved move generation using Shatranj-style direct lookup. Removed rotated bitboards. Perft speed has
been more than doubled.
• Added choice(board) and weighted_choice(board) for polyglot opening book readers.
• Added Board.attacks(square) to determine attacks from a given square. There already was
Board.attackers(color, square) returning attacks to a square.
• Added Board.is_en_passant(move), Board.is_capture(move) and Board.is_castling(move).
• Added Board.pin(color, square) and Board.is_pinned(color, square).
• There is a new method Board.pieces(piece_type, color) to get a set of squares with the specified pieces.
• Do expensive Syzygy table initialization on demand.
• Allow promotions like e8Q (usually e8=Q) in Board.parse_san() and PGN files.
• Patch by Richard C. Gerkin: Added Board.__unicode__() just like Board.__str__() but with unicode pieces.
• Patch by Richard C. Gerkin: Added Board.__html__().
Bugfixes:
• pgn.Game.setup() with the standard starting position was failing when the standard starting position was already
set. Thanks to Jordan Bray for reporting this.
Optimizations:
• Remove bswap() from Syzygy decompression hot path. Directly read integers with the correct endianness.
• Fixed pondering mode in uci module. For example ponderhit() was blocking indefinitely. Thanks to Valeriy
Huz for reporting this.
• Patch by Richard C. Gerkin: Moved searchmoves to the end of the UCI go command, where it will not cause
other command parameters to be ignored.
• Added missing check or checkmate suffix to castling SANs, e.g. O-O-O#.
• Fixed off-by-one error in polyglot opening book binary search. This would not have caused problems for real
opening books.
• Fixed Python 3 support for reverse polyglot opening book iteration.
• Bestmoves may be literally (none) in UCI protocol, for example in checkmate positions. Fix parser and return
None as the bestmove in this case.
• Fixed spelling of repetition (was repitition). can_claim_threefold_repetition() and is_fivefold_repetition() are
the affected method names. Aliases are there for now, but will be removed in the next release. Thanks to Jimmy
Patrick for reporting this.
• Added SquareSet.__reversed__().
• Use containerized tests on Travis CI, test against Stockfish 6, improved test coverage amd various minor clean-
ups.
• Implement Syzygy endgame tablebase probing. https://syzygy-tables.info is an example project that provides
a public API using the new features.
• The interface for aynchronous UCI command has changed to mimic concurrent.futures. is_done() is now just
done(). Callbacks will receive the command object as a single argument instead of the result. The result property
and wait() have been removed in favor of a synchronously waiting result() method.
• The result of the stop and go UCI commands are now named tuples (instead of just normal tuples).
• Add alias Board for Bitboard.
• Fixed race condition during UCI engine startup. Lines received during engine startup sometimes needed to be
processed before the Engine object was fully initialized.
• If there are comments in a game before the first move, these are now assigned to Game.comment instead of
Game.starting_comment. Game.starting_comment is ignored from now on. Game.starts_variation() is no
longer true. The first child node of a game can no longer have a starting comment. It is possible to have a
game with Game.comment set, that is otherwise completely empty.
• Fix export of games with variations. Previously the moves were exported in an unusual (i.e. wrong) order.
• Install gmpy2 or gmpy if you want to use slightly faster binary operations.
82 Chapter 8. Contents
python-chess, Release 0.28.1
• PGN parsing is now more robust: read_game() ignores invalid tokens. Still exceptions are going to be thrown
on illegal or ambiguous moves, but this behaviour can be changed by passing an error_handler argument.
>>> # Raises ValueError:
>>> game = chess.pgn.read_game(file_with_illegal_moves)
1. e4 e5 2. Nf3 Nf6 *
would have ended directly after the starting comment. To avoid this, the parser will now look ahead until it finds
at least one move or a termination marker like *, 1-0, 1/2-1/2 or 0-1.
• Introduce a new function scan_headers() to quickly scan a PGN file for headers without having to parse the full
games.
• Minor testcoverage improvements.
• Fix bug where pawn_moves_from() and consequently is_legal() weren’t handling en passant correctly. Thanks
to Norbert Naskov for reporting.
• Fix is_fivefold_repitition(): The new fivefold repetition rule requires the repetitions to occur on alternating
consecutive moves.
• Minor testing related improvements: Close PGN files, allow running via setuptools.
• Add recently introduced features to README.
>>> print(chess.Bitboard())
r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R
84 Chapter 8. Contents
python-chess, Release 0.28.1
Apply the lessons learned from the previous releases, redesign the API and implement it in pure Python.
Implement the basics in C++ and provide bindings for Python. Obviously performance was a lot better - but at the
expense of having to compile code for the target platform.
First experiments with a way too slow pure Python API, creating way too many objects for basic operations.
86 Chapter 8. Contents
CHAPTER 9
• genindex
• search
87
python-chess, Release 0.28.1
89
python-chess, Release 0.28.1
90 Index
python-chess, Release 0.28.1
Index 91
python-chess, Release 0.28.1
92 Index
python-chess, Release 0.28.1
V
var (chess.engine.Option attribute), 55
variation() (chess.pgn.GameNode method), 38
variation_san() (chess.Board method), 28
variations (chess.pgn.GameNode attribute), 37
visit_board() (chess.pgn.BaseVisitor method), 39
visit_comment() (chess.pgn.BaseVisitor method),
39
visit_header() (chess.pgn.BaseVisitor method), 39
visit_move() (chess.pgn.BaseVisitor method), 39
visit_nag() (chess.pgn.BaseVisitor method), 39
visit_result() (chess.pgn.BaseVisitor method), 39
W
wait() (chess.engine.AnalysisResult method), 53
was_into_check() (chess.Board method), 25
weight (chess.polyglot.Entry attribute), 43
weighted_choice()
(chess.polyglot.MemoryMappedReader
method), 43
white() (chess.engine.PovScore method), 51
white_clock (chess.engine.Limit attribute), 49
white_inc (chess.engine.Limit attribute), 49
without_tag_roster() (chess.pgn.Game class
method), 37
X
XBoardProtocol (class in chess.engine), 57
Z
zobrist_hash() (in module chess.polyglot), 43
Index 93