python-chess: a pure Python chess library
=========================================

.. image:: https://travis-ci.org/niklasf/python-chess.svg?branch=master
    :target: https://travis-ci.org/niklasf/python-chess

.. image:: https://coveralls.io/repos/niklasf/python-chess/badge.png
    :target: https://coveralls.io/r/niklasf/python-chess

.. image:: https://landscape.io/github/niklasf/python-chess/master/landscape.png
    :target: https://landscape.io/github/niklasf/python-chess/master

.. image:: https://badge.fury.io/py/python-chess.svg
    :target: https://pypi.python.org/pypi/python-chess

.. image:: https://readthedocs.org/projects/python-chess/badge/?version=latest
    :target: https://python-chess.readthedocs.org/en/latest/

Introduction
------------

python-chess is a pure Python chess library with move generation and validation
and handling of common formats. This is the scholars mate in python-chess:

.. code:: python

    >>> import chess

    >>> board = chess.Board()

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

Documentation
-------------

https://python-chess.readthedocs.org/en/latest/


* `Core <https://python-chess.readthedocs.org/en/latest/core.html>`_
* `PGN parsing and writing <https://python-chess.readthedocs.org/en/latest/pgn.html>`_
* `Polyglot opening book reading <https://python-chess.readthedocs.org/en/latest/polyglot.html>`_
* `Syzygy endgame tablebase probing <https://python-chess.readthedocs.org/en/latest/syzygy.html>`_
* `Gaviota endgame tablebase probing <https://python-chess.readthedocs.org/en/latest/gaviota.html>`_
* `UCI engine communication <https://python-chess.readthedocs.org/en/latest/uci.html>`_
* `Changelog <https://python-chess.readthedocs.org/en/latest/changelog.html>`_

Features
--------

* Supports Python 2.7 and Python 3.3+.

  .. code:: python

      >>> # Python 2 compability for the following examples.
      >>> from __future__ import print_function

* Supports standard chess and Chess960.

* Legal move generator and move validation. This includes all castling
  rules and en-passant captures.

  .. code:: python

      >>> chess.Move.from_uci("a8a1") in board.legal_moves
      False

* Make and unmake moves.

  .. code:: python

      >>> Qf7 = board.pop() # Unmake last move (Qf7#)
      >>> Qf7
      Move.from_uci('h5f7')

      >>> board.push(Qf7) # Restore

* Show a simple ASCII board.

  .. code:: python

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

* Detects checkmates, stalemates and draws by insufficient material.

  .. code:: python

      >>> board.is_stalemate()
      False
      >>> board.is_insufficient_material()
      False
      >>> board.is_game_over()
      True
      >>> board.halfmove_clock
      0

