Moderator: Andres Valverde
Onno Garms wrote:My original question was however how to avoid hash errors (different pawn structures with same 64-bit codes). Such errors might crash the program, for example if one stores the passers in the pawn hash table, retrive an entry that belongs to a different pawn structure, then evaluate the passers and find no pawn on the square where it is supposed to be. Do you check for such errors?
(If the answer is no, my next question is if you check legality of the trans move.)
Onno Garms wrote:Have you chosen your approach for code simplicity or for execution speed?
Hi Onno,Onno Garms wrote:For the pawn hash, obviously injective mappings from the set of all pawn structures to 64 bit numbers exist.
bob wrote:In crafty I use the same set of Zobrist randoms, since I already have some for pawns to update the actual hash signature, I just use the same pawn values for a separate incrementally-updated pawn hash key. I do not update this key unless a pawn moves or is captured or promotes, so I don't do a lot of "null-updating". I think the branches are much less important than the effect of doing random memory reads that might well not be in L1 or even in L2.
My MakeMove() function already has separate blocks for each type of piece, so only updating the pawn signature for pawn moves has zero mis-predicted branches in my code anyway...
Calculate the binomial coefficient of m over n, i.e. the number of ways to
select n objects out of a collection of m equal objects:
> binom m n = div (product [m - n + 1 .. m]) (product [1..n])
Count the number of possible pawn configurations with w white pawns
and b black pawns. Note that some of these probably cannot occur in
practise, because of the limited way pawns move.
> countPawnConfigurations (w,b) = (binom 48 w) * (binom (48 - w) b)
Count the total number of possible pawn configurations with 0..8 white
pawns and 0..8 black pawns:
> countAllPawnConfigurations =
> sum (map countPawnConfigurations [(w,b) | w <- [0..8], b <- [0..8]])
Main function:
> main = print countAllPawnConfigurations
H.G.Muller wrote:bob wrote:In crafty I use the same set of Zobrist randoms, since I already have some for pawns to update the actual hash signature, I just use the same pawn values for a separate incrementally-updated pawn hash key. I do not update this key unless a pawn moves or is captured or promotes, so I don't do a lot of "null-updating". I think the branches are much less important than the effect of doing random memory reads that might well not be in L1 or even in L2.
My MakeMove() function already has separate blocks for each type of piece, so only updating the pawn signature for pawn moves has zero mis-predicted branches in my code anyway...
Yes, if the branches are there anyway, you just put the code where it was needed. But in Joker all pieces are handled by the same code. (Which is also good from the viewpoint of I-cache misses.)
Note that the zero updates are very unlikely to be cache misses, as all non-pawns can share the same board-image of zeros. Which is further used by the main hash-key update on any non-capture. So it is likely to be used a lot, and thus always in L1.
But I admit that the L1-residence of the Zobrist random table is one of my worries, which might be one of the reasons why Joker runs like sh*t on a Pentium 4 (with its 8KB L1). A full Zobrist table takes 2x6x64 = 678 longs = 6KB. In Joker I already reduced that by two, by making it 768 normal ints, and using the black entries for the upper half of the key as the low-order half of the white entries, and vice versa. (Unfortunately partly offset by the fact that I distinguish 8 piece types, rather than 6, so that I end up with a 4KB table.)
Perhaps I should switch to the micro-Max scheme, where I only use a table of bytes, combining them into longs by non-aligned access. This reduces the table to 1KB, allowing it to reside permanently in L1 even on a P4.
Sven Schüle wrote:apart from the Zobrist key aspect which has already been discussed, I would like to know why you think that this is obvious. Your statement implies that 64 bit are sufficient to enumerate all possible pawn structures with up to 8 white and up to 8 black pawns. Could you explain your assumption?
Onno Garms wrote:- number of ways to place 8 pawns on 48 squares is 48 over 8
- to the power of 2 (for 2 colors, also counting black and white pawn on same square) gives 1,42 * 10^17
- which is less then 1% of 2^64.
__inline
void PawnStore(s32 val)
{
u32 i;
i = pawnPtr.lo & PAWNMASK;
pawnHash[i].sig = pawnPtr.hi;
pawnHash[i].val = val;
}
__inline
s32 PawnLook()
{
u32 i;
i = pawnPtr.lo & PAWNMASK;
if(pawnHash[i].sig == pawnPtr.hi)
return pawnHash[i].val;
return NOHASH;
}
Tord Romstad wrote:With a single thread, skipping legality checking of the transposition table is in practise 100% safe when you use 64-bit hash keys. The probability of an illegal move from the transposition table is vanishingly small.
joseph tadeusz wrote:Tord Romstad wrote:With a single thread, skipping legality checking of the transposition table is in practise 100% safe when you use 64-bit hash keys. The probability of an illegal move from the transposition table is vanishingly small.
Isn't it quite large (~ 1/(2^32))? See the hashing remark in http://en.wikipedia.org/wiki/Birthday_paradox
Or am I interpreting it wrongly?
jswaff wrote:Nice script! I am brushing up on my Haskell skills now; it's such a nice language.
I don't care for the literate style though, probably because my typical code to comment ratio is too high.
Next I plan to write a script to give me the match results and some stats from PGN (produced by XBoard by running an engine-engine match).
I'm not yet at the point I could write a chess program in Haskell, but I'm thinking about giving it a go one of these days...
Obviously it wouldn't be a competitive program, but it would be a lot of fun I think.
Return to Programming and Technical Discussions
Users browsing this forum: No registered users and 6 guests