Computer Chess Club Archives


Search

Terms

Messages

Subject: Re: Resources about rotated bitboards

Author: Gerd Isenberg

Date: 11:11:13 01/15/04

Go up one level in this thread


On January 15, 2004 at 12:37:03, Gerd Isenberg wrote:

>On January 15, 2004 at 11:15:58, Robert Hyatt wrote:
>
>>On January 15, 2004 at 06:38:19, Gerd Isenberg wrote:
>>
>>>On January 14, 2004 at 22:08:11, Ed Trice wrote:
>>>
>>>>Hello Federico,
>>>>
>>>>>Hi to all
>>>>>
>>>>>In my engine I use some bitboards, but nothing about rotated bitboards for
>>>>>bishop, rooks and queen moves.
>>>>>
>>>>>What chess sources or web pages can you recommend me?
>>>>>
>>>>>One answer can be Crafty. Seems to be a little difficult to understand but I
>>>>>don't tried yet :-)
>>>>>
>>>>
>>>>I definitely recommend reading the Crafty code for rotated bitboards. Basically,
>>>>you create rotated versions of the board so the shifting left and right "behave"
>>>>like diagonal displacements. For example, shifting bits from left to right in
>>>>increments of 1 would be like moving a rook horiztonally.
>>>>
>>>>But, if you lay the board out like this (a 45 degree rotation)
>>>>
>>>>a1,  b1,a2,  c1,b2,a3,   d1,c2,b3,a4,   e1,d2,c3,b4,a5,  ....
>>>>
>>>>you can perform shifts on the BITS corresponding to these squares as if you were
>>>>actually doing complex digaonl shifts.
>>>>
>>>>The hard part for will be keeping track of the length of the diagonals for all
>>>>of the rotation possibilies (rotating right 45, rotating left 45, etc.) as well
>>>>as all of the rotation data.
>>>>
>>>>--Ed
>>>
>>>Yes, for 8*8 boards there are eight files and eight ranks but 15 diagonals for
>>>each of the two diagonal directions. I my former rotated bitboard approach i
>>>used following scheme to index the diagonals eg. for a1h8 direction
>>>(file - rank):
>>>
>>>-7 -6 -5 -4 -3 -2 -1  0
>>>-6 -5 -4 -3 -2 -1  0  1
>>>-5 -4 -3 -2 -1  0  1  2
>>>-4 -3 -2 -1  0  1  2  3
>>>-3 -2 -1  0  1  2  3  4
>>>-2 -1  0  1  2  3  4  5
>>>-1  0  1  2  3  4  5  6
>>> 0  1  2  3  4  5  6  7
>>>
>>>to get byte aligned diagonals i map them in following manner in the rotated
>>>board:
>>>
>>> 7 -1 -1 -1 -1 -1 -1 -1
>>> 6  6 -2 -2 -2 -2 -2 -2
>>> 5  5  5 -3 -3 -3 -3 -3
>>> 4  4  4  4 -4 -4 -4 -4
>>> 3  3  3  3  3 -5 -5 -5
>>> 2  2  2  2  2  2 -6 -6
>>> 1  1  1  1  1  1  1 -7
>>> 0  0  0  0  0  0  0  0
>>>
>>>With these mapping, you get the occupied state for the desired diagonals by byte
>>>access without further shifting. Note that using only three lower bits as
>>>diagonal index leads to following mapping scheme (file - rank) & 7
>>>(ambiguous, but dosn't matter due to square index):
>>>
>>> 1  2  3  4  5  6  7  0
>>> 2  3  4  5  6  7  0  1
>>> 3  4  5  6  7  0  1  2
>>> 4  5  6  7  0  1  2  3
>>> 5  6  7  0  1  2  3  4
>>> 6  7  0  1  2  3  4  5
>>> 7  0  1  2  3  4  5  6
>>> 0  1  2  3  4  5  6  7
>>>
>>> 7  7  7  7  7  7  7  7
>>> 6  6  6  6  6  6  6  6
>>> 5  5  5  5  5  5  5  5
>>> 4  4  4  4  4  4  4  4
>>> 3  3  3  3  3  3  3  3
>>> 2  2  2  2  2  2  2  2
>>> 1  1  1  1  1  1  1  1
>>> 0  0  0  0  0  0  0  0
>>>
>>>Gerd
>>
>>
>>Note that I do this in a simple way also.
>>
>>IE if you have a diagonal with only 3 bits, I still use the
>>entire 8 bit (actually 6 bits if you read the paper) contents.
>>
>>IE rather than trying to figure out how long the diagonal really
>>is, I initialize the attack bitmap for that diagonal.  To explain
>>the diagonal gets right-aligned on the shift, but rather than ANDing
>>the good diagonal bits and turning the rest to zero, I get this:
>>
>>BBBBBGGG
>>
>>where BBBBB are the 5 bits that don't belong to the 3 bit diagonal, while
>>GGG are the three good bits.
>>
>>I then initialize all 256 (actually 64 as the paper says, but 256 is
>>easier to explain here) so that for all BBBBB in the above 8 bits, the
>>following is true:
>>
>>00000GGG = attacks for GGG
>>00001GGG = attacks for GGG
>>00010GGG = attacks for GGG
>>  ...
>>11111GGG = attacks for GGG
>>
>>This is slightly cache unfriendly, but since I really ignore the first and
>>last bit anyway, this table is only 64*64*8 bytes long, which is not that
>>bad for today's cache sizes.  And it eliminates trying to AND off the BBBBB
>>bits above to make them 00000.
>>
>>IE here is how I produce bishop moves (macro in chess.h):
>>
>>#  define AttacksDiaga1(a)                                                 \
>>      bishop_attacks_rl45[(a)][(tree->pos.occupied_rl45>>                  \
>>                                bishop_shift_rl45[(a)])&63]
>>#  define AttacksDiagh1(a)                                                 \
>>      bishop_attacks_rr45[(a)][(tree->pos.occupied_rr45>>                  \
>>                                bishop_shift_rr45[(a)])&63]
>>
>
>
>My former attack getters were similar. But i didn't use the the bishop_shift
>lookup and the 64-bit shift right to get the occupied state.
>
>Instead i calculate the index (0..7) of the diagonals with
>
> (File(a)-Rank(a)) & 7      (a1h8)
> ((File(a)^7)-Rank(a)) & 7  (h1a8)

As i see my old sources now, a bit optimized:

 (a-Rank(a)) & 7   (a1h8) // (file - rank) & 7
 (~a-Rank(a)) & 7  (h1a8) // (mirrored file - rank) & 7

Inline members of my CNode class:

__forceinline BitBoard A1H8Attacks(unsigned int sq) const {
  return sA1H8Atta[sq][(*(((BYTE*)&(m_Inc.m_OccuBBA1H8))+((sq-Rank(sq))&7))
                       & 0x7e) >> 1];
}
__forceinline BitBoard H1A8Attacks(unsigned int sq) const {
  return sH1A8Atta[sq][(*(((BYTE*)&(m_Inc.m_OccuBBH1A8))+((~sq-Rank(sq))&7))
                       & 0x7e) >> 1];
}

Rank(sq) is sq>>3 of course.

Unfortunately the compiler didn't understand the "even" 2*state semantic due to
the previous "and 0x7e", and was not able to combine the final shift right with
the "sizeof(BitBoard)*state" address calculation.

Therefore i had even more unreadable versions of these inliners without >> 1,
but casts, using 2*state as 32-bit index to get state as 64-bit index ;-)


