Changelog for python-chess

This project is pretty young and maturing only slowly. At the current stage it is more important to get things right, than to be consistent with previous versions. Use this changelog to see what changed in a new release, because this might include API breaking changes.

New in v0.11.1


  • syzygy.Tablebases.probe_dtz() has was giving wrong results for some positions with possible en passant capturing. This was found and fixed upstream:
  • Ignore extra spaces in UCI info lines, as for example sent by the Hakkapeliitta engine. Thanks to Jürgen Précour for reporting

New in v0.11.0


  • 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.

New in v0.10.1


  • Fix use-after-free in Gaviota tablebase initialization.

New in v0.10.0

New dependencies:

  • If you are using Python < 3.2 you have to install futures in order to use the chess.uci module.


  • 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.

  • 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.


  • 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, ops = chess.Board.from_epd("4k3/8/8/8/8/8/8/4K3 b - - fmvn 17; hmvc 13")
    >>> 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().

New in v0.9.1


  • 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.

New in v0.8.3


  • 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.

New in v0.9.0

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:

    >>> board = chess.Board()
    >>> # Standard castling rights.
    >>> board.castling_rights == chess.BB_A1 | chess.BB_H1 | chess.BB_A8 | chess.BB_H8
    >>> # Check for the presence of a specific castling right.
    >>> can_white_castle_queenside = chess.BB_A1 & board.castling_rights

    Castling moves were previously encoded as the corresponding king movement in UCI, e.g. e1f1 for white kingside 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

    >>> with info_handler as info:
    ...     if 1 in info["score"]:
    ...         cp = info["score"][1].cp

    where you were previously using

    >>> with info_handler as info:
    ...     if "score" in info:
    ...         cp = info["score"].cp
  • 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 PseudoLegalMoveGenerator.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 standard 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, 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__().

New in v0.8.2


  • 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.


  • Remove bswap() from Syzygy decompression hot path. Directly read integers with the correct endianness.

New in v0.8.1

  • 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.

New in v0.8.0

  • Implement Syzygy endgame tablebase probing. 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.

New in v0.7.0

  • Implement UCI engine communication.
  • Patch by Matthew Lai: Add caching for gameNode.board().

New in v0.6.0

  • 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.
  • Ignore superfluous variation opening brackets in PGN files.
  • Add GameNode.san().
  • Remove sparse_pop_count(). Just use pop_count().
  • Remove next_bit(). Now use bit_scan().

New in v0.5.0

  • 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)
    >>> # Silently ignores errors and continues parsing:
    >>> game = chess.pgn.read_game(file_with_illegal_moves, None)
    >>> # Logs the error, continues parsing:
    >>> game = chess.pgn.read_game(file_with_illegal_moves, logger.exception)

    If there are too many closing brackets this is now ignored.

    Castling moves like 0-0 (with zeros) are now accepted in PGNs. The Bitboard.parse_san() method remains strict as always, though.

    Previously the parser was strictly following the PGN spefification in that empty lines terminate a game. So a game like

    [Event "?"]
    { Starting comment block }
    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.

New in v0.4.2

  • Fix bug where pawn_moves_from() and consequently is_legal() weren’t handling en passant correctly. Thanks to Norbert Naskov for reporting.

New in v0.4.1

  • 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.

New in v0.4.0

  • Introduce can_claim_draw(), can_claim_fifty_moves() and can_claim_threefold_repitition().
  • Since the first of July 2014 a game is also over (even without claim by one of the players) if there were 75 moves without a pawn move or capture or a fivefold repetition. Let is_game_over() respect that. Introduce is_seventyfive_moves() and is_fivefold_repitition(). Other means of ending a game take precedence.
  • Threefold repetition checking requires efficient hashing of positions to build the table. So performance improvements were needed there. The default polyglot compatible zobrist hashes are now built incrementally.
  • Fix low level rotation operations l90(), l45() and r45(). There was no problem in core because correct versions of the functions were inlined.
  • Fix equality and inequality operators for Bitboard, Move and Piece. Also make them robust against comparisons with incompatible types.
  • Provide equality and inequality operators for SquareSet and polyglot.Entry.
  • Fix return values of incremental arithmetical operations for SquareSet.
  • Make polyglot.Entry a collections.namedtuple.
  • Determine and improve test coverage.
  • Minor coding style fixes.

New in v0.3.1

  • Bitboard.status() now correctly detects STATUS_INVALID_EP_SQUARE, instead of errors or false reports.
  • Polyglot opening book reader now correctly handles knight underpromotions.
  • Minor coding style fixes, including removal of unused imports.

New in v0.3.0

  • Rename property half_moves of Bitboard to halfmove_clock.

  • Rename property ply of Bitboard to fullmove_number.

  • Let PGN parser handle symbols like !, ?, !? and so on by converting them to NAGs.

  • Add a human readable string representation for Bitboards.

    >>> 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
  • Various documentation improvements.

New in v0.2.0

  • Implement PGN parsing and writing.
  • Hugely improve test coverage and use Travis CI for continuous integration and testing.
  • Create an API documentation.
  • Improve Polyglot opening-book handling.

New in v0.1.0

Apply the lessons learned from the previous releases, redesign the API and implement it in pure Python.

New in v0.0.4

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.

Pre v0.0.4

First experiments with a way too slow pure Python API, creating way too many objects for basic operations.