I did find a very efficient way to update it, namely in combination with the castling rights:
Code: Select all
int revMovesAndCastlRights = 0xF;
char spoiler[];
revMovesAndCastlRights &= (char) (spoiler[piece] + 64) | spoiler[victim];
Code: Select all
int CastlingRights = 0xF;
char spoiler[];
CastlingRights &= spoiler[piece] | spoiler[victim];
In the new code, all non-Pawns get 0x40 (=64) added to their spoiler. After adding the 64 for the piece that moved, this becomes 0x8F (which is then sign-extended to 0xFFFFFF8F). Pawns that move get to 0x4F = 0x0000004F. The first mask preserves the three high-order bytes of revMovesAndCastlRights, the second clears them. These 3 bytes then contain the reversible move count. All pieces have the sign bit of their byte-size spoiler at zero, and are tus sign-extended to a mask that clears the move counter when tey are captured. The EMPTYSQR pseudo-piece gets a castling spoiler 0xCF: this sign-extends to 0xFFFFFFFCF and thus leaves the move counter alone if it is victim (i.e. the move was a non-capture), but adding 64 turns it into 0x0F, which clears the move counter if EMPTYSQR is moved (assuming that null moves are encoded by moving from and to a dummy square which is always empty).
To increment the move counter we add 256 to revMovesAndCastlRights. For UnMake the original value, which was saved, is copied back. As it had to be done before anyway, for the benefit of the castling rights. The only extra work involved is the add instruction needed to make pieces that are moved have a different effect than pieces that are captured. This could be eliminated by having a second array of spoilers, with the 64 already added, but it is questionable if the extra demand on L1 cache space by such table would not be more detrimental to performance than having the add instruction.