Trying Bit Boards for Connect-4

Started by
27 comments, last by ashish123 12 years, 11 months ago

@alvaro:Thanks for replying.
Do you ever figure out if "%7" gives you the column or the row?

Yes, I figured it out while writing the code,that "%7" will give me the (col number -1) . Its the column number which when multiplied by H1(i.e 7, it will contain values like 0,7,14,21,28,35,42) and so on.
BTW why did you ask me this? Is there any flaw? perhaps I made a mistake or call my implementation sloppy by using magic numbers like 7, which makes the code hard to read.
[/quote]


Let's look at the diagram at the top of the code:


// bitmask corresponds to board as follows in 7x6 case:
// . . . . . . . TOP
// 5 12 19 26 33 40 47
// 4 11 18 25 32 39 46
// 3 10 17 24 31 38 45
// 2 9 16 23 30 37 44
// 1 8 15 22 29 36 43
// 0 7 14 21 28 35 42 BOTTOM


Let's pick square number 11. I would say that's in column 1, but 11%7 is 4. You should be computing 11/7, which is 1.
Advertisement
@Alvaro:Thanks for replying.


Let's look at the diagram at the top of the code:

// bitmask corresponds to board as follows in 7x6 case:
// . . . . . . . TOP
// 5 12 19 26 33 40 47
// 4 11 18 25 32 39 46
// 3 10 17 24 31 38 45
// 2 9 16 23 30 37 44
// 1 8 15 22 29 36 43
// 0 7 14 21 28 35 42 BOTTOM

Let's pick square number 11. I would say that's in column 1, but 11%7 is 4. You should be computing 11/7, which is 1.[/quote]

But then lets see the this part of code:
for (int i=0; i<WIDTH; i++)
height = (byte)(H1*i);



here H1=7.

the height array will contain {0,7,14,21,28,35,42};

All columns start from 0 and end at 6.
So to start from 1 to 7, each column is incremented in print statements.

and my board[][] representation is:

00 01 02 03 04 05 06
07 08 09 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 32 33 34
35 36 37 38 39 40 41

Suppose I am playing first want to access square 38.
then the column in which 38 is given by 38%7=3.
from the code in makeMove()
int n = square % H1;//Similar to %7.
players[nplies&1] ^= 1L<<height[n]++;
moves[nplies++] = n;


height[n]=height[3]=21.
and later on it will be incremented to the next value. so now height[3] after execution of makeMove will contain 22.
Thus providing the same effect.
If I "/7" then on MY board representation, it will return me the row number.

So now taking your example of square number 11 in bitboards mask.
Assuming there were legal moves played, height[n]=height[1]=10 before this move.
square 11 in bitboard masks corresponds to 08 in MY representation.
08%7=1
so now height[n]=height[1]=11.
However with "/7" , same result is obtained for 07,08,09,10,11,12,13 i.e row 1.

Even in Mr.Tromp's code he takes the column number (entered number-1).
col = line.charAt(i) - '1';

Am I making a valid point?

and my board[][] representation is:

00 01 02 03 04 05 06
07 08 09 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 32 33 34
35 36 37 38 39 40 41


Aha! This is the part I didn't understand. You have two different encodings of the squares as numbers, which is rather confusing, and you didn't document this anywhere. I assumed the diagram on top of the code was the only encoding used.

Are you still having trouble undoing moves? Do you have a simple test that shows the problem?
@alvaro: Thanks for replying me.
I got it correct. and now trying to develop the code further.
The thing is just by changing the valuation into bitboards,my program got 4 seconds faster,playing on empty board, I think this is very cool, and really speed up, what is your take on this?.
I am trying to convert rest of my evaluation into bitboards as well but facing problem and that is how to get the fixed values of combinations.
suppose I have to check if a player has 3 in a row with threat (blank space), then how to determine the bit boards as, three in a row is possible from all the possible variations.
so without knowing the previous bitsboards of a player, how can I write a fixed value of bitboard to check for a threat condition?
I wrote a quick prototype of connect-4 program a few days ago, and this is the code I used to detect threats:

int const HEIGHT = 6;
int const WIDTH = 7;

int const H1 = HEIGHT + 1;
int const BOARD_SIZE = HEIGHT*WIDTH;

typedef unsigned long long u64;

u64 const BOTTOM = ((1ull<<(H1*WIDTH))-1) / ((1ull<<H1)-1);
u64 const COLUMN = (1ull<<HEIGHT)-1;
u64 const BOARD = BOTTOM * COLUMN;

//...

u64 threats(u64 me, u64 en) {
u64 empty = ~(me | en) & BOARD;
u64 result = 0ull;
result |= empty & (me>>H1) & (me>>(2*H1)) & (me>>(3*H1));
result |= empty & (me>>H1) & (me>>(2*H1)) & (me<<H1);
result |= empty & (me>>H1) & (me<<H1) & (me<<(2*H1));
result |= empty & (me<<H1) & (me<<(2*H1)) & (me<<(3*H1));
result |= empty & (me>>(H1+1)) & (me>>(2*(H1+1))) & (me>>(3*(H1+1)));
result |= empty & (me>>(H1+1)) & (me>>(2*(H1+1))) & (me<<(H1+1));
result |= empty & (me>>(H1+1)) & (me<<(H1+1)) & (me<<(2*(H1+1)));
result |= empty & (me<<(H1+1)) & (me<<(2*(H1+1))) & (me<<(3*(H1+1)));
result |= empty & (me>>(H1-1)) & (me>>(2*(H1-1))) & (me>>(3*(H1-1)));
result |= empty & (me>>(H1-1)) & (me>>(2*(H1-1))) & (me<<(H1-1));
result |= empty & (me>>(H1-1)) & (me<<(H1-1)) & (me<<(2*(H1-1)));
result |= empty & (me<<(H1-1)) & (me<<(2*(H1-1))) & (me<<(3*(H1-1)));
return result;
}
@alvaro:Thanks for replying.
There are few things which I donot really understand, considering this as my first attempt in bit boards I will go step by step, looking at first line of code, kindly elaborate a bit if its possible.
according to me the 1st line of code result |= empty & (me>>H1) & (me>>(2*H1)) & (me>>(3*H1));

