One rainy Saturday afternoon, I thought I’d investigate the possibility of programmatically analyzing chess positions and entire chess games.
After spending some time on the Internet, I realized there were lots of possible ways to approach this problem. I ended up using a Python library of functions that interact with the Stockfish chess engine.
Update: After writing this post, I created my own tool to convert a PGN file to a file of FEN strings. The code is at https://github.com/jdmccaffrey/convert-pgn-to-fen.
First I downloaded the Stockfish engine for Windows from https://stockfishchess.org/download/windows/ by clicking on the 64-bit link button. This downloaded file stockfish-windows-x86-64.zip to my Downloads directory. I unzipped the file, and copied the extracted files to a local directory C:\Python\Stockfish that I created. The unzipped root directory has the Stockfish engine named as stockfish-windows-x86-64.exe in subdirectory Stockfish\stockfish-windows-x86-64\stockfish.
You don’t run the Stockfish executable directly. Stockfish needs an application program that accesses the executable.
I installed a Python library interface to Stockfish by opening a command shell and issuing the command “pip install stockfish”. The stockfish Python library is just an interface — it doesn’t include the Stockfish engine. The stockfish library is very slick and the documentation at https://pypi.org/project/stockfish/ is very good.
After a few hours of experimentation, I was able to programmatically analyze a chess game. Part of the output of my demo program looks like:
----------
position = 40 | white to move | move # 21
position in FEN =
r2qr3/pp1b1pkp/2ppnn2/4pp2/3PP3/P3PQNP/BPP3P1/R4RK1 w - - 0 21
position:
+---+---+---+---+---+---+---+---+
| r | | | q | r | | | | 8
+---+---+---+---+---+---+---+---+
| p | p | | b | | p | k | p | 7
+---+---+---+---+---+---+---+---+
| | | p | p | n | n | | | 6
+---+---+---+---+---+---+---+---+
| | | | | p | p | | | 5
+---+---+---+---+---+---+---+---+
| | | | P | P | | | | 4
+---+---+---+---+---+---+---+---+
| P | | | | P | Q | N | P | 3
+---+---+---+---+---+---+---+---+
| B | P | P | | | | P | | 2
+---+---+---+---+---+---+---+---+
| R | | | | | R | K | | 1
+---+---+---+---+---+---+---+---+
a b c d e f g h
Position evaluation = {'type': 'cp', 'value': 376}
Best moves in this position:
{'Move': 'g3f5', 'Centipawn': 399, 'Mate': None}
{'Move': 'd4e5', 'Centipawn': 185, 'Mate': None}
{'Move': 'f3f5', 'Centipawn': 54, 'Mate': None}
{'Move': 'h3h4', 'Centipawn': -173, 'Mate': None}
{'Move': 'a2e6', 'Centipawn': -269, 'Mate': None}
----------
The evaluation values, such as 399 and -269 above, are measured in centipawns, or 1/100 of a pawn. A positive value means the position favors White. A negative value means the position favors Black. Almost all popular chess programs convert these evaluations by dividing by 100, and so the move 21.Ng3xf5 results in a position with an evaluation of +3.99 pawns in favor of White, and the move 21.Ba2xe6 results in a position with an evaluation of -2.69 pawns in favor of Black.

Left: The Stockfish engine download page. Right: The online tool I used to convert PGN notation to FEN notation.

