Updated: 2023-11-20
Chess is a board game played on an eight-row by eight-column size. Each player has a king, queen, two rooks, two bishops, two knights, and eight pawns. To win the game, either side has to checkmate their opponent's king.
One interesting part of this game is that - after it is over, the players can still enjoy analyzing those critical and difficult-to-assess positions. Lucky for us, we already have some strong computer chess programs or engines that can help us evaluate all those positions.
In this article, we will be building a Python script program that can analyze chess games using a chess engine. It can annotate moves with good, interesting, dubious, mistake and blunder symbols through NAGs (Numeric Annotation Glyphs). It also generates accuracy percentages for both players.
A. Requirements
- A game to analyze
- A computer chess program
- A Python chess library
- The script that we are going to create
1. A game to analyze
carlsen-giri-tatasteel-2022.pgn
[Event "84th Tata Steel Masters"]
[Site "Wijk aan Zee NED"]
[Date "2022.01.16"]
[Round "2.1"]
[White "Carlsen, M"]
[Black "Giri, A"]
[Result "1-0"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2865"]
[BlackElo "2772"]
[ECO "E06"]
[Opening "Catalan"]
[Variation "closed, 5.Nf3"]
[WhiteFideId "1503014"]
[BlackFideId "24116068"]
[EventDate "2022.01.15"]
d4 Nf6 2. c4 e6 3. Nf3 d5 4. g3 Be7 5. Bg2 O-O 6. O-O dxc4 7. Na3 Bxa3 8.
bxa3 Bd7 9. a4 Bc6 10. Ba3 Re8 11. Qc2 Nbd7 12. Rac1 a6 13. Qxc4 Nb6 14. Qc3
Nxa4 15. Qb3 Qd5 16. Rxc6 Qxc6 17. Ne5 Qb5 18. Qc2 Nd5 19. Rb1 Qa5 20. Bxd5 exd5
Rxb7 c5 22. Qf5 Rf8 23. Nxf7 Qd8 24. dxc5 Qf6 25. Qxf6 gxf6 26. Nh6+ Kh8 27.
c6 Rfc8 28. c7 Nc3 29. Bb2 d4 30. Nf7+ Kg7 31. Nd6 Kg6 32. Kf1 Nb5 33. Nxc8 Rxc8
a4 Nxc7 35. Bxd4 Ne6 36. Be3 1-0
2. A computer chess program
Download the strong Stockfish computer chess program.
3. A Python chess library
It parses the moves, manages computer chess program, etc. Install it with:
pip install chess
4. The script that we are going to create
analysis.py code snippets
Docstring and imports
"""Analyze a chess game.
Reads a game position by position, evaluates the game moves using a uci
engine of your choice that supports searchmoves feature. It outputs the
analyzed games in PGN format.
"""
import chess
import chess.engine
import chess.pgn
Game parser
def main():
engine_path_file = 'stockfish_16.exe' # r"d:\chess\engines\stockfish16.exe"
engine_threads = 1
engine_hash = 128 # MB
inputfn = 'carlsen-giri-tatasteel-2022.pgn'
outputfn = 'analyzed_games.pgn'
aspp = 1 # analysis seconds per position, can be 0.1 and above
multipv = 3 # may include a max of multipv alternative lines of analysis
with open(inputfn) as h:
while True:
game = chess.pgn.read_game(h)
if game is None:
break
analyze_game(
game,
engine_path_file,
multipv=multipv,
aspp=aspp,
outputfn=outputfn,
engine_threads=engine_threads,
engine_hash=engine_hash
)
Modify the "engine_path_file" in the code if your engine is located in other folder. Also modify "inputfn" if you want other games to be analyzed.
The analysis time variable "aspp", is only 1 second in the code. You can increase this to get a stable engine analysis. The "multipv" value in the code is only 3, you can increase this to something like 5, so that we can see the other top analysis lines according to the engine.
The complete code can be found in the chess game analysis sourceforge repository. Select the Files tab and press "analysis.py" file.
B. Sample game analysis output
[Event "84th Tata Steel Masters"]
[Site "Wijk aan Zee NED"]
[Date "2022.01.16"]
[Round "2.1"]
[White "Carlsen, M"]
[Black "Giri, A"]
[Result "1-0"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2865"]
[BlackElo "2772"]
[ECO "E06"]
[Opening "Catalan"]
[Variation "closed, 5.Nf3"]
[WhiteFideId "1503014"]
[BlackFideId "24116068"]
[EventDate "2022.01.15"]
[Annotator "Stockfish 16 @0.5 sec/pos, hash=256, threads=2"]
[WhiteAccuracy "83.33"]
[BlackAccuracy "80.0"]
1. d4 { 0.32 } ( 1. Nf3 d5 2. d4 c5 3. c4 cxd4
4. Qxd4 { 0.33 } ) 1... Nf6 { -0.27 } 2. c4 { 0.28 } 2... e6 { -0.31 }
3. Nf3 { 0.33 } ( 3. g3 d5 4. Bg2 c5 5. cxd5 exd5 6. Nf3 { 0.39 } )
3... d5 { -0.34 } 4. g3 { 0.31 } 4... Be7 { -0.38 } ( 4... dxc4 5. Qa4+ Qd7
6. Qxc4 Qc6 7. Qxc6+ Nxc6 { -0.28 } ) ( 4... Bb4+ 5. Bd2 a5 6. Qc2 c5
7. cxd5 exd5 { -0.36 } ) 5. Bg2 { 0.38 } 5... O-O { -0.42 }
6. O-O { 0.38 } 6... dxc4 $1 { -0.36 } ( 6... c5 7. cxd5 exd5 8. dxc5 Bxc5
9. Nc3 Nc6 { -0.4 } ) 7. Na3 { -0.06 } ( 7. Ne5 Nd5 8. Nxc4 b5 9. Ne3 Bb7
10. Nxd5 { 0.43 } ) ( 7. Qc2 b5 8. a4 Bb7 9. axb5 a6
10. bxa6 { 0.41 } ) ( 7. a4 Nc6 { 0.12 } ) ( 7. Qa4 a6 8. Qxc4 b5 9. Qc2 Bb7
10. Bf4 { 0.1 } ) 7... Bxa3 { 0.1 } 8. bxa3 { -0.05 } 8... Bd7 { 0.05 }
9. a4 { -0.07 } ( 9. Ne5 Bc6 10. Nxc6 Nxc6 11. Bxc6 bxc6 12. Bg5 { 0.02 } )
( 9. Qc2 Bc6 10. a4 Re8 11. Ba3 Nbd7 12. Rfe1 { -0.03 } ) 9... Bc6 { 0.1 }
10. Ba3 $1 { -0.06 } ( 10. Qc2 Nbd7 11. Ba3 Re8 12. Rfe1 Rc8 13. e4 { -0.11 } )
10... Re8 { 0.01 } 11. Qc2 { -0.07 } 11... Nbd7 { 0.07 } 12. Rac1 { -0.1 }
12... a6 $1 { 0.08 } ( 12... Bd5 { 0.07 } ) 13. Qxc4 { -0.12 }
13... Nb6 { 0.07 } 14. Qc3 $5 { -0.05 } ( 14. Qd3 Bxa4 15. Rfe1 Bb5 16. Qb1 h6
17. e4 { -0.02 } ) 14... Nxa4 { 0.17 } 15. Qb3 { -0.45 } ( 15. Qc2 Nb6
16. Rfe1 Be4 17. Qxc7 Qxc7 18. Rxc7 { -0.15 } ) ( 15. Qb4 a5 16. Qb3 Nb6
17. Qb2 Be4 18. Rfd1 { -0.27 } ) 15... Qd5 { -0.08 } ( 15... Nb6 16. Rxc6 bxc6
17. Ne5 Qxd4 18. Nxc6 Qd2 { 0.51 } ) ( 15... Bb5 16. Rfe1 h6 17. e3 Nb6
18. e4 Rc8 { 0.0 } ) 16. Rxc6 $5 { 0.0 } ( 16. e4 Qxb3 17. axb3 Nb6 18. Ne5 Bxe4
19. Rxc7 { 0.05 } ) 16... Qxc6 { 0.0 } 17. Ne5 { 0.0 } 17... Qb5 { 0.0 }
18. Qc2 { 0.0 } 18... Nd5 $2 { -1.41 } ( 18... Nb6 19. Bxb7 Nc4 20. Bxa8 Nxa3
21. Qc6 Qxe2 { 0.0 } ) ( 18... c6 19. Rb1 Qa5 20. Bb4 Qd8
21. Qxa4 Qxd4 { -0.87 } ) ( 18... Rac8 19. Rb1 Qa5 20. Bxb7 Nc3
21. Rb3 Nb5 { -1.18 } ) ( 18... c5 19. Rb1 { -1.35 } ) 19. Rb1 { 1.46 }
19... Qa5 { -1.41 } 20. Bxd5 { 0.43 } ( 20. Be4 g6 21. Bxd5 exd5 22. Bb4 Qb6
23. Bc3 { 1.41 } ) ( 20. e4 Nf6 21. Bb4 Qb6 22. Qxa4 Qxd4 23. Qa5 { 1.3 } )
20... exd5 $2 { -1.46 } ( 20... Nc3 21. Bb4 Nxe2+ 22. Qxe2 Qxd5
23. Rc1 Qxd4 { -0.35 } ) ( 20... Qxd5 21. Qxa4 { -0.99 } ) 21. Rxb7 { 1.25 }
21... c5 { -2.15 } ( 21... Nc3 22. Bb4 Qxa2 23. Qxc3 Qb1+
24. Kg2 Qe4+ { -1.51 } ) ( 21... Qe1+ 22. Kg2 Nc3 23. Bb4 Qxe2
24. Qxc3 Qe4+ { -1.81 } ) 22. Qf5 { 2.21 } 22... Rf8 { -2.41 }
23. Nxf7 $1 { 3.1 } ( 23. Rxf7 h6 24. Kg2 Qc3 25. Rxf8+ Rxf8
26. Qe6+ { 2.35 } ) 23... Qd8 { -3.91 } ( 23... Qd2 24. e3 a5
25. Qg5 Rxf7 26. Rxf7 Kxf7 { -2.92 } ) ( 23... Qe1+ 24. Kg2 Qxe2
25. Qxd5 h5 26. Ng5+ Kh8 { -3.39 } ) ( 23... Qc3 24. e3 Qd2 25. Nh6+ Kh8
26. Rf7 Rfc8 { -3.42 } ) 24. dxc5 { 4.0 } 24... Qf6 { -4.0 } 25. Qxf6 { 4.2 }
25... gxf6 { -4.27 } 26. Nh6+ { 4.0 } 26... Kh8 { -4.13 } 27. c6 { 4.08 }
27... Rfc8 { -4.13 } 28. c7 { 4.28 } 28... Nc3 { -4.31 } ( 28... Kg7
29. Nf5+ Kf7 30. Nd6+ Ke6 31. Nxc8 Rxc8 { -4.19 } ) 29. Bb2 { 4.08 } ( 29. Nf5 Nb5
30. Bd6 d4 31. Kg2 Nc3 32. Ne7 { 4.35 } ) 29... d4 { -4.08 } 30. Nf7+ { 4.05 }
30... Kg7 { -4.07 } 31. Nd6 { 4.04 } 31... Kg6 { -4.06 } 32. Kf1 { 4.17 }
32... Nb5 { -4.06 } 33. Nxc8 { 4.19 } 33... Rxc8 { -4.05 } 34. a4 { 4.21 }
34... Nxc7 { -4.2 } 35. Bxd4 { 4.15 } 35... Ne6 { -4.21 } 36. Be3 { 4.21 } 1-0
White achieved an accuracy of [WhiteAccuracy "83.33"] and black got [BlackAccuracy "80.0"].
C. Summary
It feels satisfying when your evaluation of positions during a game is answered truthfully by an engine after the game. The engine together with this script also presents new ideas by examining the engine's principal variation lines. You can expand the engine's analysis lines by increasing the multipv value to more than one.
D. Resources