Advertisement

Hashing connect -4 board (for Transposition table)..need some help :)

Started by June 26, 2013 02:54 AM
81 comments, last by Tom Sloper 8 years, 10 months ago

Here's the code again:

u64 encode(Position const &p) {
u64 occupied = p.white | p.black;
  return ((occupied << 1) | BOTTOM_ROW) ^ p.black;
}

Hopefully it is clear what `occupied' means. `occupied << 1' means the set of squares that are above an occupied square. I compute the union of that and the bottom row (which is a constant) and now I have the set of all the squares occupied or on top of an occupied one. XORing that with the set of black pieces sets the places with black pieces to 0, leaving the unique number that I described early in this thread.

Is that clear now?

Remember to do something about what I mentioned at the end of post #17.

ok,thx. yeah,i chose a prime number for the table (do you think that 500009 is enough?) and for the bit mixing part, the function you posted already does that i suppose(?).

By the way, i do keep the lowest free square of each column in an array called height[] (size 7) but i am only using the exact 42 bits for the board,unlike john tromp's 49 bits representation. when i make a move and updating ( height[n]++ ), when i reach the column topMost square , the height[n] becomes equal to the next column lowest bottom square. (this is how i also check for if a certain column is full or not). e.g i don't have extra 7 dummy bits.

so i guess the method you showed above still works in my case..?

Advertisement

ok,thx. yeah,i chose a prime number for the table (do you think that 500009 is enough?) and for the bit mixing part, the function you posted already does that i suppose(?).


No, it does not. But you might not need it because you are using a non-power-of-2 table size.

By the way, i do keep the lowest free square of each column in an array called height[] (size 7) but i am only using the exact 42 bits for the board,unlike john tromp's 49 bits representation. when i make a move and updating ( height[n]++ ), when i reach the column topMost square , the height[n] becomes equal to the next column lowest bottom square. (this is how i also check for if a certain column is full or not). e.g i don't have extra 7 dummy bits.

so i guess the method you showed above still works in my case..?


No, in that case the code I posted is useless to you. The whole point of the 49-bit representation is to allow that trick.

Alternatively, you can still use a traditional Zobrist key, which would work just fine. Simply generate a table `long zobrist_table[42][2]', initialize it with random 64-bit numbers and XOR together the numbers corresponding to the pieces present on the board. The resulting quantity can be updated incrementally when a disk is place or removed, and it's quite cheap.

ok thanks! sorry for the confusion..I should have known that me not using the 49 bits method will not work in this case. so i think i will go for the zobrist solution,seems simple enough. Although i can't help thinking that if a combination of 2 bitboards (white and black both) is unique for each position (e.g there can't be a situation with the same white bitboard and the same black bitboard together) , there is a straight forward way to produce some kind of key (something like addition of the two bitboards together + another something smile.png ) but i afraid that if i will go for something of my own i will get identical keys / codes for different positions. so i will go for the safe and popular method of zobrist.

And another quick question if i may, i know that after making a move (an actual one) i should empty the table and start fresh (please correct me if i am wrong) .

But what about if there is no depth limit..e.g if i am doing a complete tree search. Do i still need to reinitialize the table after each move?

EDIT: while the random numbers part is rather easy for me to understand. from what i understood, one of the advantages of zobrist is that instead of hashing the entire board every time, one can update it's hash value by means of using XOR. but i can't figure out how exactly should i do it in connect 4. I would like to get some example if possible.
Or maybe this is not relevant in connect 4 since unlike chess, the pieces are not moving on the board,just being added to it..??

EDIT2: what do you say about those two functions. one is initializing the table with random numbers and the other produce the hash code. I need some review here
zobrist is a long [42] [2] array defined in the begining of this class

private void initZobrist(){
Random r = new Random();
for(int i = 0; i<42; i++){
for(int j = 0; j<2; j++){
zobrist[j] = r.nextLong();
}
}
}
public long hash(long[]board){
long hashValue = 0;
long y1 = board[0]; //white's bitBoard
long y2 = board[1]; //black's bitBoard
for(int i = 0; i<42; i++){
if((y1 & (1L<<i)) !=0){
hashValue ^= zobrist[0];
}
else if((y2 & (1L<<i)) !=0){
hashValue ^= zobrist[1];
}
}
return hashValue;
}
That code is very very slow. Make hashValue a part of the board representation and whenever you place a disk or remove a disk, ^= it with the appropriate entry from the table.
Advertisement

sorry,but i am a slow thinker smile.png when exactly should i ^= ? when i make an actual move or when i make a move inside the alpha beta function? and how (technically) this can be done?? after i make a move i get a totally new position..how (without hashin it) can i tell what to xor it with? and what did you mean by making the hashvalue a part of the board representation?

The way I structure my code, there is a class Board that looks something like this:

struct Board {
  u64 bitboard[2];
  int height[7];
  u64 hash_key;
 
  void make_move(int square, int side) {
    bitboard[side] ^= u64(1) << square;
    height[square/7]++;
    hash_key ^= zobrist_table[square][side];
  }
 
  void undo_move(int square, int side) {
    bitboard[side] ^= u64(1) << square;
    height[square/7]--;
    hash_key ^= zobrist_table[square][side];
  }
};

I don't create new positions in the search tree, but modify the existing one by calling make_move and undo_move. Does that make more sense?

ok.. first of all thx for the code,it is very helpful. and yes,ofcourse i am also doing make and unmake move (not creating new board arrays or something like this) i am working on a single board in my program. I see that you XOR that hash_key class member..but what are you doing with it? aren't you saving it in the hash table or something?

in my program i have a special class for all the transposition table functions. you can also find there the zobrist making and the insert and find method. In the alpha beta function,after i finish calculating a move, or when i get an early cut off i normally store the current board position in the hash table. and in the start of the function i search for a similar position to see if it is in the table (and for that i need to hash it again!). but you know that stuff already smile.png I just don't get how your hash_key relates to the transposition table

I'll let you think about this on your own for a little while. Ask again if it still doesn't make sense in a couple of days.

This topic is closed to new replies.

Advertisement