Left: The documentation for the Python Stockfish library. Right: The Nakamura-Topalov game at chessgames.com
I was analyzing a game between Hikaru Nakamura (White) and Veselin Topalov (Black) from 2017. I downloaded the game in PGN format from chessgames.com which is:
[Event "Champions Showdown in Saint Louis (Blitz)"] [Site "St Louis, MO USA"] [Date "2017.11.12"] [EventDate "2017.10.21"] [Round "12.1"] [Result "1-0"] [White "Hikaru Nakamura"] [Black "Veselin Topalov"] [ECO "C26"] [WhiteElo "2774"] [BlackElo "2749"] [PlyCount "43"] 1. e4 e5 2. Nc3 Nf6 3. Bc4 Bc5 4. d3 c6 5. Bb3 d6 6. Nf3 O-O 7. h3 Nbd7 8. O-O Bb6 9. a3 Nc5 10. Ba2 Ne6 11. Ne2 Re8 12. Be3 Bxe3 13. fxe3 Qc7 14. Nh4 Qd8 15. Nf3 Bd7 16. Ng3 g6 17. d4 Qc7 18. Nh4 Qd8 19. Qf3 Kg7 20. Nhf5+ gxf5 21. Nxf5+ Kg6 22. Bxe6 1-0
I was unable to determine how to use the stockfish Python library with PGN data, so I had to convert the PGN data to FEN (“Forsyth–Edwards Notation”) data. Luckily I discovered a very nice online tool to do this at https://www.lutanho.net/pgn/pgn2fen.html. The game in FEN format is:
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1 rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2 rnbqkbnr/pppp1ppp/8/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR b KQkq - 1 2 rnbqkb1r/pppp1ppp/5n2/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR w KQkq - 2 3 rnbqkb1r/pppp1ppp/5n2/4p3/2B1P3/2N5/PPPP1PPP/R1BQK1NR b KQkq - 3 3 . . . r2qr3/pp1b1p1p/2ppnnk1/4pN2/3PP3/P3PQ1P/BPP3P1/R4RK1 w - - 1 22 r2qr3/pp1b1p1p/2ppBnk1/4pN2/3PP3/P3PQ1P/1PP3P1/R4RK1 b - - 0 22
Each line of data is a position from the game. The full FEN is listed below. The Wikipedia article on Forsyth–Edwards Notation has a good explanation.
Here are a few of the key lines of my demo program. These statements fire up the Stockfish engine:
from stockfish import Stockfish loc = "C:\\Python\\Stockfish\\" + \ "stockfish-windows-x86-64\\stockfish\\" + \ "stockfish-windows-x86-64.exe" stockfish = Stockfish(path=loc)
These lines set and display a position from the game:
game = ".\\game_04.txt" # FEN data
f = open(game, "r")
. . .
line = f.readline() # read a line from the FEN data
stockfish.set_fen_position(line) # set position using FEN
vis_pos = stockfish.get_board_visual() # ASCII visual
print("position: ")
print(vis_pos)
. . .
All in all it was a very interesting experiment and there are many ideas to explore. For example, chess positions where about half of the possible moves result in positive evaluations, and the other possible moves result in negative evaluations, seem like “risky” positions in some sense. I suspect some grandmaster chess players tend to make moves that lead to risky positions, as opposed to objectively the best move, because a risky position gives their opponent more chance to make a mistake.
I’m sure there’s a lot more to learn about the stockfish library, but even so, I think I’ve made a good start.

