Material

    The material tables in Rybka were one of the more interesting features introduced. Their implementation was a new way to evaluate material imbalances. The indexing and evaluations in the table seem to be unique, but there are some very interesting similarities in the information stored in the table with Fruit.

Structure

    Rybka's material tables are implemented as a massive data structure that is indexed by the count of every piece on the board. The count of each piece is limited to a reasonable maximum, that can only be exceeded by promotions. This is done to keep the table a reasonable size. Pawns have a count from 0-8, minors and rooks have a count from 0-2, and queens are from 0-1. The total size of the table is thus 9*9*3*3*3*3*3*3*2*2 entries. The table is indexed in a sort of split base, with the pawn counts as the most significant indices. This means that while positions with, say, 4 queens will not overflow the index (but will point to an entry with an incorrect material configuration). The evaluation for a material configuration is stored as a 32-bit integer, which is added to the material balance determined with a sum of piece values.
    In addition to the material values, Rybka keeps flags for certain situations in the table, as well as a phase value. One flag, the flag for lazy evaluation, is only in Rybka (Fruit has no lazy eval). All of the other flags come directly from Fruit.
    The structure of the material table (at the source code level) isn't certain. It seems likely, based on the disassembly, that the data type is something like this:

Rybka Material Table Structure
struct {
unsigned char flags;
unsigned char cflags;
unsigned char phase;
bool lazy_eval;
int mat_value;
};

    The use of unsigned char and bool for phase and lazy eval are quite likely, because of their use: the assembly code uses the byte registers for dealing with them (al, bl, etc.). Phase is used only after zero-extending from a byte register to an int (hence the unsigned). Lazy eval is most likely a bool in the source because it takes the value 0 or 1, whereas the other flags use other bits, and it is not in contiguous memory with the flags (the phase field separates them).
    The flags fields are unclear, though. In Fruit, there are two sets of flags: color dependent and not. The color dependent flags are stored in a two-element array (cflags) and the others are stored in one element (flags). Each is 8 bits, giving 8 bits total. It is at least clear that flags and cflags are stored in separate fields in Rybka--they are accessed in the assembly using byte ptr semantics, with cflags taken from the stack address of flags+1.
    It seems that cflags is not an array though. It has two flags, MatWhiteKingFlag and MatBlackKing flag that are bits 0x08 and 0x80 respectively. The same flag is in Fruit, MatKingFlag, used with bit 0x08 (1 << 3). This is stored in cflags[color], to indicate the flag for both colors. In Rybka's material structure, it is as if it had the same array but with 4-bit bitfields instead of bytes (though it is not possible in C to have arrays of bitfields)--this would put the flags in the same bits that they are now. This compression of two bytes to one byte was most likely done so that each material table entry would be 8 bytes long. The use of 0x08 for a king safety flag in both programs is certainly interesting, though. The exact usage of the flags are dicussed below.
    Also, for the one flag used in Rybka in the color-independent field, DrawBishopFlag, it is stored as bit 0x80. However, Rybka's code only tests if the flags field is nonzero, so the exact value is irrelevant. In Fruit, the same flag is in bit 0x02 (1 << 1).

Flags

    Below, I compare the different material flags used in both Rybka and Fruit. I will note that all of the formulae for Rybka's flags have been decoded--since the material table is a large constant array in the Rybka executable, the code to set the flags is not there. The formulae are found by analyzing the pattern of when it appears in the material table.
    There are a set of flags in Fruit that are not in Rybka. All of these (DrawNodeFlag, MatRookPawnFlag, MatBishopFlag, and MatKnightFlag) are not included in Rybka because it does not have any separate endgame knowledge, which is the purpose of all of these flags in Fruit. Rybka has all other flags that are in Fruit,
and also an additional lazy evaluation flag. Fruit does not have lazy evaluation, so there is no flag in it.

MatKingFlag

    Fruit stores a flag for each color for whether king safety will be evaluated. This is stored as 0x08 in the cflags array in the material table (see above). The formula for this table is shown below. In Rybka, the exact same formula is used--if the enemy has a queen and at least two pieces total, king safety is evaluated for that side. Note also that cflags is a single byte, not a two-byte array as in Fruit (see above, again).

MatKingFlag in Fruit MatKingFlag in Rybka
const int MatKingFlag = 1 << 3;

