Created one using python-chess lib.zenpawn wrote:On the topic of static evals, is there a way/program to run through a PGN to get the evaluations of the positions along the way without the engine doing any search? I came close by setting Fritz 15 to perform its blunder check with depth 1 and margin (to consider a move a blunder) of 0.
Read the upper part of the code, sections E, F and H for instructions. Note this will only work with stockfish, utilizing the eval command after position setup.
sf_staticeval.py
Code: Select all
"""
A. Program name
Stockfish static eval
B. Program description
Read pgn and annotate the game with Stockfish static eval.
C. License notice
This program is free software, you can redistribute it and/or modify
it under the terms of the GPLv3 License as published by the
Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License (LICENSE)
along with this program, if not visit https://www.gnu.org/licenses/gpl.html
D. Program version development log
version 1
1. Annotate a pgn file with Stockfish static eval
E. Dependent modules and/or programs
1. python-chess
https://pypi.python.org/pypi/python-chess
F. Programming language
1. Python v2.7.11
https://www.python.org/
G. Program other info
1. filename: sf_staticeval.py
2. version: 1
3. author: ferdy
4. created: Oct 3, 2016
H. Tests and Guide
1. This is tested under Windows 7 OS, python 2.7.11
and python-chess 0.14.1.
2. You need to install python 2.7 version (or later but not tested)
on your computer.
3. You need to install python-chess on your computer, see section E.
4. Rename your input pgn file to src.pgn.
5. Rename your engine to stockfish.exe.
6. The output filename is out_src.pgn, this is overwrite mode.
"""
import subprocess
import os
import sys
import chess
from chess import pgn
def DeleteFile(fn):
""" Delete fn file """
if os.path.isfile(fn):
os.remove(fn)
def GetEngineIdName(engine):
""" Returns the engine id name """
engineIdName = engine[0:-4]
# Run the engine
p = subprocess.Popen(engine, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
# Send command
p.stdin.write("uci\n")
for eline in iter(p.stdout.readline, ''):
line = eline.strip()
# Print engine output after uci command
print(line)
# Save id name
if 'id name ' in line:
idName = line.split()
engineIdName = ' '.join(idName[2:])
if "uciok" in line:
break
p.stdin.write('quit\n')
p.communicate()
return engineIdName
def EvaluatePosition(engine, pos):
""" Run stockfish, setup position pos and send
eval command to get its static eval score
"""
score = -32000.0
# Run the engine
p = subprocess.Popen(engine, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
# Send commands to engine
p.stdin.write("uci\n")
# Parse engine replies
for eline in iter(p.stdout.readline, ''):
line = eline.strip()
if "uciok" in line:
break
p.stdin.write("isready\n")
for eline in iter(p.stdout.readline, ''):
line = eline.strip()
if "readyok" in line:
break
p.stdin.write("ucinewgame\n")
p.stdin.write("position fen " + pos + "\n")
p.stdin.write("eval\n")
# Parse the output and extract the score
for eline in iter(p.stdout.readline, ''):
line = eline.strip()
if 'Total Evaluation: ' in line:
first = line.split('(')[0]
score = float(first.split()[2])
break
p.stdin.write('quit\n')
p.communicate()
assert score != -32000.0, 'Something is wrong in the eval'
return score
def main(argv):
""" start """
inputFile = 'src.pgn'
outputFile = 'out_src.pgn'
engineName = 'stockfish.exe'
# Delete existing 'out-src.pgn'
DeleteFile(outputFile)
# Get engine id name for the Annotator tag
engIdName = GetEngineIdName(engineName)
# Open the input pgn file
pgnHandle = open(inputFile, 'r')
# Read the input pgn file
game = chess.pgn.read_game(pgnHandle)
game_cnt = 0
# Loop thru the games
while game:
game_cnt += 1
print('Annotating game %d...' %(game_cnt))
# Save the tag section of the game
for key, value in game.headers.items():
with open(outputFile, 'a+') as f:
f.write('[%s "%s"]\n' %(key, value))
# Write the annotator tag
with open(outputFile, 'a+') as f:
f.write('[Annotator "%s"]\n\n' %(engIdName))
# Save result to be written later at end of game of every game
res = game.headers['Result']
# Loop thru the moves
gameNode = game
while gameNode.variations:
side = gameNode.board().turn
fen = gameNode.board().fen()
fmvn = gameNode.board().fullmove_number
nextNode = gameNode.variation(0)
sanMove = nextNode.san()
# Get Stockfish static eval
staticEval = EvaluatePosition(engineName, fen)
# Write the move and score as comment
with open(outputFile, 'a+') as f:
if side:
f.write('%d. %s {%+0.2f} ' %(fmvn, sanMove, staticEval))
else:
f.write(' %s {%+0.2f} ' %(sanMove, staticEval))
# Don't write in one long line
if fmvn % 3 == 0:
f.write('\n')
# Read the next position
gameNode = nextNode
# Write the result and a space between games
with open(outputFile, 'a') as f:
f.write(' %s\n\n' %(res))
# Read the next game
game = chess.pgn.read_game(pgnHandle)
# Close the file handle
pgnHandle.close()
print('Done!!\n')
if __name__ == "__main__":
main(sys.argv[1:])
Sample output.
Code: Select all
[Event "4th Sinquefield Cup 2016"]
[Site "Saint Louis USA"]
[Date "2016.08.05"]
[Round "1.1"]
[White "Giri, Anish"]
[Black "Vachier-Lagrave, Maxime"]
[Result "1/2-1/2"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2769"]
[BlackElo "2819"]
[ECO "B90"]
[Opening "Sicilian"]
[Variation "Najdorf"]
[WhiteFideId "24116068"]
[BlackFideId "623539"]
[EventDate "2016.08.05"]
[Annotator "Stockfish 7 64 POPCNT"]
1. e4 {+0.08} c5 {+0.74} 2. Nf3 {+0.65} d6 {+0.95} 3. d4 {+0.39} cxd4 {+1.12}
4. Nxd4 {+0.35} Nf6 {+1.17} 5. Nc3 {+0.54} a6 {+1.06} 6. Be3 {+0.98} Ng4 {+1.11}
7. Bc1 {+1.14} Nf6 {+0.85} 8. f3 {+0.98} e5 {+0.90} 9. Nb3 {+0.27} Be6 {+0.62}
10. Be3 {+0.30} Be7 {+0.58} 11. Qd2 {+0.52} O-O {+0.46} 12. O-O-O {-0.18} Nbd7 {+0.12}
13. g4 {-0.21} b5 {-0.25} 14. g5 {-0.26} b4 {+0.16} 15. gxf6 {-0.14} bxc3 {+3.36}
16. Qxc3 {-0.52} Nxf6 {+1.16} 17. Na5 {-0.34} Rc8 {-0.47} 18. Nc6 {-0.47} Qe8 {+0.22}
19. Nxe7+ {+0.21} Qxe7 {+3.73} 20. Qa5 {-0.14} Rc6 {-0.36} 21. Kb1 {-0.37} Rfc8 {-0.05}
22. Rd2 {-0.54} Nh5 {-0.41} 23. Rg1 {-0.20} Qh4 {-0.02} 24. Be2 {-0.53} Nf4 {+0.05}
25. Bd1 {-0.25} f5 {-0.41} 26. exf5 {-0.19} Bxf5 {+0.34} 27. Ka1 {-0.51} d5 {-0.55}
28. c3 {-0.83} Rg6 {-0.71} 29. Rxg6 {-0.64} hxg6 {+3.83} 30. Bxf4 {+0.31} Qxf4 {+3.97}
31. Qxd5+ {-0.26} Kh7 {+1.24} 32. Bb3 {+1.40} a5 {+1.57} 33. a4 {+1.69} Re8 {+1.52}
34. Ka2 {+1.79} Be6 {+1.68} 35. Qc6 {+1.64} Bxb3+ {+1.05} 36. Kxb3 {-2.23} Rb8+ {+0.90}
37. Kc2 {+0.95} Rxb2+ {+0.38} 38. Kxb2 {-0.44} Qxd2+ {+5.69} 39. Kb3 {+0.30} Qxh2 {+0.06}
40. Qd5 {-0.03} Qe2 {+0.07} 41. Qxa5 {+0.03} Qd1+ {+0.16} 42. Kb2 {+0.73} Qd2+ {+0.06}
43. Kb3 {+0.66} Qd1+ {+0.44} 44. Kb2 {+0.73} Qd2+ {+0.06} 45. Ka3 {+0.66} Qc1+ {+0.24}
46. Kb4 {+0.72} Qb1+ {+0.48} 47. Ka3 {+1.17} 1/2-1/2
[Event "4th Sinquefield Cup 2016"]
[Site "Saint Louis USA"]
[Date "2016.08.05"]
[Round "1.2"]
[White "Anand, Viswanathan"]
[Black "Caruana, Fabiano"]
[Result "1/2-1/2"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2770"]
[BlackElo "2807"]
[ECO "C15"]
[Opening "French"]
[Variation "Winawer (Nimzovich) variation"]
[WhiteFideId "5000017"]
[BlackFideId "2020009"]
[EventDate "2016.08.05"]
[Annotator "Stockfish 7 64 POPCNT"]
1. e4 {+0.08} e6 {+0.74} 2. d4 {+0.13} d5 {+0.91} 3. Nc3 {+0.31} Bb4 {+0.77}
4. exd5 {+0.17} exd5 {+0.75} 5. Bd3 {-0.07} Nf6 {+0.02} 6. Ne2 {-0.34} O-O {-0.12}
7. O-O {-0.75} c6 {+0.21} 8. Bg5 {+0.04} h6 {+0.28} 9. Bh4 {-0.20} Re8 {-0.20}
10. f3 {-0.46} Nbd7 {-0.35} 11. Qd2 {-0.34} Nf8 {-0.46} 12. Rae1 {-0.33} Bd7 {-0.22}
13. a3 {-0.30} Be7 {+0.20} 14. Bf2 {+0.21} Ng6 {+0.17} 15. Bg3 {+0.25} Nh5 {+0.26}
16. Bxg6 {+0.33} fxg6 {+3.64} 17. Be5 {+0.17} Bh4 {+0.27} 18. Rd1 {+0.04} Bg5 {+0.03}
19. f4 {-0.00} Be7 {+0.34} 20. h3 {+0.07} Be6 {+0.00} 21. Kh2 {+0.12} Nf6 {-0.47}
22. Nc1 {-0.13} h5 {-0.59} 23. Nd3 {-0.22} Bf5 {+0.14} 24. Ne2 {-0.15} Ne4 {-0.37}
25. Qe3 {-0.92} h4 {-0.93} 26. Rc1 {-0.76} Rc8 {-0.98} 27. c3 {-0.83} Qb6 {-0.82}
28. b4 {-0.69} Bf6 {-1.01} 29. Nc5 {-1.07} Nxc5 {-1.12} 30. bxc5 {-3.48} Qb2 {+0.38}
31. Ng1 {-0.45} b6 {-1.62} 32. Nf3 {-1.36} bxc5 {-1.42} 33. dxc5 {-1.95} Be4 {-1.84}
34. Rce1 {-1.11} Re7 {-1.09} 35. Bxf6 {-0.81} gxf6 {+2.68} 36. Nxh4 {-0.83} Rce8 {-0.13}
37. Qg3 {-0.19} Rg7 {-0.44} 38. Ra1 {-0.07} g5 {-0.25} 39. fxg5 {-0.86} Rxg5 {+1.15}
40. Qf2 {-0.09} Qxc3 {-0.26} 41. Qxf6 {-1.65} Qg3+ {+1.83} 42. Kg1 {+3.78} Qxg2+ {+2.36}
43. Nxg2 {-0.44} Rxg2+ {+11.77} 44. Kh1 {+8.47} Rf2+ {+8.16} 45. Kg1 {+8.09} 1/2-1/2
[Event "4th Sinquefield Cup 2016"]
[Site "Saint Louis USA"]
[Date "2016.08.05"]
[Round "1.3"]
[White "So, Wesley"]
[Black "Nakamura, Hikaru"]
[Result "1-0"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2771"]
[BlackElo "2791"]
[ECO "E06"]
[Opening "Catalan"]
[Variation "closed, 5.Nf3"]
[WhiteFideId "5202213"]
[BlackFideId "2016192"]
[EventDate "2016.08.05"]
[Annotator "Stockfish 7 64 POPCNT"]
1. d4 {+0.08} Nf6 {+0.70} 2. c4 {+0.27} e6 {+0.48} 3. Nf3 {+0.02} d5 {+0.44}
4. g3 {-0.21} Be7 {-0.19} 5. Bg2 {-0.20} O-O {-0.10} 6. O-O {-0.71} dxc4 {-0.07}
7. Ne5 {-0.67} Nc6 {-0.31} 8. Nxc6 {-0.68} bxc6 {+2.47} 9. Na3 {-0.41} Bxa3 {-0.38}
10. bxa3 {-2.81} Ba6 {-0.41} 11. Qd2 {-0.46} Rb8 {-0.33} 12. Qa5 {-0.52} Qc8 {-0.75}
13. a4 {-0.55} Rd8 {-0.62} 14. Ba3 {-0.80} Rxd4 {-0.67} 15. Rfb1 {-1.17} Rb6 {-1.01}
16. Bc5 {-1.19} Rd7 {-0.60} 17. Rd1 {-0.95} h6 {-0.97} 18. Rxd7 {-0.90} Nxd7 {+4.09}
19. Bxb6 {-1.24} cxb6 {+3.54} 20. Qd2 {-1.51} c5 {-0.47} 21. Rd1 {-0.26} Nf6 {+0.17}
22. Kf1 {+0.10} Kh7 {-0.02} 23. Qc2+ {+0.30} Kg8 {-0.09} 24. Qd2 {+0.03} Kh7 {-0.02}
25. Qd8 {+0.30} Qxd8 {-0.09} 26. Rxd8 {-10.43} c3 {-0.40} 27. Ke1 {-1.19} Bc4 {-1.08}
28. Kd1 {-1.23} Bxa2 {-0.35} 29. Kc2 {-0.46} Bc4 {+0.44} 30. e3 {-0.15} b5 {+0.27}
31. Kxc3 {+0.28} a6 {+0.40} 32. Ra8 {+0.61} Nd5+ {+0.60} 33. Bxd5 {+0.60} exd5 {+3.66}
34. a5 {+0.74} b4+ {+0.58} 35. Kd2 {+0.49} Bf1 {-0.57} 36. Rc8 {-0.24} c4 {+0.17}
37. Rb8 {-0.22} b3 {+0.57} 38. Kc3 {+0.76} 1-0
[Event "4th Sinquefield Cup 2016"]
[Site "Saint Louis USA"]
[Date "2016.08.05"]
[Round "1.4"]
[White "Ding, Liren"]
[Black "Aronian, Levon"]
[Result "1/2-1/2"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2755"]
[BlackElo "2792"]
[ECO "D37"]
[Opening "QGD"]
[Variation "4.Nf3"]
[WhiteFideId "8603677"]
[BlackFideId "13300474"]
[EventDate "2016.08.05"]
[Annotator "Stockfish 7 64 POPCNT"]
1. d4 {+0.08} Nf6 {+0.70} 2. c4 {+0.27} e6 {+0.48} 3. Nf3 {+0.02} d5 {+0.44}
4. Nc3 {-0.21} Nbd7 {+0.09} 5. Bg5 {-0.07} h6 {+0.20} 6. Bh4 {-0.23} Be7 {-0.41}
7. e3 {-0.45} O-O {+0.17} 8. Rc1 {-0.38} c5 {-0.52} 9. dxc5 {-0.60} dxc4 {-0.40}
10. Bxc4 {-0.61} Nxc5 {+0.24} 11. O-O {-0.52} a6 {+0.20} 12. Nd4 {+0.14} Nce4 {-0.03}
13. Nxe4 {+0.15} Nxe4 {+3.50} 14. Bxe7 {+0.02} Qxe7 {+4.79} 15. Qc2 {+0.57} Nf6 {+0.57}
16. Bb3 {+0.70} Rb8 {+0.95} 17. e4 {+1.12} Rd8 {+0.99} 18. Rfd1 {+0.54} e5 {+0.84}
19. Nf5 {+0.34} Bxf5 {+1.88} 20. exf5 {-2.32} Rxd1+ {+0.29} 21. Rxd1 {-4.90} e4 {+0.34}
22. Qc3 {+0.50} Rd8 {+0.47} 23. Rxd8+ {+0.22} Qxd8 {+5.59} 24. h3 {+0.24} h5 {+0.04}
25. Qe5 {+0.25} b5 {+0.10} 26. g3 {+0.16} Qd7 {-0.09} 27. g4 {+0.15} hxg4 {+0.19}
28. hxg4 {-1.13} Nxg4 {+0.03} 29. Qxe4 {-0.91} Nf6 {+0.04} 30. Qa8+ {-0.17} Kh7 {+0.02}
31. Qh1+ {+0.12} 1/2-1/2
[Event "4th Sinquefield Cup 2016"]
[Site "Saint Louis USA"]
[Date "2016.08.05"]
[Round "1.5"]
[White "Topalov, Veselin"]
[Black "Svidler, Peter"]
[Result "1-0"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2761"]
[BlackElo "2751"]
[ECO "C88"]
[Opening "Ruy Lopez"]
[Variation "closed, anti-Marshall 8.a4"]
[WhiteFideId "2900084"]
[BlackFideId "4102142"]
[EventDate "2016.08.05"]
[Annotator "Stockfish 7 64 POPCNT"]
1. e4 {+0.08} e5 {+0.74} 2. Nf3 {+0.08} Nc6 {+0.59} 3. Bb5 {+0.07} a6 {+0.16}
4. Ba4 {-0.36} Nf6 {-0.36} 5. O-O {-0.87} Be7 {-0.26} 6. Re1 {-0.20} b5 {-0.17}
7. Bb3 {-0.78} O-O {+0.07} 8. a4 {-0.57} b4 {-0.65} 9. d3 {-0.52} d6 {+0.09}
10. a5 {-0.26} Be6 {-0.34} 11. Bxe6 {-0.47} fxe6 {+2.69} 12. Nbd2 {-0.48} d5 {-0.20}
13. c3 {-0.22} Bd6 {-0.31} 14. d4 {-0.12} bxc3 {+0.05} 15. bxc3 {-1.21} exd4 {-0.02}
16. cxd4 {-1.23} dxe4 {+0.21} 17. Nxe4 {-1.26} Bb4 {+0.62} 18. Bd2 {+0.16} Nxe4 {+0.60}
19. Rxe4 {-3.10} Qd5 {+0.44} 20. Bxb4 {+0.13} Qxe4 {+3.81} 21. Bxf8 {-1.11} Rxf8 {+3.20}
22. Rc1 {+0.10} h6 {+0.14} 23. Qd2 {+0.25} Rb8 {+0.30} 24. Qe3 {+0.35} Qd5 {+0.21}
25. h3 {+0.20} Rb4 {+0.09} 26. Qc3 {+0.16} Nxd4 {-0.03} 27. Qxb4 {-0.52} Ne2+ {+4.90}
28. Kh1 {+4.84} 1-0