Negamax AI and staying on the same turn
The reality is that the number of moves will be way less than 9x9. The remove only happens when there are 3-in-a-row which is going to be 1 or 2 times per move if at all. For the purposes of 9 men morris. You won't get any benefit from breaking up the move because an evaluation for an intermediate position (i.e. evaluating after the move and before the removal of a piece) doesn't really make sense - at least not to me :)
Other games an intermediate evaluation might be useful depending on the type of game
u64 magic_table[24] = {
0x1000000010000000ull,
0x1000000000010000ull,
0x1000000000000001ull,
0x0100000001000000ull,
0x0100000000010000ull,
0x0100000000000010ull,
0x0010000000100000ull,
0x0010000000010000ull,
0x0010000000000100ull,
0x0001000010000000ull,
0x0001000001000000ull,
0x0001000000100000ull,
0x0000100000000100ull,
0x0000100000000010ull,
0x0000100000000001ull,
0x0000010000100000ull,
0x0000010000001000ull,
0x0000010000000100ull,
0x0000001001000000ull,
0x0000001000001000ull,
0x0000001000000010ull,
0x0000000110000000ull,
0x0000000100001000ull,
0x0000000100000001ull
};
u64 new_mills(u64 magic_sum, int square) {
u64 before_mills = magic_sum & 0x4444444444444444ull;
u64 after_mills = (magic_sum + magic_table[square]) & 0x4444444444444444ull;
return after_mills & ~before_mills;
}
EDIT: I fixed a mistake in the code.
Thanks for the snippet. What do the set bits of the magic table represent and why did you use 0x4444444444444444ull ??
 
I am using 64-bit integers as groups of 16 four-bit integers ("nibbles") which count how many pieces a player has in each possible mill on the board. The table indicates which mills each square participates in (one horizontal, one vertical). Since I want to detect situations where the mill counts reach 3, I use a trick where I initialize all the counters to 1 instead of 0, because detecting if a nibble has reached 4 is a bit simpler than detecting if it has reached 3: That's what "& 0x4444444444444444ull" does.
So to make sure I'm following how did you define the board in order to find out the bits in the table?
I defined my board as a 1D array of 24 positions and it set it up like this:
// 0 1 2
// 3 4 5
// 6 7 8
// 9 10 11 12 13 14
// 15 16 17
// 18 19 20
// 21 22 23
So, taking as an example the first entry (ie. 0x1000000010000000ull), why did you decide to set the 1st and 8th bit?
By accumulating do you mean adding?
Also I had to change the code to Java. Java has 64bit long but not unsigned. I tried to do the following:
long m1 = magic_sum[0] + magic_table[0] + magic_table[1] + magic_table[2];
long m2 = magic_sum[1];
long t1 = new_mills(m1, 0);
long t2 = new_mills(m2, 1);
The result of m1 is 4688547452336345362, m2 is 1229782938247303441 whilst both t1 and t2 return 0. I don't think is correct.
/*
0--------1--------2
| 3-----4-----5 |
| | 6--7--8 | |
9-10-11 12-13-14
| | 15-16-17 | |
| 18----19----20 |
21-------22-------23
*/
`unsigned' is not important. I use unsigned integers in C/C++ when I am going to manipulate bits, because there are some surprises when you shift signed integers and because the C++ standard doesn't guarantee that overflow will behave nicely for signed integer types. But we are not shifting and we are not using the sign bit for anything in this case.That's not right. I didn't set the 1st and the 8th bit. Each hexadecimal digit represents four bits, and bits are numbered from the least-significant, starting with 0. So I set bits 28 and 60, but that's not a useful description of what I did. I indicated which two mills the square participates in by putting a hexadecimal digit 1 in two spots. Reading the numbers from left to right, I am listing all horizontal mills first, top to bottom, and all vertical mills next, left to right.So, taking as an example the first entry (ie. 0x1000000010000000ull), why did you decide to set the 1st and 8th bit?
Yes.By accumulating do you mean adding?
`new_mills' returns the new mills formed by adding a piece at the second argument. In your example, `m1' already has the first mill completed, and you can't play at `0' anyway.
long m1 = 0x1111111111111111l + magic_table[0] + magic_table[1];
long t1 = now_mills(m1, 2); // This should return 0x1000000000000000l, because 2 completes the first horizontal mill.
If you want to include diagonal mills, you need more digits. You would think that 64 bits isn't enough, but we are actually only using 3 out of every 4 bits. You can just use octal instead of hexadecimal and it would work the same.
Let me know if anything is still not clear.
 Ooops! It returns 0x4000000000000000l, of course. Sorry about that.
If you want to include diagonal mills, you need more digits. You would think that 64 bits isn't enough, but we are actually only using 3 out of every 4 bits. You can just use octal instead of hexadecimal and it would work the same.
Let me know if anything is still not clear.
I think it should return 0x4000000000000000 which I'm interpreting that the horizontal mill for square 1 is closed.
Using the same concept, is it possible to determine:
1. if mills are open (1 square is still empty, 2 owned by the player)
2. if mills are blocked (2 squares owned by the player, 1 by the opponent)
3. get adjacent squares for a square
4. get empty squares
5. etc....
And if this would be possible, I'm assuming that shifting bits needs to be done and it wouldn't work on signed values.