Stockfish(1): Chess Piece Values
What is the value of a queen, a bishop, or a knight in chess? There have been several attempts in the past, mainly in books to help assessing positions and taking a decision (e.g., when exchanging pieces). We’ll all agree that you should not give your queen for a pawn in general, but chess is full of subtlety and beauty! A Twitter thread nicely summarizes the different attempts. This nice Wikipedia article lists different proposals, including the article “Assessing Game Balance with AlphaZero: Exploring Alternative Rule Sets in Chess” from Deepmind and Vladimir Kramnik as co-author (funny affiliation: “World Chess Champion 2000–2007”). I have been intrigued to know how Stockfish (the strongest, open source chess engine) encodes and deals with chess piece values. So here we go, let’s dig into the source code and git repository!
Relative values in Stockfish
A short investigation shows that
src/types.h is a promising entry point.
More specifically, the following lines of code:
PawnValueMg = 126, PawnValueEg = 208, KnightValueMg = 781, KnightValueEg = 854, BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, QueenValueMg = 2538, QueenValueEg = 2682,
Md means middle game and
Eg means end-game. When a middle game starts and stops is another story, but it makes sense to distinguish both phases.
In fact some proposals listed in the Wikipedia article also make this distinction.
Overall, we obtain the following relative values in middle game, assuming a pawn has value 1:
(Note: The values in the Value column have been rounded to 2 digits.)
The usual piece values are pawn=1, knight=3, bishop=3, rook=5, queen=10 (see a list of piece values’ proposal or the AlphaZero article). Hence, I may have missed something, but the relative values are a bit different w.r.t pawns. Otherwise, it’s in line with what have been proposed. Another (important) note: how these relative values are leveraged as part of the search/eval functions should be taken into account. I can imagine, for instance, some heuristics to augment or downgrade relative values depending on some positions or other variables/parameters’ values (e.g., thresholds). Stated differently, pieces’ values are tuned to work along other factors in Stockfish’s algorithm (there are many other factors!)… And not necessarily to reflect on the way human chess players internalize the actual values of pieces. There is an interesting reddit discussion.
For end-games, the relative trend is similar:
(Note: The values in the Value column have been rounded to 2 digits.)
Yet, all kinds of pieces, but pawns are less powerful (intuition: a pawn is more likely to be promoted to a pawn in end-game). Numbers concur with the answer in stackexchange.
Evolution of pieces’ values
I have then investigated whether the pieces’ values have evolved during Stockfish development.
src/types.h over different commits, I’ve written a Bash script (with ChatGPT of course!):
It gives the following results:
./git_full_diff.sh src/types.h > diffs.txt cat diffs.txt | grep -e "+ QueenValueMg" + QueenValueMg = 2529, QueenValueEg = 2687, + QueenValueMg = 2528, QueenValueEg = 2698, + QueenValueMg = 2527, QueenValueEg = 2697, + QueenValueMg = 2500, QueenValueEg = 2670, + QueenValueMg = 2526, QueenValueEg = 2646, + QueenValueMg = 2513, QueenValueEg = 2648, + QueenValueMg = 2513, QueenValueEg = 2650, + QueenValueMg = 2521, QueenValueEg = 2658, + QueenValueMg = 2521, QueenValueEg = 2558, + QueenValueMg = 2521, QueenValueEg = 2558 cat diffs.txt | grep -e "+ PawnValueMg" + PawnValueMg = 124, PawnValueEg = 206, + PawnValueMg = 128, PawnValueEg = 213, + PawnValueMg = 136, PawnValueEg = 208, + PawnValueMg = 142, PawnValueEg = 207, + PawnValueMg = 175, PawnValueEg = 240, + PawnValueMg = 171, PawnValueEg = 240, + PawnValueMg = 188, PawnValueEg = 248, + PawnValueMg = 198, PawnValueEg = 258,
It is quite stable, though pawns’ values have evolved more than other pieces. And clearly there is an effort of the contributors/devs of Stockfish to carefully and empirically tune these values.
Where are static values used?
The different values above in
src/types.h are used in the code base of Stockfish, in
evaluate.cpp mainly through
For example: https://github.com/official-stockfish/Stockfish/blob/master/src/evaluate.cpp#L916-L934
Right now there are many open questions:
- How are (static) chess piece values leveraged? I’ve only scratched the surface. Frankly speaking, I was surprised to see relative values somehow hardcoded in the code base. Neural networks (with NNUE) have I think a stronger role/potential in assessing chess positions, and the values are typically learned, not even explicitly (a surrogate is certainly needed to actually compute these values out of this blackbox, see next point). How these static values participate in the search/evaluation function and combine with the NN is a question for which I don’t have answer. I have a hypothesis: (H1) there is “classical” evaluation and a NNUE evaluation, the two are simply mutually exclusive and there is no usage of static values by NNUE. Another hypothesis is that (H2) these relative piece values cannot be interpreted “as such” (as absolute values) and without understanding their exact exploitation as part of Stockfish
- How are these values updated as part of Stockfish evolution? I’m suspecting some empirical tuning, but how it’s made is unclear right now. Looking at the source code of Stockfish I’m always amazed and intrigued by the apparently ad-hoc values that are present here and there. Don’t get me wrong, these values come from some human chess knowledge and/or empirical tuning, and thus not from nowhere. But we are far from the over-simplified explanations that a chess engine is “just” a black-box that have learned a magical function evaluation. There are lots of engineering and community effort, especially for a hybrid chess engine like Stockfish. I have a hypothesis: (H3) the tuning of Stockfish occurs in many other places and for other “variables” than static relative piece values that are not that often modified.
- In the article about AlphaZero, the method to compute chess pieces’ values is described in Section 3.8. The general principle is to compute a linear model that predicts the game outcome based on the difference in numbers of each piece. As acknowledged, it is an approximation (and has the merit of being quite general to other chess variants). We can certainly apply/adapt similar methods for Stockfish (using games eg of Stockfish against itself).
Overall, I am not sure static chess pieces’ values, as encoded in Stockfish, can be transferred to human chess players’ mental model. The encoding serves a specific computational purpose and pieces’ values interact with a lot of other factors as part of Stockfish algorithm. This short blog post is actually the starting point of a (hopefully longer) series, where I will try to report on my understanding of the Stockfish source code. My writing is most probably inaccurate and incomplete (e.g., I have written 3 hypotheses that need more research). But it’s good to me to keep traces of my current knowledge. And secretly I hope to have feedbacks ;)