Passed Pawn Evaluation

    Rybka's passed pawn evaluation was originally thought to be extremely complex. In reality though, it's really very simple. Here I will show that the passed pawn evaluation is equivalent to Fruit's, except for different weights, using a quick approximation of the static exchange evaluation, and the division of the free pawn bonus into three separate bonuses.


    In showing the equivalence between Fruit and Rybka's passed pawn evaluation, it is first necessary to understand the quad() function in Fruit. quad() calculates a bonus for a passed pawn based on a minimum and a maximum, with the final score being based on the rank of the pawn. It does this using a lookup table with a value from 0 to 256. This is used as a ratio (over 256) which is how far between the minimum and maximum the final score is. If the pawn is on the 7th rank, it gets the full bonus; if it is on the 2nd or 3rd, it gets the minimum. On ranks 4-6 it gets somewhere in between.

quad() in Fruit
for (rank = 0; rank < RankNb; rank++) Bonus[rank] = 0;

Bonus[Rank4] = 26;
Bonus[Rank5] = 77;
Bonus[Rank6] = 154;
Bonus[Rank7] = 256;


int quad(int y_min, int y_max, int x) {
int y;
y = y_min + ((y_max - y_min) * Bonus[x] + 128) / 256;
return y;

    In Fruit, there are several bonuses for a passed pawn. The endgame score has a fixed minimum, and all the bonuses for the pawn simply increase the maximum. This is equivalent to adding a set endgame bonus with quad() (between PassedEndgameMin and PassedEndgameMax) and using quad() for each subsequent bonus with a minimum of 0 and a max of the bonus. This has been implemented in an optimized (and slightly confusing) way in Fruit. To illustrate this, here is Fruit's evaluation and a simplified version. They both produce the same output, but the simplified version is a bit slower.

Endgame passed pawn evaluation in Fruit Simplified version
min = PassedEndgameMin;
max = PassedEndgameMax;
delta = max - min;


// misc. bonuses
delta += bonus;


eg[att] += min;
if (delta > 0) eg[att] += quad(0,delta,rank);
eg[att] += quad(PassedEndgameMin,PassedEndgameMax,rank);


// misc. bonuses
eg[att] += quad(0,bonus,rank);


    The opening value for passed pawns is very simple in both programs: we simply add a fixed bonus based on the rank.

Fruit Rybka
op[att] += quad(PassedOpeningMin,PassedOpeningMax,rank);
opening += PassedOpening[rank];


    Rybka and Fruit both have the same basic structure in the endgame passed pawn scoring: calculate a minimum and a maximum value and interpolate the real value based on the rank of the pawn. See above for details regarding Fruit; Rybka works the same way. We start out initializing the minimum:

Fruit Rybka
min = PassedEndgameMin;


eg[att] += min;
endgame += PassedEndgame[rank];

Dangerous Bonuses

    Next, we add the "dangerous" bonuses to the endgame maximum. There are actually a few of these: if the opponent has no pieces, we detect whether the passer is unstoppable, or if our king is in a position to protect it while promoting. We then check if the passer is free; that is, it can walk to the promotion square without being blocked or captured.
    The unstoppable passer is simply a passer that isn't blocked by a friendly piece and the opponent king is outside its "square". The king passer is a passer on the 6th or 7th rank which the king defends while simultaneously defending the promotion square. These defintions are exact bitboard equivalents in Rybka, and they both receive the same bonus of UnstoppablePasser. This bonus is not based on rank like the other bonuses, but is simply the value of a queen minus the value of a pawn.
    Next is the free pawn. The opponent has pieces in this case to potentially keep our pawn from promoting, so we need to check if it can escape. In Fruit, the square in front of the pawn must be empty, and the pawn must be able to advance safely there. We use the static exchange evaluation (SEE) to make sure that even if the square is attacked by the opponent, we can recapture on the square. This check is only done on the square directly in front of the pawn in Fruit, but since Rybka is bitboard based, we can quickly do the same calculation for all squares in front of the pawn up to the promotion square. To do this we make an approximation of the SEE that is usually equivalent. We simply make sure that for every square in the promotion path that is attacked by the opponent, we also have a piece defending that square. There are some cases where this might not be the same as being able to advance safely (according to SEE), but they are rather unusual (two knights attacking, one queen defending).
    There is also one more slight difference here: in Fruit, to be a free passer, all of the above conditions must apply. In Rybka, we break the conditions down and award partial bonuses if only some of the conditions are met.

Fruit Rybka
if (board->piece_size[def] <= 1
&& (unstoppable_passer(board,sq,att) ||
king_passer(board,sq,att))) {
delta += UnstoppablePasser;
} else if (free_passer(board,sq,att)) {
delta += FreePasser;

if ((Board.pieces[BQ] | Board.pieces[BR] |
Board.pieces[BB] | Board.pieces[BN]) == 0) {
if (white_unstoppable_passer(square) ||
endgame += UnstoppablePasser;
} else {
if ((mob & Board.pieces[White]) == 0)
endgame += PassedUnblockedOwn[rank];
if ((mob & Board.pieces[Black]) == 0)
endgame += PassedUnblockedOpp[rank];
if (((~mob_w) & mob & mob_b) == 0)
endgame += PassedFree[rank];

King Distance

    Both Rybka and Fruit now apply a bonus based on the distances of both kings to the square in front of the pawn. These bonuses are applying the same way, two bonuses, one for each king, simply multiplied by the distance of that king to the square in front of the pawn. The bonuses in Rybka are also based on the rank of the pawn.

Fruit Rybka
delta -= pawn_att_dist(sq,KING_POS(board,att),att) * AttackerDistance;
delta += pawn_def_dist(sq,KING_POS(board,def),att) * DefenderDistance;
endgame -= pawn_att_dist(square,wking_square,White) * PassedAttDistance[rank];
endgame += pawn_def_dist(square,bking_square,White) * PassedDefDistance[rank];


    If the above mentioned similarities in the evaluations were all that were there, this might be simply a coincidence. The exact same set of terms are used, and the same method of accumulating opening and endgame scores is used (interpolating between maximum and minimum based on rank, fixed score for opening, bonuses increase maximum endgame score). The free passer bonuses are separated, though, and the semantics for adding bonuses are changed (albeit into a mathematically equivalent method). But we haven't yet looked at the values for the pawns. As discussed above, Fruit's bonuses are based on the Bonus array, with values {0..., 26, 77, 154, 256, 0}, where rank 4 is 26, 5 is 77, etc. Once we look at Rybka's values, we see that they are based on the same Bonus array, and are simply precalculated outputs of the quad() function. Rybka's values and their Fruit equivalents (see the simplified Fruit code above) are shown below.
    Also, note that in the Rybka code, the equivalent rank 8 value of the Bonus array is 256 (like rank 7) instead of 0 as in Fruit. This difference is completely meaningless however, since there can never be a pawn on the 8th rank.

Rybka Fruit Equivalent
int PassedOpening[8] = { 0, 0, 0, 489, 1450, 2900, 4821, 4821 };
int PassedEndgame[8] = { 146, 146, 146, 336, 709, 1273, 2020, 2020 };
int PassedUnblockedOwn[8] = { 0, 0, 0, 26, 78, 157, 262, 262 };
int PassedUnblockedOpp[8] = { 0, 0, 0, 133, 394, 788, 1311, 1311 };
int PassedFree[8] = { 0, 0, 0, 101, 300, 601, 1000, 1000 };
int PassedAttDistance[8] = { 0, 0, 0, 66, 195, 391, 650, 650 };
int PassedDefDistance[8] = { 0, 0, 0, 131, 389, 779, 1295, 1295 };
int PassedOpeningMin = 0;
int PassedOpeningMax = 4821;
int PassedEndgameMin = 146;
int PassedEndgameMax = 2020;
int PassedUnblockedOwnMax = 262;
int PassedUnblockedOppMax = 1311;
int FreePasserMax = 1000;
int AttackerDistanceMax = 650;
int DefenderDistanceMax = 1295;

    We also need to take a look at the candidate bonus. This bonus is done statically and stored in the pawn hash table, as discussed here, but we haven't looked at the values yet. We see that in Fruit candidates are scored using the same quad() function. And sure enough, Rybka's scores are based on the same array.

Rybka Fruit Equivalent
int CandidateOpening[8] = { 0, 0, 0, 382, 1131, 2263, 3763, 3763 };
int CandidateEndgame[8] = { 18, 18, 18, 181, 501, 985, 1626, 1626 };
int CandidateOpeningMin = 0;
int CandidateOpeningMax = 3763;

int CandidateEndgameMin = 18;
int CandidateEndgameMax = 1626;