Created by Nate Solon. Email: nate.solon@gmail.com

Purpose 👊

This notebook will show you how to get started with using data to analyze chess. By the end you will be able to:

  • Download the chess games of any player
  • Play through the moves
  • Use a chess engine to analyze positions

Let's go! 🚀

Get Games ⛳️

First you'll use the lichess API to download some games. Lichess is the second-biggest online chess server in the world. It's free and open source.

pip install chess python-lichess
Collecting chess
  Downloading chess-1.7.0-py3-none-any.whl (147 kB)
     |████████████████████████████████| 147 kB 5.2 MB/s 
Collecting python-lichess
  Downloading python_lichess-0.10-py3-none-any.whl (21 kB)
Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from python-lichess) (1.15.0)
Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from python-lichess) (2.23.0)
Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->python-lichess) (3.0.4)
Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->python-lichess) (2.10)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->python-lichess) (1.24.3)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->python-lichess) (2021.10.8)
Installing collected packages: python-lichess, chess
Successfully installed chess-1.7.0 python-lichess-0.10
import lichess.api
from lichess.format import SINGLE_PGN

You can download the games of any player provided you know their username. Maybe you'd like to check your own games... or a potential opponent?

Let's get the latest game from Magnus Carlsen, the World Chess Champion, whose username happens to be DrNykterstein. "Nykter" is Norwegian for "sober"... 🤔

user = "DrNykterstein" # Magnus Carlsen! You can change this to any lichess user.

Chess games are often stored in PGN format, which is just a text file with information about the game and the moves.

pgn = lichess.api.user_games(user, max=1, format=SINGLE_PGN)
print(pgn)
[Event "Rated Blitz game"]
[Site "https://lichess.org/JmBtiW5e"]
[Date "2021.11.17"]
[White "DrNykterstein"]
[Black "Miawlet"]
[Result "1-0"]
[UTCDate "2021.11.17"]
[UTCTime "01:57:30"]
[WhiteElo "3199"]
[BlackElo "2979"]
[WhiteRatingDiff "+3"]
[BlackRatingDiff "-2"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[Variant "Standard"]
[TimeControl "180+0"]
[ECO "E21"]
[Termination "Normal"]

1. d4 Nf6 2. c4 e6 3. Nc3 Bb4 4. Nf3 O-O 5. Bg5 h6 6. Bh4 c5 7. e3 d5 8. cxd5 g5 9. Bg3 Nxd5 10. Rc1 Qa5 11. a3 Bxc3+ 12. bxc3 cxd4 13. Nxd4 Nc6 14. Bd3 Nxd4 15. exd4 Bd7 16. O-O Nf4 17. Bxf4 gxf4 18. Qf3 Bc6 19. Qxf4 Qg5 20. Qxg5+ hxg5 21. f4 gxf4 22. Rxf4 f5 23. c4 Rad8 24. Re1 Kf7 25. d5 exd5 26. Rxf5+ Kg8 27. Rg5+ Kh8 28. Re3 1-0



Experienced chess players can read the moves by sight, but it's no problem if you can't. In the next section you'll use the python-chess library to read the game. For now, let's save our pgn.

with open("magnus.pgn", "w") as f:
  f.write(pgn)

Read Games 🤓

In this section you'll use the pgn module of python-chess to open and read a pgn file.

import chess
import chess.pgn
pgn = open("magnus.pgn")
game = chess.pgn.read_game(pgn)

You can see the current board position right in the notebook!

board = game.board()
board

Nice! But this is just the starting position, not very interesting. Let's make some moves...

for i, move in enumerate(game.mainline_moves()):
    board.push(move)
    if i == 20:
        break
board

Looks like a tense middlegame! Who's winning? If only you could ask a chess master...

Analyze Games 🧐

In this section you'll use Stockfish, the current strongest chess engine in the world, to analyze a position.

First you need to download Stockfish. If you're running this notebook on Colab, the command below will work. If running locally, you can download the appropriate version of Stockfish.

!   wget https://stockfishchess.org/files/stockfish_14_linux_x64_popcnt.zip && \
    unzip stockfish_14_linux_x64_popcnt.zip stockfish_14_linux_x64_popcnt/stockfish_14_x64_popcnt
--2021-12-11 16:52:00--  https://stockfishchess.org/files/stockfish_14_linux_x64_popcnt.zip
Resolving stockfishchess.org (stockfishchess.org)... 104.25.159.9, 172.67.80.249, 104.25.158.9, ...
Connecting to stockfishchess.org (stockfishchess.org)|104.25.159.9|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 28531469 (27M) [application/zip]
Saving to: ‘stockfish_14_linux_x64_popcnt.zip’

stockfish_14_linux_ 100%[===================>]  27.21M   107MB/s    in 0.3s    

2021-12-11 16:52:00 (107 MB/s) - ‘stockfish_14_linux_x64_popcnt.zip’ saved [28531469/28531469]

Archive:  stockfish_14_linux_x64_popcnt.zip
  inflating: stockfish_14_linux_x64_popcnt/stockfish_14_x64_popcnt  
import chess.engine

python-chess has an engine module as well. How convenient! To connect to the engine, all you need is the location of the Stockfish executable.

engine = chess.engine.SimpleEngine.popen_uci("/content/stockfish_14_linux_x64_popcnt/stockfish_14_x64_popcnt")

Let's instruct the engine to analyze the current position for 1 second.

info = engine.analyse(board, chess.engine.Limit(time=1))
info
{'depth': 18,
 'hashfull': 303,
 'multipv': 1,
 'nodes': 695688,
 'nps': 693607,
 'pv': [Move.from_uci('b4c3'),
  Move.from_uci('b2c3'),
  Move.from_uci('c5d4'),
  Move.from_uci('e3d4'),
  Move.from_uci('b8c6'),
  Move.from_uci('d1c2'),
  Move.from_uci('e6e5'),
  Move.from_uci('d4e5'),
  Move.from_uci('a5a3'),
  Move.from_uci('f1d3'),
  Move.from_uci('c8g4'),
  Move.from_uci('e1g1'),
  Move.from_uci('a8d8'),
  Move.from_uci('f3d4'),
  Move.from_uci('c6d4'),
  Move.from_uci('c3d4'),
  Move.from_uci('a3b4'),
  Move.from_uci('h2h4'),
  Move.from_uci('b4d4')],
 'score': PovScore(Cp(-68), BLACK),
 'seldepth': 26,
 'string': 'NNUE evaluation using nn-3475407dc199.nnue enabled',
 'tbhits': 0,
 'time': 1.003}

This is a little confusing, but for the moment all you need is the score. This represents who's winning in the current position.

info['score'].pov(chess.WHITE)
Cp(+68)

Cp means centipawns. 100 centipawns = 1 pawn. So according to the engine, from White's pov (point of view) the evaluation is +68. White is ahead by a little less than a pawn. It seems Magnus has the upper hand.

Conclusion 🎬

You now know how to download games, play through the moves, and analyze positions with a chess engine. What will you do with all this power??