if (bq >= 1 && bq+br+bb+bn >= 2)
cflags[White] |= MatKingFlag;
if (wq >= 1 && wq+wr+wb+wn >= 2)
cflags[Black] |= MatKingFlag;
const int MatWhiteKingFlag = 1 << 3;
const int MatBlackKingFlag = 1 << 7;

if (bq >= 1 && bq+br+bb+bn >= 2)
cflags |= MatWhiteKingFlag;
if (wq >= 1 && wq+wr+wb+wn >= 2)
cflags |= MatBlackKingFlag;

DrawBishopFlag

    Fruit and Rybka store a flag in their material tables for signifying the possibility of an opposite-color bishop endgame, which is generally drawish. The flag has the exact same formula in both programs: there must be only bishops and pawns, each side must have exactly one bishop, and the difference in the number of pawns of each side cannot be more than two.

DrawBishopFlag in Fruit DrawBishopFlag in Rybka
const int DrawBishopFlag = 1 << 1;

if (wq+wr+wn == 0 && bq+br+bn == 0) {
if (wb == 1 && bb == 1) {
if (wp-bp >= -2 && wp-bp <= +2) {
flags |= DrawBishopFlag;
}
}
}
const int DrawBishopFlag = 1 << 7;

if (wq+wr+wn == 0 && bq+br+bn == 0) {
if (wb == 1 && bb == 1) {
if (wp-bp >= -2 && wp-bp <= +2) {
flags |= DrawBishopFlag;
}
}
}

    The usage of these flags is just as interesting: at the very end of the evaluation, after the total score is computed, the flag is checked. Since both programs do not distinguish the color of the bishops in the material table, the flag only indicates whether an OCB ending is possible. The color of the bishops must still be checked. The actual check is done in different ways because Rybka is in bitboards, but the test has the same meaning. In Fruit, if it is really an OCB ending, the mul value is set to 8 for each side (provided a draw recognizer has not already marked this as a drawish ending). After this check, Fruit multiplies the score by mul[color]/16, with the color depending on which side is ahead. If both sides have a value of 8, as is the case when there is not a draw recognition, this has the effect of dividing the score by two, bringing it closer to the draw value, 0. In Rybka, there is no mul value, as there aren't any draw recognizers. But we see that in the case of an OCB ending, it does the same thing as Fruit: divide the score by two.
DrawBishopFlag usage in Fruit DrawBishopFlag usage in Rybka
if ((mat_info->flags & DrawBishopFlag) != 0) {
wb = board->piece[White][1];
bb = board->piece[Black][1];
if (SQUARE_COLOUR(wb) != SQUARE_COLOUR(bb)) {
if (mul[White] == 16) mul[White] = 8;
if (mul[Black] == 16) mul[Black] = 8;
}
}
if (flags & DrawBishopFlag) {
mask = Board.pieces[BB] | Board.pieces[WB];
if ((mask & MaskLightSquares) && (mask & MaskDarkSquares)) {
opening = opening / 2;
endgame = endgame / 2;
}
}

Lazy Evaluation

    In addition to the flags discussed above, Rybka stores a boolean flag for whether to perform lazy evaluation or not. Rybka has an extremely aggressive lazy eval--if the material difference (not including the material table offset) is beyond bounds set at the root based on previous iterations, the evaluation is based only on material (this time including the material table offset). In addition to these cases, there are a set of material configurations for which lazy evaluation (material only) is performed unconditionally. For instance, in a KRR vs KQN ending, Rybka does absolutely no evaluation beyond material--it simply returns a constant value, regardless of previous search values or the position of pieces. The pattern of material configurations which have this flag set is not very clear. There are 1106 such configurations (though due to symmetry there are only 553 unique ones). Each of these configurations also has in common that they are not equal (the material is imbalanced), but the difference in material value is not very large (the only configurations with more than 4 pawns difference are KNN vs K and KNN vs KP). Beyond that, though, it's not very clear. Perhaps these configurations were harvested from a collection of games and found to have some property. There are certainly too many configurations, including very obscure ones (such as KQRBPPPPP vs KQBBNN), for this to have been done by hand.
    However, there is a very serious bug in Rybka with regards to lazy evaluation. The upper and lower bounds are set to the root score at the end of every iteration that is at least 6 plies. However, Rybka deals with two different scales of evaluation: units of a centipawn and units of 1/32 of a centipawn. In this case, the two values are mixed up: Rybka's search value is in centipawns, but it sets the lazy eval as if this value were in 1/32 centipawn units. Thus, every evaluation (that happens to be less than 32 pawns in either direction, i.e. always) will cause the lazy evaluation bounds to be set based on a score of 0. This means that if the root score (before dividing by 3399) is >0, the bounds are set to -3 and 4, and if the score is <0, the bounds are set to -4 and 3. Every single position with a score outside of these bounds is lazily evaluated, which means that once the score is in this range, Rybka effectively switches to material-only evaluation.