>
>and did a byte-access on the rotated occupied board with that index to get the
>state. Due to the 6 innner bits have to be masked to shrink the arrays, i and
>with 0x7e to get 2*state.
>
>I guess with 64-bit shift available your method requires less instructions and
>is favorable on 64-bit machines, even more if your rotated occupied bitboard is
>already inside a register.
>
>Another possible drawback of my method: the byte access requires the occupied
>board in memory, to cast BitBoard* to BYTE* and access via *(BytePtr+index).
>That may yield to load/store forewarding stalls, if short after the 64-bit store
>(in MakeMove) an "unsigned" byte load follows inside the same memory area.
>
>In the meantime, i don't use any rotated stuff anymore, but fill-algorithms and
>x^(x-2) trick with SIMD-instructions (MMX,SSE2) ;-)
>
>Gerd
>
>
>>bishop_attacks_rl45[64][64] is the pre-computed attack bitmaps, and is
>>interpreted as bishop_attacksa_rl45[square][diag_contents].  In the above,
>>just taking one of the two diagonals, what you see is
>>
>>bishop_attacks_rl45[a][b]
>>
>>a=square we are generating bishops for.
>>I then compute b = rotated_left_45degree (the occupied squares bitmap that is
>>rotated correctly for this diagonal) and shift it right bishop_shift_rr45[a]
>>bits which moves that diagonal bit set to the rightmost N bits of the value. I
>>then AND with 63 (the shift actually is one more than needed to cut off the
>>rightmost bit that does not affect attacks since it is attacked whether the
>>square is empty or occupied) which cuts off the left bit if this is a real 8-bit
>>diagonal.  I now use this value as "b" above.
>>
>>I hope that helps, if not feel free to ask questions or point out something that
>>might be faster. :)



This page took 0 seconds to execute

Last modified: Thu, 15 Apr 21 08:11:13 -0700

Current Computer Chess Club Forums at Talkchess. This site by Sean Mintz.