-
Notifications
You must be signed in to change notification settings - Fork 6
Evaluation
The evaluation is -quite literally- the heart of the engine. It determines which position the engines considers as good and which it considers as bad, so that the best decision on which moves to be played, can be made.
Evaluation function
Display Evaluation
Evaluation - Search tradeoff
Phasing
Evaluation Features
Tempo
Pawn structure
Passed pawns
Knights and outposts
Rooks
Piece values
Mobility
King Safety
Attack
Piece-Square-Tables
The evaluation function is only a heuristic evaluating how good a position is for either side. In the actual implementation, the evaluation is always done from white's point of view, but since a good position for white is synonimic for a bad position for black, the evaluation is multiplied by -1
if we want the score for blacks side. An arbitrary score is assigned to a position - the higher the score the better the position is for white. This score is most often normalized to the centipawn
scale, e.g. a position where white would be up a pawn (and only a pawn) would be worth 100 cp(centipawns)
. In FabChess, no scaling is done for midgame scores, since the internal evaluation values resemble the centipawn
scale quite closely. The endgame scores are divided by 1.5 before they are used.
As the evaluation function only is a heuristic, it is often not accurate at all. This is why there is search, to compensate the weakness of the evaluation function.
Implementation | Evaluation module, Evaluation function
If you have a specific position in mind and want to see how the evaluation pans out, FabChess can display a detailed overview of its evaluation. Head over to the README, where there is a detailed manual on how to get this overview.
In a sense there is a tradeoff between a complex evaluation function and the search function - the more complex the evaluation, the slower it will be and thus less positions can be searched. It thus sometimes makes sense to let the search figure out cues about a position instead of the evaluation function. The first example would be if a passer can be stopped or if a piece is hanging. Although the mate and draw detection semantically belong to the evaluation function, it is logistically easier to have this done by the search function. That is why there is no information concerning mate/draw detection on this page. Check out the search page instead, if you are interested in that.
The evaluation is very much dependant on in which phase of the game a gamestate is in. In the opening it is usually recommended to have developed pieces and the king hidden behind his shielding pawns while in the endgame the king should be activated. The evaluation function considers this and actually has two different evaluations for the middlegame(referred to as MG
) and the endgame(referred to as EG
) between which an interpolation is done depending on the phase a gamestate is in.
The phase of a gamestate depends on how many pieces there are on the board and which pieces there are on the board. It is a value ranging from 0.0
(fully endgame) to 128.0
(fully middlegame). It is calculated by taking the sum of the values of the pieces on the board except pawns(SUM
) and then
phase = 128 * (SUM - EG_LIMIT) / (MG_LIMIT - EG_LIMIT)
where, EG_LIMIT
and MG_LIMIT
are predefined constants determining the thresholds on which a position can be considered as endgame and midgame.
Currently, a full endgame is reached when there is only one queen and a rook or two minors on the board.
The final score will be calculated based on the phase. Given a midgame score(MG_SCORE
) and an endgame score(EG_SCORE
), final evaluation will be:
final_eval = (MG_SCORE * phase + EG_SCORE * (128-phase))/128
As the phase only depends on the pieces on the board, it can easily be updated incrementally from GameState to GameState, as it is currently done in FabChess.
Implementation | MG_LIMIT, EG_LIMIT, Phase calculation, Incremental update of Phase, Interpolation of a score,
Before you jump into the Evaluation features: If some of the evaluation constants seem off to you, don't be confused. This is due to the automatic tuning. As most of the features are dependent on each other, the tuned constants might sometimes fail the human intuition for a certain feature. The evaluation consists of a set of features which together form a linear combination like this:
evaluation = (Pawns on Board White - Pawns on Board Black) * PAWN_VALUE + ... + (Queens on Board White - Queens on Board Black) * QUEEN_VALUE + ...
Almost every feature has its MG
and EG
component which are used for the midgame evaluation sum
and the endgame evaluation sum
. Both of those sums are handled in a single Struct, which combines them and also supports the normal vector operations.
Implementation | Evaluation sums, Evaluation Score struct
Tempo
Pawn structure
Passed pawns
Knights and outposts
Rooks
Piece values
Mobility
King Safety
Attack
Piece-Square-Tables
A small bonus is given to the side which is to move, as it can be estimated they can improve their position.
Doubled, isolated and backward pawns are evaluated as they provide a structural weakness to our position which might be exploited by the enemy. Doubled pawns are when 2 pawns are in the same file. A pawn is isolated when there is no allied pawn in the neighboring files. A pawn is backward when its stop (the next square it would push to) is attacked and can no longer be attacked by an allied pawn(thus a backward pawn is always isolated). Pawns supported by own pawns are evaluated based on the square they are on. Pawn attacks to the center of the board (D4,E4,D5,E5) and their mobility are also evaluated.
Implementation | Constants, Needed bitboards, Linear combination pawns, Doubled pawns, Isolated pawns, Backward pawns, Supported pawns, Center attack, Pawn mobility
Passed pawns are evaluated based on their ranks. Weak passed pawns get a small malus, with a weak passed pawn being an attacked but not defended piece (attack and defense by all pieces). A bonus is given for an unblocked passer, e.g. a passer which is not weak and all the squares upto conversion are either defended by our side or not attacked by the enemy side or unoccupied. It is also evaluated whether there is an allied or enemy rook behind the passer. Additionally, we evaluate the distance to the kings to the passer. There are 3 features for this, the first being the distance of our king to the passer, the second being the distance of the enemy king to the passer and the third being the difference of the distance of the kings to the passer. Those features reward being near to a passer in endgames and can guide the search.
Implementation | Constants, Needed bitboards, Rook behind passer, Basic passed pawn, Weak passers, Unblocked passer, Distance to kings
Knights supported by a pawn are given a slight bonus. Knights on an outpost are also evaluated based on the square they are on. A knight is on an outpost in Fabchess when it is supported by a pawn and can no longer be attacked by an enemy pawn. If the knight is black, its square is converted to the square it would be on if the board were mirrored, so that the same lookup table can be used. This mirroring is done by another table, containing the correct mapping.
Implementation | Constants, Black squares, Supported by pawn, Outpost detection
Rooks on an open file and rooks on the seventh rank are evaluated. Rooks on an open or semi-open file provide more control and are thus given a bonus, the bonus for a semi-open file being smaller than the one for the open file. The same is done for Queens, which are given a different score for them being on an open or semi-open file.
Implementation | Constants, Detection, Linear combination
The pieces still on the board give us an immediate overview over which side is stronger. For example, if we have a queen against a bishop and a pawn, this is likely to be better for our side. Every piece still on the board is evaluated on a per piece basis. The knight value is slightly adjusted based on how many pawns are still in the game. The bishop pair bonus is also evaluated. This type of evaluation can easily also be done incrementally, similar to how the evaluation of piece square tables currently works, but as some endgame knowledge depends on this(evaluating a single bishop or knight as 0), I decided it is easier not to do incrementally.
Implementation | Constants, Calculation
Piecewise mobility is an important factor in how strong a certain piece is. On a per piece basis, the mobility count is evaluated as the amount of pseudo legal moves a piece can make (not including capturing own pieces). The mobility count and the piece type are then used to evaluate the final mobility score for that piece(by lookup in corresponding tables indexed by the mobility count). For bishops, it is also evaluated how many allied pawns are on the diagonally adjacent squares. Mobility of pawns is evaluated in the pawn section.
Implementation | Constants, Mobility Knight, Bishop diagonally adjacent own pawns, Generation of diagonally adjacent bitboard, Mobility Bishop, Mobility Rook, Mobility Queen
King safety plays a major role in chess, as we ultimately want the enemy king to be unsafe enough to be checkmated. Currently, the only thing being evaluated for king safety are the shielding pawns covering the king. However, the attack feature also goes into the direction of king safety. Shielding pawns are the pawns directly forward adjacent to the king. In FabChess, if a pawn is a rank in front of a forward adjacent square of the king, it is also counted as a valid shielding pawn. Missing shielding pawns are given a malus, and if a shielding pawns is missing on an file and there is no enemy pawn on that file(so basically an open file. It could however be possible that an enemy pawn is behind the king), an even bigger malus is given. Also note that when the king is on file a or on file h, the width of the shield is extended by one square towards the center of the board. The malus which is given for missing shielding pawns depends on how many shielding pawns are missing and the score is contained in a table indexed by the amount of shielding pawns missing. The malus for missing shielding pawns on an (basically[see above why only basically]) open file is calculated the same way.
Implementation | Constants, Generation of valid shielding squares bitboard, Detection of shielding pawns missing, Evaluation of shielding pawns missing
The attack towards the enemies king is evaluated, as it plays a major role in king safety, and the goal of chess is ultimately for the enemy king to be vulnerable to being checkmated. Several things are measured. First of all, an attack is defined the following: An attack is when a pseudo legal move of a (non-pawn-)piece is towards the king zone of the enemy king (definition following) and the attacked squares are not defended by enemy minor pieces(enemy pawns, knights and bishops). An attack is also, when a pseudo legal move of a (non-pawn-) piece is towards a square which is not defended by enemy minor pieces and that piece gives check to the enemy king on that square. This is what is called a safe check(piece can safely check the enemy king). The king zone are all the squares adjacent to the king, also including all squares which are two in front of the king. The final attack score depends on the number of different attackers and the attack value. The attack value is a weighted sum of the attacked squares, the attacker type and the sum of safe checks.
attack value = sum of : for each knight: (knight_attacks * KNIGHT_ATTACK_VALUE + knight_safe_check * KNIGHT_SAFE_CHECK_VALUE) + for each bishop: ....
For the final attack score the attack value is used to index a SAFETY_TABLE
, whose score is then multiplied by the attack weight, which depends on the number of different attackers. Finally it is divided by 100(for scaling reasons):
final_attack_score = SAFETY_TABLE[attack_value] * ATTACK_WEIGHT[different_attackers] / 100
Additionally, pieces attacking the king through one piece get a slight bonus. This is called xraying
the enemy king. Those attacks are scored for rooks, bishops and queens.
Implementation | Constants, XRAY Constants, XRAY Detection, Generation of king zone bitboard, Knight attack value, Bishop attack value, Rook attack value, Queen attack value, Attack calculation
Every piece gets a bonus or malus based on the square it is currently on. Those scores are provided in tables, hence the name Piece-Square-Table. Note that for symmetry reasons, black pieces squares are mirrored in the rank axis and in the file axis, so that the same tables can be used for white and black pieces. This is done using a predefined table. The piece square table evaluation is done incrementally in FabChess, providing a minor speedup. Whenever a first position is initially constructed, their piece square table evaluation is once evaluated with a slower method. Everytime a move is then made on that position, the values of the evaluation are updated by the incremental methods.