Using Math and Stockfish to find the sharpest Opening Lines
A simple method to find sharpest (sensible) opening lines with Math, Python, and Stockfish, and nice results.Results
6 Consecutive Brilliant moves in the French Sicilian, with a weird King on f7!
Definitions
Define a line L as a principal variation PV_n from Stockfish:
- L = (m_1, m_2, ..., m_T)
and the sequence of positions it produces:
- (P_0 -> P_1 -> ... -> P_T)
where P_0 is the input FEN.
Let
- Eval(P_t) be the engine evaluation of P_t in centipawns from the side to move at P_t.
Volatility
Define Volatility as the magnitude of evaluation swings along the PV.
At each position P_t, compare its evaluation to the previous position P_t-1.
- Volatility(L) = Sum from t=1 to T of |Eval(P_t) - Eval(P_t-1)| / 100
Criticality
Define Criticality as how "only-move" the position is.
At each position P_t, request Stockfish for the top 2 principal variations (MultiPV = 2) and obtain the best and second-best continuations’ evaluations Eval_1(Pt), Eval_2(Pt) from the side to move. Then:
- Criticality(L) = Sum from t=0 to T-1 of |Eval_1(Pt) - Eval_2(Pt)| / 100
Sharpness
Finally,
- Sharpness(L) = w_v * Volatility(L) + w_c * Criticality(L)
where w_V, w_C are tunable weights to put more emphasis on either metric.
Constraints
To avoid selecting sharp but objectively bad moves, we apply a root constraint.
At the root P_0, Stockfish returns MultiPV = N candidate moves with evaluations {Eval_i(P0)}.
Let the best root evaluation be:
- Eval_best(P0) = max_i Eval_i(P0)
and for a candidate line L with root evaluation Eval(L) define:
- Loss(L) = Eval_best(P_0) - Eval(L)
and we will only want to keep lines satisfying Loss(L) <= Delta where Delta is a centipawn threshold.
Algorithm
- From P0, ask Stockfish for MultiPV = N candidate moves.
- For each PV L:
- Walk through the first T plies to compute Volatility(L).
- At each P_t, ask Stockfish for MultiPV = 2 to compute Criticality(L).
- Compute Sharpness(L) and Loss(L). - Keep only lines with Loss(L) <= Delta.
- Sort remaining lines by Sharpness(L).
Well..
Even though I found a beautiful line above, my algorithm doesn't look for "beauty" with hanging pieces, and someone can probably build on this simple implementation.
And there will always be a main problem - engine search depth.
As humans, we know by experience and just by looking at it that the Fried Liver in the Italian (..Ng5) is probably the sharpest, but the engine recommends ..Bc5 to be sharper at low depth.
