It depends. In my latest engines I use copy-modify, so the original (incremental) key gets never changed. There is no performance penalty for the copy, as to operate on the key a copy of it has to be made in a CPU register anyway. So it is just a matter of whether you store the updated key back in the same memory location or in a different one. Copy-modify is only bad on large data structures that stay largely unmodified, where copying the unmodified part is pure overhead.
The method I now prefer is to define a struct 'Stairway', to contain all data to be shared between parent and child. I declare one in Search, and pass a pointer to it to its children in the recursive call. The hash key for the next child can be written in there by MakeMove(), derived from the hash key of its parent.
There never is a full hash key to recalculate. Each instance of Search only needs it once, for the hash probe. This calculates the full key, splits it into an index (entry number, or bucket number and slot number) and a signature. The latter reside in local variables, remaining available for the remainder of the routine. (Used when storing the search result in the TT.)
BTW, note that you can get rid of the side-to-move key completely, by defining the Zobrist key for an empty square to its value instead. This way, when you do
Code: Select all
newKey = oldKey ^ Zobrist[piece][fromSqr] ^ Zobrist[promotedPiece][toSqr] ^ Zobrist[victim][captSqr];
the Zobrist[victim][captSqr] will cause the color-flip when victim = EMPTY. In case of a capture the actual victim has to cause the color flip, which means you must
imagine that your tabulated Zobrist keys are the XOR of the stm Key and some 'bare' piece key. The stm key will cancel in the from- and to-contributions for the moved piece. As the keys were random to begin with, it doesn't matter whether you generate the bare key or the key that already includes the stm key. (Caveat: this does not work in the presence of multiple captures.)