June 17, 2024
Determining the strength of a position with material value, piece-square tables, and more
Before we get to searching through trees of different moves, we have to be able to evaluate how good or bad a position is. Otter's evaluation approach is not overly advanced, and only really considers two metrics to estimate a position's strength.
The first and most immediate indicator of the strength of a position is the difference in material value. To do this, each Piece
can be assigned a material value and summed up to determine each color's material value.
Piece | Value |
---|---|
Pawn | 100 |
Knight | 300 |
Bishop | 300 |
Rook | 500 |
Queen | 900 |
King | 0 |
These rough estimates are commonly used for determining material value, where a Rook
is slightly worse than a Bishop
/Knight
pair, and a Queen
is slightly worse than a Rook
pair.
The King
is given a value of 0 because for all valid positions, each player has a single King
piece. Each player's additional value generated by a King
would just cancel out.
Just as important as material value is the position of the pieces. For example, the threat of a Pawn
promoting could single-handedly turn the tables of a game, so in that case the Pawn
should definitely be worth much more than its base value.
A convenient way of representing the value of a position is by using a piece-square table, which stores a value per square on the board. Piece-square tables allow us to help push pieces in the right direction by using positive values, or prevent pieces from doing generally poor things with negative values. They follow a lot of the general rules of chess, but don't overly influence things.
Following are heatmaps for each piece-square table used, along with any rationale for why some values were set. They were originally fetched from this wiki post. Also, note that each table is relative to White
. When used for Black
, the table is flipped so it works identically for each color.
0
0
0
0
0
0
0
0
50
50
50
50
50
50
50
50
10
10
20
30
30
20
10
10
5
5
10
25
25
10
5
5
0
0
0
20
20
0
0
0
5
-5
-10
0
0
-10
-5
5
5
10
10
-20
-20
10
10
5
0
0
0
0
0
0
0
0
-50
-40
-30
-30
-30
-30
-40
-50
-40
-20
0
0
0
0
-20
-40
-30
0
10
15
15
10
0
-30
-30
5
15
20
20
15
5
-30
-30
0
15
20
20
15
0
-30
-30
5
10
15
15
10
5
-30
-40
-20
0
5
5
0
-20
-40
-50
-40
-30
-30
-30
-30
-40
-50
-20
-10
-10
-10
-10
-10
-10
-20
-10
0
0
0
0
0
0
-10
-10
0
5
10
10
5
0
-10
-10
5
5
10
10
5
5
-10
-10
0
10
10
10
10
0
-10
-10
10
10
10
10
10
10
-10
-10
5
0
0
0
0
5
-10
-20
-10
-10
-10
-10
-10
-10
-20
0
0
0
0
0
0
0
0
5
10
10
10
10
10
10
5
-5
0
0
0
0
0
0
-5
-5
0
0
0
0
0
0
-5
-5
0
0
0
0
0
0
-5
-5
0
0
0
0
0
0
-5
-5
0
0
0
0
0
0
-5
0
0
0
5
5
0
0
0
Black
), where they might be able to flank and take pawns.-20
-10
-10
-5
-5
-10
-10
-20
-10
0
0
0
0
0
0
-10
-10
0
5
5
5
5
0
-10
-5
0
5
5
5
5
0
-5
0
0
5
5
5
5
0
-5
-10
5
5
5
5
5
0
-10
-10
0
5
0
0
0
0
-10
-20
-10
-10
-5
-5
-10
-10
-20
-30
-40
-40
-50
-50
-40
-40
-30
-30
-40
-40
-50
-50
-40
-40
-30
-30
-40
-40
-50
-50
-40
-40
-30
-30
-40
-40
-50
-50
-40
-40
-30
-20
-30
-30
-40
-40
-30
-30
-20
-10
-20
-20
-20
-20
-20
-20
-10
20
20
0
0
0
0
20
20
20
30
10
0
0
10
30
20
Both material and position values are summed up for each side and the inactive color's values are subtracted from the active color's values. This results in a relative evaluate()
function that returns a positive value if the active color is winning, or negative if the active color is losing.
type Score = i16;
def evaluate(board: &Board) -> Score {
// initially equal evaluation
let mut score = 0;
// for the active color, add values based on advantages
for active_square in board.active_pieces() {
let active_piece = board.piece_at(active_square).unwrap();
score += active_piece.material_value();
score += piece_square_table(active_piece, board.active_color(), active_square);
}
// for the inactive color, subtract values based on advantages
for inactive_square in board.inactive_pieces() {
let inactive_piece = board.piece_at(inactive_square).unwrap();
score -= inactive_piece.material_value();
score -= piece_square_table(inactive_piece, board.inactive_color(), inactive_square);
}
score
}
This is just about all that Otter uses for its evaluation function. The evaluation should not be overly complicated, but should act more as a general guide for having a good position
There are many more involved and sophisticated approaches to position evaluation that top engines employ, such as:
King
in the early game would follow some different general rules than one in the late game.King
safetyFor all of these approaches, including those that Otter implements, there are many different importances assigned to different factors. Many engines use different scales for material value or more fine-tuned piece-square tables.
Tweaking some of these different values can influence the engine in its "personality" while playing, but generally, the search function does most of the heavy lifting when it comes to finding best moves.