Phase

    One of the more unique aspects of the Fruit evaluation is that it calculates two different scores, for opening and endgame, and interpolates between the two based on the phase of the game (which is calculated from the material left on the board). This was quite uncommon when Fruit first appeared (if it was used elsewhere at all), though in the meantime many other engines have begun to use this strategy. It is interesting that Rybka uses the same approach (with one interesting modification), though it is not necessarily evidence of any wrongdoing. Looking at the phase value that is used to interpolate between the two values, however, it is very clear that Rybka has copied Fruit's values.
    Both Fruit and Rybka store the phase value in the material table. Fruit's formula is pretty simple: for the opening, a phase of 0 is used, and for the endgame, 256. This is calculated by taking phase values for each piece (pawns do not count, minors count for 1, rooks for 2, and queens 4). The total of these values is subtracted from TotalPhase (which is 24). This is then expanded into the 0-256 range with a simple proportionality constant.

Phase in Fruit Phase in Rybka
static const int PawnPhase = 0;
static const int KnightPhase = 1;
static const int BishopPhase = 1;
static const int RookPhase = 2;
static const int QueenPhase = 4;

static const int TotalPhase = PawnPhase * 16 +
KnightPhase * 4 + BishopPhase * 4 +
RookPhase * 4 + QueenPhase * 2;

...

phase = TotalPhase;

phase -= wp * PawnPhase;
phase -= wn * KnightPhase;
phase -= wb * BishopPhase;
phase -= wr * RookPhase;
phase -= wq * QueenPhase;

phase -= bp * PawnPhase;
phase -= bn * KnightPhase;
phase -= bb * BishopPhase;
phase -= br * RookPhase;
phase -= bq * QueenPhase;

if (phase < 0) phase = 0;

phase = (phase * 256 + (TotalPhase / 2)) / TotalPhase;
static const int PawnPhase = 0;
static const int KnightPhase = 1;
static const int BishopPhase = 1;
static const int RookPhase = 2;
static const int QueenPhase = 4;

static const int TotalPhase = PawnPhase * 16 +
KnightPhase * 4 + BishopPhase * 4 +
RookPhase * 4 + QueenPhase * 2;

...

phase = TotalPhase;

phase -= wp * PawnPhase;
phase -= wn * KnightPhase;
phase -= wb * BishopPhase;
phase -= wr * RookPhase;
phase -= wq * QueenPhase;

phase -= bp * PawnPhase;
phase -= bn * KnightPhase;
phase -= bb * BishopPhase;
phase -= br * RookPhase;
phase -= bq * QueenPhase;

if (phase < 0) phase = 0;

phase = (phase * 256 + (TotalPhase / 2)) / TotalPhase;

phase /= 4;

    Rybka has the same formula as Fruit. There is one important difference though: in order for the value to fit into the one-byte field in the material table (which has a range of only 0-255, instead of 0-256), it is divided by 4, bringing the range from 0 to 65. There is not much loss here, since the values are extrapolated from only 25 different phase values. It is interesting to note, however, that only 25 of the values are ever possible. Rybka could have simply stored the 0-25 phase without extrapolating to a larger range. Since the phase is used to index a table (see below), this means that there are 40*2 entries which are never accessed in this table. In my opinion, this makes it clear that the original code wasn't understood fully.
    In Rybka, the final interpolation between opening and endgame scores is done using a table, phase_value[65][2]. The opening value is multiplied by phase_value[phase][0], the endgame value is multiplied by phase_value[phase][1], and these are added together. This is then divided by 256*32--the sum of each phase_value for opening and endgame is around 256, and Rybka evaluates with a base of 32 units per centipawn (with the pawn actually worth 3399, about 106 centipawns). Each of these values (256 and 32) are confirmed by looking at other places in the eval: when setting the lazy eval, Rybka multiplies by 256 and divides by the sum of the two phase values. When returning the lazy eval, it takes the material difference multiplied by 3399, adds the material table offset, and divides by 32.
    The phase_value table has values which are not quite simple, but when divided into three sections of phases (0-12, 13-51, 52-64), the values can be quite closely described by quadratic equations. This gives six total equations.