Chess

Author

Clayton Cafiero

Published

2026-06-02

Heuristic (static) evaluation of chess

We’ve seen the naive approach: the difference of sums of weights of pieces.

Type of piece Weight
queen 9
rook 5
bishop 3
knight 3
pawn 1


Obviously, there’s much more that we could consider when evaluating a game state:

  • attacking pieces and pieces under attack,
  • hung pieces (unprotected against attack),
  • mobility of pieces, e.g. Is a rook on an open file?,
  • exposure of king,
  • etc.

An early attempt at a chess-playing machine was by none other than Claude Shannon in the late 1940s and early 1950s. In a 1950 paper, Programming a Computer for Playing Chess (Philosophical Magazine, Series 7, 41:314, 1950), Shannon writes:

Although in chess there is no known simple and exact evaluating function f(P), and probably never will be because of the arbitrary and complicated nature of the rules of the game, it is still possible to perform an approximate evaluation of a position. Any good chess player must, in fact, be able to perform such a position evaluation. Evaluations are based on the general structure of the position, the number and kind of black and white pieces, pawn formation, mobility, etc. These evaluations are not perfect, but the stronger the player the better [their] evaluations.

Here’s a snippet of code that’s part of Stockfish’s evaluation function for “middle game” states (distinguished from opening game and end game):

function middle_game_evaluation(pos, nowinnable) {
  var v = 0;
  v += piece_value_mg(pos) - piece_value_mg(colorflip(pos));
  v += psqt_mg(pos) - psqt_mg(colorflip(pos));
  v += imbalance_total(pos);
  v += pawns_mg(pos) - pawns_mg(colorflip(pos));
  v += pieces_mg(pos) - pieces_mg(colorflip(pos));
  v += mobility_mg(pos) - mobility_mg(colorflip(pos));
  v += threats_mg(pos) - threats_mg(colorflip(pos));
  v += passed_mg(pos) - passed_mg(colorflip(pos));
  v += space(pos) - space(colorflip(pos));
  v += king_mg(pos) - king_mg(colorflip(pos));
  if (!nowinnable) v += winnable_total_mg(pos, v);
  return v;
}

piece_value_mg() computes piece values as above, but notice there are many other terms:

  • psqt_mg() (piece-square tables) computes a bonus for certain pieces being on certain squares on the board,
  • imbalance_total() takes into consideration that a bishop can only attack pieces on the same colored squares—if, for example, our king is on a white square, it cannot be checked by a bishop on a black square,
  • plus additional terms for pawn configuration, mobility and threats.

Much of such evaluation is down to the coefficients applied to various terms.

Because there are usually three distinct phases in a chess game—opening, middle game, and end game—there are often different evaluation functions applied depending on how many moves have been transacted or how much material is left on the board.

In the opening, chess engines play “by the book”, choosing from a limited set of options for positions that exist in a database of openings. If a match is found, the exact value of a position is retrieved from the book. If a match is not found, or we’ve progressed to the middle game, heuristics must be used for static evaluations. Things are somewhat simpler in the end game due to the smaller number of pieces on the board. It may be the case that certain end game configurations can be looked up in a database as well.

So you see, it’s not at all straightforward how we evaluate game states in chess. Indeed, Shannon points out

These and similar principles are only generalizations from empirical evidence of numerous games, and only have a kind of statistical validity. Probably any chess principle can be contradicted by particular counterexamples. However, from these principles one can construct a crude evaluation function.

(with minor corrections by the author from Shannon’s original typescript).

Copyright © 2023–2026 Clayton Cafiero

No generative AI was used in producing drafts of this material. This was written the old-fashioned way. AI was used to rewrite existing pseudocode in LaTeX to produce standalone *.tex files for rendering, and for revisions toward satisfying WCAG 2.1 AA-level accessibility standards as required by UVM policy. It may also have been used to proofread selected human-written prose. Claude 2.1.150 with model Sonnet 4.6. Revisions, if any, were performed by the author. AI was not used in generating any code whatsoever. All code samples and starter code are by the author only.