I’ve been looking at evaluating chess positions using the Stockfish chess engine. I decided to write some code that computes the overall sharpness of a complete chess game.
Assuming the existence of a function that computes the sharpness of a single chess position, I figured it wouldn’t be too difficult to compute an overall measure of a complete game — just add up the sharpness values for all positions. But then I realized it wasn’t so simple. For one thing, the sum of all position sharpness values will increase as the length of a game increases. So, a more reasonable metric is the average sharpness of all the moves in a game.
But a simple average isn’t a great idea for very long games where the sharpness values of positions late in the game are typically very low, which would drag the overall average sharpness down.
So, a better idea is to compute the average sharpness of, say, moves 10 through 30. The idea here is that opening moves are often standard and shouldn’t count. This idea is reasonable but then I decided it might make more sense to define the overall sharpness of a complete chess game as the average of the 20 largest position chess position sharpness values. So, that’s what I implemented.
For a demo, I used a chess game Euwe vs. Colle from 1929. The game in PGN notation is:
[Event "Karlsbad"] [Site "Karlsbad CSR"] [Date "1929.08.18"] [Round "15"] [White "Max Euwe"] [Black "Edgar Colle"] [Result "1-0"] 1.Nf3 Nf6 2.d4 e6 3.c4 b6 4.g3 Bb7 5.Bg2 Bb4+ 6.Bd2 Bxd2+ 7.Nbxd2 d6 8.O-O O-O 9.Re1 Nbd7 10.Qc2 e5 11.Nxe5 Bxg2 12.Nxd7 Bh3 13.Nxf8 1-0
So there are a total of 26 individual positions, including the starting position. I used a short game because computing the sharpness of chess positions using Stockfish is quite slow.
My function that computes the sharpness of an individual chess position requires positions in FEN format. I have a tool that converts a game file in PGN notation to a file of FEN strings. See https://jamesmccaffreyblog.com/2024/05/15/programmatically-converting-chess-pgn-to-fen/ for an explanation. The tool code is on GitHub at https://github.com/jdmccaffrey/convert-pgn-to-fen.
The output of my game-sharpness demo looks like:
Begin game sharpness analysis
Stockfish engine parameters:
{'Debug Log File': '', 'Contempt': 0, 'Min Split Depth': 0,
'Ponder': 'false', 'MultiPV': 1, 'Skill Level': 20,
'Move Overhead': 10, 'Minimum Thinking Time': 20,
'Slow Mover': 100, 'UCI_Chess960': 'false',
'UCI_LimitStrength': 'true', 'UCI_Elo': 2000,
'Threads': 1, 'Hash': 16}
Computing game sharpness for
.\Data\euwe_colle_karlsbad_1929.fen
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 | sharpness = 7.4
rnbqkbnr/pppppppp/8/8/8/5N2/PPPPPPPP/RNBQKB1R b KQkq - 1 1 | sharpness = 13.4
rnbqkb1r/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKB1R w KQkq - 2 2 | sharpness = 10.8
rnbqkb1r/pppppppp/5n2/8/3P4/5N2/PPP1PPPP/RNBQKB1R b KQkq d3 0 2 | sharpness = 5.6
rnbqkb1r/pppp1ppp/4pn2/8/3P4/5N2/PPP1PPPP/RNBQKB1R w KQkq - 0 3 | sharpness = 10.4
rnbqkb1r/pppp1ppp/4pn2/8/2PP4/5N2/PP2PPPP/RNBQKB1R b KQkq c3 0 3 | sharpness = 2.6
rnbqkb1r/p1pp1ppp/1p2pn2/8/2PP4/5N2/PP2PPPP/RNBQKB1R w KQkq - 0 4 | sharpness = 23.4
rnbqkb1r/p1pp1ppp/1p2pn2/8/2PP4/5NP1/PP2PP1P/RNBQKB1R b KQkq - 0 4 | sharpness = 8.6
rn1qkb1r/pbpp1ppp/1p2pn2/8/2PP4/5NP1/PP2PP1P/RNBQKB1R w KQkq - 1 5 | sharpness = 27.2
rn1qkb1r/pbpp1ppp/1p2pn2/8/2PP4/5NP1/PP2PPBP/RNBQK2R b KQkq - 2 5 | sharpness = 19.8
rn1qk2r/pbpp1ppp/1p2pn2/8/1bPP4/5NP1/PP2PPBP/RNBQK2R w KQkq - 3 6 | sharpness = 257.8
rn1qk2r/pbpp1ppp/1p2pn2/8/1bPP4/5NP1/PP1BPPBP/RN1QK2R b KQkq - 4 6 | sharpness = 11.8
rn1qk2r/pbpp1ppp/1p2pn2/8/2PP4/5NP1/PP1bPPBP/RN1QK2R w KQkq - 0 7 | sharpness = 469.2
rn1qk2r/pbpp1ppp/1p2pn2/8/2PP4/5NP1/PP1NPPBP/R2QK2R b KQkq - 0 7 | sharpness = 9.8
rn1qk2r/pbp2ppp/1p1ppn2/8/2PP4/5NP1/PP1NPPBP/R2QK2R w KQkq - 0 8 | sharpness = 19.2
rn1qk2r/pbp2ppp/1p1ppn2/8/2PP4/5NP1/PP1NPPBP/R2Q1RK1 b kq - 1 8 | sharpness = 22.8
rn1q1rk1/pbp2ppp/1p1ppn2/8/2PP4/5NP1/PP1NPPBP/R2Q1RK1 w - - 2 9 | sharpness = 8.4
rn1q1rk1/pbp2ppp/1p1ppn2/8/2PP4/5NP1/PP1NPPBP/R2QR1K1 b - - 3 9 | sharpness = 25.8
r2q1rk1/pbpn1ppp/1p1ppn2/8/2PP4/5NP1/PP1NPPBP/R2QR1K1 w - - 4 10 | sharpness = 24.4
r2q1rk1/pbpn1ppp/1p1ppn2/8/2PP4/5NP1/PPQNPPBP/R3R1K1 b - - 5 10 | sharpness = 47.0
r2q1rk1/pbpn1ppp/1p1p1n2/4p3/2PP4/5NP1/PPQNPPBP/R3R1K1 w - - 0 11 | sharpness = 83.4
r2q1rk1/pbpn1ppp/1p1p1n2/4N3/2PP4/6P1/PPQNPPBP/R3R1K1 b - - 0 11 | sharpness = 64.0
r2q1rk1/p1pn1ppp/1p1p1n2/4N3/2PP4/6P1/PPQNPPbP/R3R1K1 w - - 0 12 | sharpness = 847.0
r2q1rk1/p1pN1ppp/1p1p1n2/8/2PP4/6P1/PPQNPPbP/R3R1K1 b - - 0 12 | sharpness = 123.6
r2q1rk1/p1pN1ppp/1p1p1n2/8/2PP4/6Pb/PPQNPP1P/R3R1K1 w - - 1 13 | sharpness = 941.8
r2q1Nk1/p1p2ppp/1p1p1n2/8/2PP4/6Pb/PPQNPP1P/R3R1K1 b - - 0 13 | sharpness = 18.2
Game sharpness = 153.1
End analysis
The top 20 sharpness values are (941.8, 847.0, 469.2, 257.8, 123.6, 83.4, 64.0, 47.0, 27.2, 25.8, 24.4, 23.4, 22.8, 19.8, 19.2, 18.2, 13.4, 11.8, 10.8, 10.4). The average sharpness is (941.8 + 847.0 + . . + 10.4) / 20 = 153.1.
To implement my idea, first I downloaded the open source Stockfish engine (for Windows) from https://stockfishchess.org/download/. The Stockfish engine is like a server — it accepts commands in text format using a protocol called UCI (Universal Chess Interface) and returns results as text. Using the Stockfish engine directly from a command line is very awkward and so hobbyists have created several libraries on GitHub that allow you to use a standard programming language (Python, Java, C#, JavaScript, etc.) to interact with Stockfish.
I used the very slick, but somewhat confusingly named, Python language “stockfish” library. I installed it directly over the Internet by issuing the command “pip install stockfish” on my command line. See https://github.com/zhelyabuzhsky/stockfish.
To summarize, I wrote a Python language program that calls into functions in the stockfish library, which sends UCI commands to the Stockfish engine on my machine.
An interesting exploration. A possible next step will be to compute the average sharpness of games played by different chess players to get an indication of their playing styles.