determines three in a row horizontally.
Now I am confused with the empty square thing.
All empty squares are not threats, so how to decide whether a the empty square is threat or no, also all threats are not of same values so how to determine threat at row three and give it more importaance than other threat in row 1(how to classify threats according to rows).
I tired a simpler approach but is highly inefficient.
I used strings to get the board representation(1st inefficiency), convert that into a "long" number so that bit operators could be carried out. and then search each 3 in row with threats, convert them into hex and use bit wise operator.But the time taken by this method is far too much.
Thank you.
I am stuck again.
As mentioned in the above post, I was unable to decide or find out which piece of code is that which tells us the particualr empty square.
So I wrote the program as follow.

long bitboard=0L;
player1_bitboard=0L;
player2_bitboard=0L;
for(int sq=0;sq<dimensions_of_board;sq++)
if(board[sq]!=0)//May contain 1 or 2 , player1 and player2.
bitboard|=(1L<<sq);//setting the particular bits 1.
if(board[sq]==1)
player1_bitboard|=1L<<sq;
if(board[sq]==2)
player2_bitboard|=1L<<sq;
.
But still I am having trouble to locate the empty square. One logical answer is
long empty=~bitboard
But then how can I say that the above empty square will be in sequence of threat.
and then it sees in the pattern of three in a row.
But since there has to be a blank space for a threat,I am unable to determine whether the blank space would lie in which row.
in short I am a bit confused as how to transform the code.
if (posn[row][column] == 0 && posn[row][column + 1] == posn[row][column + 2] && posn[row][column + 2] == posn[row][column + 3] && posn[row][column + 3] == player1_piece..) {
weight=...

in bitboards.
Kindl help if any one can.
TYIA
The code in my previous post does all the things you talk about, including how to determine a bitmap of which squares are empty and how to determine where the threats are. The parameters to my function `threats' are the bitmaps with my pieces (`me') and the enemy's pieces (`en'). It will return a bitmap with all my threats marked.
@alvaro and mandar9589:
I found this thread very helpful to develop my connect-4 game.
@alvaro: Since bitboards are quiet tricky to implement, would you mind to describe each threat as what it does perhaps atleast tell us which representation does it follow

0 7 14 21 28 35 42 ...(This is the bottom row) or 35 36 37 38 39 40 41 (the array representation, bottom row).

Would also help to explain how did you derive the conditions, by using for loops its very simple just mention the conditions and its there,how to do it in bitboards.
I am asking these questions because copying your code won't help me be better, just want to know hows and whys...

The next question is the variable empty is a bitboard containing bitboards of all empty places,
so suppose if I want to see if square 35(array representation) is empty or not using the empty variable, how to go for it?

Last question is if I want to test _XXX condition for threat where _=35,X=36,37,38 , how to do this in bit boards?
in arrays very inefficently we can verify say board[35]==0&&board[36]==X...
but then I am finding this very confusing in bitboards.

even if some(or all) questions appear dumb, I request to kindly explain because I am new to bitboards.
Thank you very much.

@alvaro and mandar9589:
I found this thread very helpful to develop my connect-4 game.
@alvaro: Since bitboards are quiet tricky to implement, would you mind to describe each threat as what it does perhaps atleast tell us which representation does it follow

0 7 14 21 28 35 42 ...(This is the bottom row) or 35 36 37 38 39 40 41 (the array representation, bottom row).

The bottom row is 0 7 14 21 28 35 42. I am following the diagram from John Tromp's code:
// bitmask corresponds to board as follows in 7x6 case:
// . . . . . . . TOP
// 5 12 19 26 33 40 47
// 4 11 18 25 32 39 46
// 3 10 17 24 31 38 45
// 2 9 16 23 30 37 44
// 1 8 15 22 29 36 43
// 0 7 14 21 28 35 42 BOTTOM


Would also help to explain how did you derive the conditions, by using for loops its very simple just mention the conditions and its there,how to do it in bitboards.
I am asking these questions because copying your code won't help me be better, just want to know hows and whys...[/quote]
I am not sure what you are asking. Perhaps you can point at a few lines of my code and I'll try to explain what they do.

The next question is the variable empty is a bitboard containing bitboards of all empty places,
so suppose if I want to see if square 35(array representation) is empty or not using the empty variable, how to go for it?[/quote]
You would check if bit 35 is set. This is done by checking (((empty>>35)&1)==1). But you should probably write a bittest function that hides that detail.

Last question is if I want to test _XXX condition for threat where _=35,X=36,37,38 , how to do this in bit boards?
in arrays very inefficently we can verify say board[35]==0&&board[36]==X...
but then I am finding this very confusing in bitboards.[/quote]
You try not to check individual cells when using bitboards. You check all occurrences of a particular pattern anywhere on the board.

I'll try to give you better answers, but I don't know if I'll find the time.

This topic is closed to new replies.

Advertisement