Chess grandmasters often have very warped personalities because they must dedicate essentially their entire life to chess. For example, Robert Fisher (1943-2008, 11th champion 1972-1975) was truly a bizarre human being — and not in a nice way. But several of the 17 modern world chess champions have reputations as being nice people. These nice guys of chess include Jose Raul Capablanca (1888-1942, 3rd champion 1921-1927), Max Euwe (1901-1981, 5th champion 1935-1937), Vasily Smyslov (1921-2010, 7th champion 1957-1958), Boris Spassky (b. 1937, 10th champion 1969-1971), and Viswanathan Anand (b. 1969, 15th champion 2007-2013).
Left: Jose Raul Capablanca. Center: Max Euwe. Right: Viswanathan Anand.
Demo program:
# stockfish_demo.py
# Anaconda3-2023.09-0 Python 3.11.5
# Windows 10/11
from stockfish import Stockfish
loc = "C:\\Python\\Stockfish\\" + \
"stockfish-windows-x86-64\\stockfish\\" + \
"stockfish-windows-x86-64.exe"
stockfish = Stockfish(path=loc)
stockfish.update_engine_parameters({"UCI_Elo": 2000})
p = stockfish.get_parameters()
print("\nstockfish parameters: ")
print(p)
game = ".\\game_04.txt"
f = open(game, "r")
pos_number = 0
while True:
line = f.readline()
if not line: break
if line.startswith("#"): continue
if line.startswith("["): continue
line = line.strip()
print("\n----------")
print("\nposition = " + str(pos_number) +\
" | ", end="")
tokens = line.split(" ")
if tokens[1] == "w":
print("white to move | ", end="")
elif tokens[1] == "b":
print("black to move | ", end="")
print(" move # " + str(tokens[-1]))
print("\nposition in FEN = ")
print(line)
stockfish.set_fen_position(line)
vis_pos = stockfish.get_board_visual()
print("\nposition: ")
print(vis_pos)
curr_eval = stockfish.get_evaluation()
print("Position evaluation = ", end="")
print(curr_eval)
bms = stockfish.get_top_moves(5)
print("\nBest moves in this position:")
for i in range(len(bms)):
print(bms[i])
pos_number += 1
print("\n----------")
# input()
f.close()
print("\nEnd analysis ")
Data:
# game_04.txt
#
# [Event "Champions Showdown in Saint Louis (Blitz)"]
# [Site "St Louis, MO USA"]
# [Date "2017.11.12"]
# [EventDate "2017.10.21"]
# [Round "12.1"]
# [Result "1-0"]
# [White "Hikaru Nakamura"]
# [Black "Veselin Topalov"]
# [ECO "C26"]
# [WhiteElo "2774"]
# [BlackElo "2749"]
# [PlyCount "43"]
#
# 1. e4 e5 2. Nc3 Nf6 3. Bc4 Bc5 4. d3 c6 5. Bb3 d6 6. Nf3 O-O
# 7. h3 Nbd7 8. O-O Bb6 9. a3 Nc5 10. Ba2 Ne6 11. Ne2 Re8
# 12. Be3 Bxe3 13. fxe3 Qc7 14. Nh4 Qd8 15. Nf3 Bd7 16. Ng3 g6
# 17. d4 Qc7 18. Nh4 Qd8 19. Qf3 Kg7 20. Nhf5+ gxf5 21. Nxf5+
# Kg6 22. Bxe6 1-0
#
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1
rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2
rnbqkbnr/pppp1ppp/8/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR b KQkq - 1 2
rnbqkb1r/pppp1ppp/5n2/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR w KQkq - 2 3
rnbqkb1r/pppp1ppp/5n2/4p3/2B1P3/2N5/PPPP1PPP/R1BQK1NR b KQkq - 3 3
rnbqk2r/pppp1ppp/5n2/2b1p3/2B1P3/2N5/PPPP1PPP/R1BQK1NR w KQkq - 4 4
rnbqk2r/pppp1ppp/5n2/2b1p3/2B1P3/2NP4/PPP2PPP/R1BQK1NR b KQkq - 0 4
rnbqk2r/pp1p1ppp/2p2n2/2b1p3/2B1P3/2NP4/PPP2PPP/R1BQK1NR w KQkq - 0 5
rnbqk2r/pp1p1ppp/2p2n2/2b1p3/4P3/1BNP4/PPP2PPP/R1BQK1NR b KQkq - 1 5
rnbqk2r/pp3ppp/2pp1n2/2b1p3/4P3/1BNP4/PPP2PPP/R1BQK1NR w KQkq - 0 6
rnbqk2r/pp3ppp/2pp1n2/2b1p3/4P3/1BNP1N2/PPP2PPP/R1BQK2R b KQkq - 1 6
rnbq1rk1/pp3ppp/2pp1n2/2b1p3/4P3/1BNP1N2/PPP2PPP/R1BQK2R w KQ - 2 7
rnbq1rk1/pp3ppp/2pp1n2/2b1p3/4P3/1BNP1N1P/PPP2PP1/R1BQK2R b KQ - 0 7
r1bq1rk1/pp1n1ppp/2pp1n2/2b1p3/4P3/1BNP1N1P/PPP2PP1/R1BQK2R w KQ - 1 8
r1bq1rk1/pp1n1ppp/2pp1n2/2b1p3/4P3/1BNP1N1P/PPP2PP1/R1BQ1RK1 b - - 2 8
r1bq1rk1/pp1n1ppp/1bpp1n2/4p3/4P3/1BNP1N1P/PPP2PP1/R1BQ1RK1 w - - 3 9
r1bq1rk1/pp1n1ppp/1bpp1n2/4p3/4P3/PBNP1N1P/1PP2PP1/R1BQ1RK1 b - - 0 9
r1bq1rk1/pp3ppp/1bpp1n2/2n1p3/4P3/PBNP1N1P/1PP2PP1/R1BQ1RK1 w - - 1 10
r1bq1rk1/pp3ppp/1bpp1n2/2n1p3/4P3/P1NP1N1P/BPP2PP1/R1BQ1RK1 b - - 2 10
r1bq1rk1/pp3ppp/1bppnn2/4p3/4P3/P1NP1N1P/BPP2PP1/R1BQ1RK1 w - - 3 11
r1bq1rk1/pp3ppp/1bppnn2/4p3/4P3/P2P1N1P/BPP1NPP1/R1BQ1RK1 b - - 4 11
r1bqr1k1/pp3ppp/1bppnn2/4p3/4P3/P2P1N1P/BPP1NPP1/R1BQ1RK1 w - - 5 12
r1bqr1k1/pp3ppp/1bppnn2/4p3/4P3/P2PBN1P/BPP1NPP1/R2Q1RK1 b - - 6 12
r1bqr1k1/pp3ppp/2ppnn2/4p3/4P3/P2PbN1P/BPP1NPP1/R2Q1RK1 w - - 0 13
r1bqr1k1/pp3ppp/2ppnn2/4p3/4P3/P2PPN1P/BPP1N1P1/R2Q1RK1 b - - 0 13
r1b1r1k1/ppq2ppp/2ppnn2/4p3/4P3/P2PPN1P/BPP1N1P1/R2Q1RK1 w - - 1 14
r1b1r1k1/ppq2ppp/2ppnn2/4p3/4P2N/P2PP2P/BPP1N1P1/R2Q1RK1 b - - 2 14
r1bqr1k1/pp3ppp/2ppnn2/4p3/4P2N/P2PP2P/BPP1N1P1/R2Q1RK1 w - - 3 15
r1bqr1k1/pp3ppp/2ppnn2/4p3/4P3/P2PPN1P/BPP1N1P1/R2Q1RK1 b - - 4 15
r2qr1k1/pp1b1ppp/2ppnn2/4p3/4P3/P2PPN1P/BPP1N1P1/R2Q1RK1 w - - 5 16
r2qr1k1/pp1b1ppp/2ppnn2/4p3/4P3/P2PPNNP/BPP3P1/R2Q1RK1 b - - 6 16
r2qr1k1/pp1b1p1p/2ppnnp1/4p3/4P3/P2PPNNP/BPP3P1/R2Q1RK1 w - - 0 17
r2qr1k1/pp1b1p1p/2ppnnp1/4p3/3PP3/P3PNNP/BPP3P1/R2Q1RK1 b - - 0 17
r3r1k1/ppqb1p1p/2ppnnp1/4p3/3PP3/P3PNNP/BPP3P1/R2Q1RK1 w - - 1 18
r3r1k1/ppqb1p1p/2ppnnp1/4p3/3PP2N/P3P1NP/BPP3P1/R2Q1RK1 b - - 2 18
r2qr1k1/pp1b1p1p/2ppnnp1/4p3/3PP2N/P3P1NP/BPP3P1/R2Q1RK1 w - - 3 19
r2qr1k1/pp1b1p1p/2ppnnp1/4p3/3PP2N/P3PQNP/BPP3P1/R4RK1 b - - 4 19
r2qr3/pp1b1pkp/2ppnnp1/4p3/3PP2N/P3PQNP/BPP3P1/R4RK1 w - - 5 20
r2qr3/pp1b1pkp/2ppnnp1/4pN2/3PP3/P3PQNP/BPP3P1/R4RK1 b - - 6 20
r2qr3/pp1b1pkp/2ppnn2/4pp2/3PP3/P3PQNP/BPP3P1/R4RK1 w - - 0 21
r2qr3/pp1b1pkp/2ppnn2/4pN2/3PP3/P3PQ1P/BPP3P1/R4RK1 b - - 0 21
r2qr3/pp1b1p1p/2ppnnk1/4pN2/3PP3/P3PQ1P/BPP3P1/R4RK1 w - - 1 22
r2qr3/pp1b1p1p/2ppBnk1/4pN2/3PP3/P3PQ1P/1PP3P1/R4RK1 b - - 0 22

.NET Test Automation Recipes
Software Testing
SciPy Programming Succinctly
Keras Succinctly
R Programming
2026 Visual Studio Live
2025 Summer MLADS Conference
2026 DevIntersection Conference
2025 Machine Learning Week
2025 Ai4 Conference
2026 G2E Conference
2026 iSC West Conference
You must be logged in to post a comment.