Material can be encoded in a single 64 bit number with a four bit counter for every piece type separated by a bit which is always 0. X is used as a special piece type that stands for having the bishop pair. (A single bit would be sufficient.)
Code: Select all
00000QQQQ0RRRR0BBBB0NNNN0PPPP0XXXX0qqqq0rrrr0bbbb0nnnn0pppp0xxxx
For the position on the board, there are two numbers, one for the total material on the board and one just for the imbalance which is what one side has more than the other.
Example: KRBBPPP-krnnpppp is encoded as
Code: Select all
0000 0QQQQ 0RRRR 0BBBB 0NNNN 0PPPP 0XXXX 0qqqq 0rrrr 0bbbb 0nnnn 0pppp 0xxxx
material: 0000 00000 00001 00010 00000 00011 00001 00000 00001 00000 00010 00100 00000
imbalance: 0000 00000 00000 00010 00000 00000 00001 00000 00000 00000 00010 00001 00000
Material evaluation is made by a list of rules. Every rule has material, imbalance and a value. To test if a rule can be applied, just a subtraction is needed. The trick is that when a rule cannot be applied to some material, there is an overflow into one of the bits that should be 0.
Pseudocode for material eval:
Code: Select all
const mask = 0000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000;
board.material = material_encoding();
board.imbalance = imbalance_encoding();
for every rule do
{
if ( ((board.material - rule.material) & mask) == 0 )
{
while ( ((board.imbalance - rule.imbalance) & mask) == 0 )
{
board.imbalance -= rule.imbalance;
eval += rule.value;
}
}
}
Theoretically it is possible to do the whole material evaluation with a list of rules. Special rules come first, general rules for a single piece last. In practice I still have classical material eval in Arminius and just some rules in the latest version coming soon.