The designs of chess sets used in world championship matches have varied greatly.
Left: A very Victorian-looking set was used in the Lasker-Schlechter match in 1910. The match was a tie at 5-5 and so Lasker retained his title.
Center: A very Soviet-looking set was used in the Tal-Botvinnik match in 1960. Tal won 12.5 – 8.5 to win the championship.
Right: A modernistic Scandinavian-looking set was used in the Carlsen-Caruana match in 2018. The match was tied 12-12 then Carlsen won in a three-game rapid chess playoff to retain his title.
Demo program. Replace “lt” (less than) and “gt” with Boolean operator symbols (my blog editor chokes on symbols).
# game_sharpness.py
# Anaconda3-2023.09-0 Python 3.11.5
# engine: Stockfish 16.1
# API library: stockfish-3.28.0
# Windows 10/11
from stockfish import Stockfish
import numpy as np
# -----------------------------------------------------------
def compute_sharpness(pos_fen):
# sharpness of a valid FEN position
stockfish.set_fen_position(pos_fen)
curr_eval = stockfish.get_evaluation()
if curr_eval is None: return 0.0
curr_val = curr_eval["value"]
bms = stockfish.get_top_moves(5)
if bms is None or len(bms) == 0:
return 0.0
sum = 0
for i in range(len(bms)): # could be less than 5
if bms[i]["Mate"] is not None: # forced mate
print("a forced mate exists")
if bms[i]["Mate"] "lt" 0: # black mates white
poss_val = -400
elif bms[i]["Mate"] "gt" 0: # white mates black
poss_val = 400
else:
poss_val = bms[i]["Centipawn"]
delta = np.abs(curr_val - poss_val)
if (curr_val "gt" 0 and poss_val "lt" 0) or \
(curr_val "lt" 0 and poss_val "gt" 0):
delta *= 2
sum += delta
return sum / len(bms)
# -----------------------------------------------------------
def game_sharpness(game_fen_file):
# average sharpness of top 20 sharpest positions
sharpness_list = []
f = open(game_fen_file, "r")
for fen_position in f:
fen_position = fen_position.strip()
pos_sharpness = compute_sharpness(fen_position)
# print("-----")
print(fen_position, end="")
print(" | sharpness = %0.1f" % pos_sharpness)
sharpness_list.append(pos_sharpness)
f.close()
# print(sharpness_list)
if len(sharpness_list) "lt" 20: # short game
n = len(sharpness_list)
else:
n = 20
sum = 0.0
sharpness_list.sort(reverse=True) # sort in-place
print(sharpness_list)
for i in range(n):
sum += sharpness_list[i]
return sum / n
# -----------------------------------------------------------
print("\nBegin game sharpness analysis ")
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 engine parameters: ")
print(p)
game_fen_file = ".\\Data\\euwe_colle_karlsbad_1929.fen"
# game_fen_file = ".\\Data\\fischer_spassky_belgrade_1992.fen"
# game_fen_file = ".\\Data\\amateur_expert_issaquah_2024.fen"
print("\nComputing game sharpness for ")
print(game_fen_file + "\n")
gs = game_sharpness(game_fen_file)
print("\nGame sharpness = %0.1f " % gs)
print("\nEnd analysis ")

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