Hi Bob, As I mentioned in my previous post, I don't think storing a 64 bit hash signature is enough even now on hardware like at TCEC. Wasp used "only" 16G of hash memory = 1G positions, so 30 bits is used up in the address. Since the table is always full of entries, that leaves 34 bits of useful verification. So with 4 probes I think the collision rate is 4 in 2^34 or 1 in 4 billion. At 100M nps if the HT is probed at every node (not quite the case for Wasp) there should be around 360 collisions per hour of search. So I think the move retrieved from the HT cannot be played without enough verification to ensure it won't cause a crash. I added a full move legality test for each successful hash fetch and surprisingly the slowdown is only about 1%.
The code is below. I could eliminate the test for leaving king in check since that is done in the moveloop as part of my normal move legality test for pseudo-legal moves from the move generator.
John
Code: Select all
int HashMoveIsLegal(POS* pos, int side, MOVE mv)
{
SQUARE f = moveFROM(mv);
SQUARE t = moveTO(mv);
PIECE fpc = PieceType(Piece(pos,f));
PIECE tpc = PieceType(Piece(pos,t));
BITBOARD bbf = SQ2BB(f);
BITBOARD bbt = SQ2BB(t);
BITBOARD occ = Occupied(pos);
// check that the color of the moving piece is correct and that
// there is not a piece of the same color on the to_square
if (!(pos->PieceBB[side][0] & bbf) || (pos->PieceBB[side][0] & bbt)) return(0);
// if capture, test that the bit for the captured piece is set
if (tpc && !(pos->PieceBB[!side][tpc] & bbt)) return(0);
// if promotion, test that this is pawn moving to 8th rank
if ((mv & promoteAny) && (fpc != pawn || Rank(side,t) != RANK8)) return(0);
int cpc = tpc; // captured piece
int csq = t;
switch (fpc)
{
case pawn:
if (BB_PawnRays[side][f] & bbt)
{
if (t == pos->epsquare)
{
cpc = pawn;
csq = t-pawn_dir[side];
if (tpc != 0) return(0);
if (!(pos->PieceBB[!side][pawn] & SQ2BB(csq))) return(0);
}
else if (!tpc) return(0);
}
else if (t == f+pawn_dir[side])
{
if (tpc) return(0);
}
else if (t == f+2*pawn_dir[side] && Rank(side,f) == RANK2)
{
if (tpc || Piece(pos,f+pawn_dir[side])) return(0);
}
else return(0);
break;
case knight:
if (!(BB_KnightRays[f] & bbt)) return(0);
break;
case bishop:
if (!(BB_BishopRays[f] & bbt) || (BB_Between[f][t] & occ)) return(0);
break;
case rook:
if (!(BB_RookRays[f] & bbt) || (BB_Between[f][t] & occ)) return(0);
break;
case queen:
if (!((BB_BishopRays[f] | BB_RookRays[f]) & bbt) ||
(BB_Between[f][t] & occ)) return(0);
break;
case king:
if (!(BB_KingRays[f] & bbt))
{
if (t == f+2 || t == f-2)
{
if (Rank(side,f) != RANK1) return(0);
SQUARE s1,s2,s3;
if (t > f) {s1=f+1; s2=f+2; s3=f+3;}
else {s1=f-1; s2=f-2; s3=f-4;}
if (Piece(pos,s1) || Piece(pos,s2)) return(0);
if (Piece(pos,s3) != Piece(pos,f)-2) return(0);
if (SqAtakd(pos,f,!side)) return(0);
if (SqAtakd(pos,t,!side)) return(0);
if (SqAtakd(pos,s1,!side)) return(0);
}
else return(0);
}
}
// Update bitboards to remove captured piece
if (cpc)
{
pos->PieceBB[!side][cpc] ^= SQ2BB(csq);
pos->PieceBB[!side][0] ^= SQ2BB(csq);
}
// Update fsq & tsq bits of occupied piece bitboard
BITBOARD bb = (SQ2BB(f) | SQ2BB(t));
pos->PieceBB[side][0] ^= bb;
// test whether own king is attacked
SQUARE ksq = pos->kingsq[side];
if (ksq == f) ksq = t;
int legal = !SqAtakd(pos,ksq,!side);
// restore bitboards
pos->PieceBB[side][0] ^= bb;
if (cpc)
{
pos->PieceBB[!side][cpc] ^= SQ2BB(csq);
pos->PieceBB[!side][0] ^= SQ2BB(csq);
}
return(legal);
}