* Detects repetitions. Has a half move clock.

  .. code:: python

      >>> 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 drawn (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.

  .. code:: python

      >>> board.is_fivefold_repetition()
      False
      >>> board.is_seventyfive_moves()
      False

* Detects checks and attacks.

  .. code:: python

      >>> board.is_check()
      True
      >>> board.is_attacked_by(chess.WHITE, chess.E8)
      True

      >>> attackers = board.attackers(chess.WHITE, chess.F3)
      >>> attackers
      SquareSet(0b100000001000000)
      >>> chess.G2 in attackers
      True
      >>> print(attackers)
      . . . . . . . .
      . . . . . . . .
      . . . . . . . .
      . . . . . . . .
      . . . . . . . .
      . . . . . . . .
      . . . . . . 1 .
      . . . . . . 1 .

* Detects absolute pins and their directions.

  .. code:: python

      >>> board.is_pinned(chess.BLACK, chess.E8)
      True
      >>> pin = board.pin(chess.BLACK, chess.E8)
      >>> pin
      SquareSet(0b1000000100000010000001000000000000000000000000000000000000000)
      >>> print(pin)
      . . . . 1 . . .
      . . . . . 1 . .
      . . . . . . 1 .
      . . . . . . . 1
      . . . . . . . .
      . . . . . . . .
      . . . . . . . .
      . . . . . . . .

* Parses and creates SAN representation of moves.

  .. code:: python

      >>> board = chess.Board()
      >>> board.san(chess.Move(chess.E2, chess.E4))
      'e4'

* Parses and creates FENs, extended FENs and Shredder FENs.

  .. code:: python

      >>> board.fen()
      'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
      >>> board.shredder_fen()
      'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w HAha - 0 1'
      >>> board = chess.Board("8/8/8/2k5/4K3/8/8/8 w - - 4 45")
      >>> board.piece_at(chess.C5)
      Piece.from_symbol('k')

* Parses and creates EPDs.

  .. code:: python

      >>> board = chess.Board()
      >>> board.epd(bm=board.parse_uci("d2d4"))
      'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - bm d4;'

      >>> ops = board.set_epd("1k1r4/pp1b1R2/3q2pp/4p3/2B5/4Q3/PPP2B2/2K5 b - - bm Qd1+; id \"BK.01\";")
      >>> ops == {'bm': [chess.Move.from_uci('d6d1')], 'id': 'BK.01'}
      True

* Read Polyglot opening books.
  `Docs <https://python-chess.readthedocs.org/en/latest/polyglot.html>`_.

  .. code:: python

      >>> import chess.polyglot

      >>> book = chess.polyglot.open_reader("data/opening-books/performance.bin")

      >>> board = chess.Board()
      >>> main_entry = book.find(board)
      >>> main_entry.move()
      Move.from_uci('e2e4')
      >>> main_entry.weight
      1
      >>> main_entry.learn
      0

      >>> book.close()

* Read and write PGNs. Supports headers, comments, NAGs and a tree of
  variations.
  `Docs <https://python-chess.readthedocs.org/en/latest/pgn.html>`_.

  .. code:: python

      >>> import chess.pgn

      >>> pgn = open("data/games/molinari-bordais-1979.pgn")
      >>> first_game = chess.pgn.read_game(pgn)
      >>> pgn.close()

      >>> first_game.headers["White"]
      'Molinari'
      >>> first_game.headers["Black"]
      'Bordais'

      >>> # Iterate through the mainline of this embarrasingly short game.
      >>> node = first_game
      >>> while node.variations:
      ...     next_node = node.variation(0)
      ...     print(node.board().san(next_node.move))
      ...     node = next_node
      e4
      c5
      c4
      Nc6
      Ne2
      Nf6
      Nbc3
      Nb4
      g3
      Nd3#

      >>> first_game.headers["Result"]
      '0-1'

* Probe Syzygy endgame tablebases (DTZ, WDL).
  `Docs <https://python-chess.readthedocs.org/en/latest/syzygy.html>`_.

  .. code:: python

      >>> import chess.syzygy

      >>> tablebases = chess.syzygy.open_tablebases("data/syzygy")

      >>> # Black to move is losing in 53 half moves (distance to zero) in this
      >>> # KNBvK endgame.
      >>> board = chess.Board("8/2K5/4B3/3N4/8/8/4k3/8 b - - 0 1")
      >>> tablebases.probe_dtz(board)
      -53

      >>> tablebases.close()

* Probe Gaviota endgame tablebases (DTM, WDL) via a wrapper around
  `libgtb <https://github.com/michiguel/Gaviota-Tablebases>`_.
  `Docs <https://python-chess.readthedocs.org/en/latest/gaviota.html>`_.

  .. code:: python

      >>> import chess.gaviota

      >>> tablebases = chess.gaviota.open_tablebases("data/gaviota")

      >>> # White to move mates in 31 half moves in this KRvK endgame.
      >>> board = chess.Board("8/8/8/8/4k3/8/6R1/7K w - - 0 1")
      >>> tablebases.probe_dtm(board)
      31

      >>> tablebases.close()

* Communicate with an UCI engine.
  `Docs <https://python-chess.readthedocs.org/en/latest/uci.html>`_.

  .. code:: python

      >>> import chess.uci
      >>> import time

      >>> engine = chess.uci.popen_engine("stockfish")
      >>> engine.uci()
      >>> engine.author
      'Tord Romstad, Marco Costalba and Joona Kiiski'

      >>> # Synchronous mode.
      >>> board = chess.Board("1k1r4/pp1b1R2/3q2pp/4p3/2B5/4Q3/PPP2B2/2K5 b - - 0 1")
      >>> engine.position(board)
      >>> engine.go(movetime=2000) # Gets tuple of bestmove and ponder move.
      BestMove(bestmove=Move.from_uci('d6d1'), ponder=Move.from_uci('c1d1'))

      >>> # Asynchronous mode.
      >>> def callback(command):
      ...    bestmove, ponder = command.result()
      ...    assert bestmove == chess.Move.from_uci('d6d1')
      ...
      >>> command = engine.go(movetime=2000, async_callback=callback)
      >>> command.done()
      False
      >>> command.result()
      BestMove(bestmove=Move.from_uci('d6d1'), ponder=Move.from_uci('c1d1'))
      >>> command.done()
      True

      >>> # Quit.
      >>> engine.quit()
      0

Peformance
----------

python-chess is not intended to be used by serious chess engines where
performance is critical. The goal is rather to create a simple and relatively
highlevel library.

You can install the `gmpy2` or `gmpy` (https://pypi.python.org/pypi/gmpy2)
modules in order to get a slight performance boost on basic operations like
bitscans and population counts.

python-chess only imports very basic general (non-chess-related)
operations from native libraries. All logic is pure Python. There will always
be pure Python fallbacks.

Installing
----------

* With pip:

  ::

      pip install futures # Backport for Python < 3.2
      pip install python-chess

* From current source code:

  ::

      python setup.py sdist
      python setup.py install

Featured projects
-----------------

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/
* a cross platform chess GUI - https://asdfjkl.github.io/jerry/
* extracting reasoning from chess engines - https://github.com/pcattori/deep-blue-talks

License
-------

python-chess is licensed under the GPL3. See the LICENSE file for the
full copyright and license information.

Thanks to Sam Tannous for publishing his approach to `avoid rotated bitboards
with direct lookup (pdf) <http://arxiv.org/pdf/0704.3773.pdf>`_ alongside
his GPL2+ engine `Shatranj <https://github.com/stannous/shatranj>`_. Some of
the bitboard move generation parts are ported from there.

Thanks to Ronald de Man for his Syzygy endgame tablebases
(https://github.com/syzygy1/tb). The probing code in
python-chess is very directly ported from his C probing code.

Thanks to Miguel A. Ballicora for his Gaviota tablebases
(https://github.com/michiguel/Gaviota-Tablebases).
