### Re: Understanding first rank attack state generation

Posted:

**Wed Oct 09, 2019 6:53 pm**My guess is because masking by 2*63 already gives you a value that's already multiplied by two, masking out the LSBit

Hosted by Your Move Chess & Games - chessusa.com

http://talkchess.com/forum3/

Page **2** of **2**

Posted: **Wed Oct 09, 2019 6:53 pm**

My guess is because masking by 2*63 already gives you a value that's already multiplied by two, masking out the LSBit

Posted: **Thu Oct 10, 2019 3:21 am**

May be correct ("**May be!**"). But the following argument makes me think otherwise:

When you generate rank attacks (generate_rank_attacks() - see my post earler for the code) it is invoked as:

Ideally every square is multiplied by 2 (the multiplication by 2 is needed since the edge bits should be masked and only 6 bits should be considered) and passed as occupancy to generate_rank_attacks() which then works it way through the file bits stopping at the blocker bit in either directions to generate rank attacks. This is when initializing the rank attacks. But what happens when looking up for the rank attacks when you actually get an rank occupancy bitboard is what is shown in the code below:

What I am still not convinced with my understanding and reasoning is this:

When looking up given the rank occupancy bit board after masking it through right shift rank multiplier (rank * 8 - this gets the rank occupancy to LSBits) and masking off LSBits (2 * 63 = 126 = 0b01111110 - take only 6 bits) the calculation of the index using the formula as shown below is still not very clear and I'm not understanding what's written in CPW page regarding scaling by 4 instead of 8. I think this in first place requires a clarification and second the wiki page should be corrected to read the statement properly so that layman like me can understand much better.

When you generate rank attacks (generate_rank_attacks() - see my post earler for the code) it is invoked as:

Code: Select all

`generate_rank_attacks(2 * sq, file);`

Code: Select all

```
BYTE arrFirstRankAttacks64x8[64*8]; // 512 Bytes = 1/2KByte
U64 rankAttacks(U64 occ, enumSquare sq) {
unsigned int file = sq & 7;
unsigned int rkx8 = sq & 56; // rank * 8
occ = (occ >> rkx8) & 2*63;
U64 attacks = arrFirstRankAttacks64x8[4*occ + file];
return attacks << rkx8;
}
```

When looking up given the rank occupancy bit board after masking it through right shift rank multiplier (rank * 8 - this gets the rank occupancy to LSBits) and masking off LSBits (2 * 63 = 126 = 0b01111110 - take only 6 bits) the calculation of the index using the formula as shown below is still not very clear and I'm not understanding what's written in CPW page regarding scaling by 4 instead of 8. I think this in first place requires a clarification and second the wiki page should be corrected to read the statement properly so that layman like me can understand much better.

Code: Select all

`4*occ + file`

Posted: **Thu Oct 10, 2019 5:52 am**

Sorry for the confusion. I will comment the routine a little bit more ...kalyan4code wrote: ↑Thu Oct 10, 2019 3:21 amWhat I am still not convinced with my understanding and reasoning is this:Code: Select all

`BYTE arrFirstRankAttacks64x8[64*8]; // 512 Bytes = 1/2KByte U64 rankAttacks(U64 occ, enumSquare sq) { unsigned int file = sq & 7; unsigned int rkx8 = sq & 56; // rank * 8 occ = (occ >> rkx8) & 2*63; U64 attacks = arrFirstRankAttacks64x8[4*occ + file]; return attacks << rkx8; }`

When looking up given the rank occupancy bit board after masking it through right shift rank multiplier (rank * 8 - this gets the rank occupancy to LSBits) and masking off LSBits (2 * 63 = 126 = 0b01111110 - take only 6 bits) the calculation of the index using the formula as shown below is still not very clear and I'm not understanding what's written in CPW page regarding scaling by 4 instead of 8. I think this in first place requires a clarification and second the wiki page should be corrected to read the statement properly so that layman like me can understand much better.Code: Select all

`4*occ + file`

Code: Select all

```
unsigned int rankOccX2 = (occ >> rkx8) & 2*63; // 2 times the inner six bit rank occupancy used as index
U64 attacks = arrFirstRankAttacks64x8[4*rankOccX2 + file]; // 8 * rank occupancy
```

Posted: **Thu Oct 10, 2019 6:04 am**

Thanks Gerd. That was clear to me already. But still my question remains unanswered:Sorry for the confusion. I will comment the routine a little bit more ...

While looking up the rankattacks array the computation if index uses occupancy multipled by 4 instead of 8, why?

Code: Select all

`4*occ + file`

Posted: **Thu Oct 10, 2019 9:38 am**

Because of the inner six bit mask with 01111110b, which would usually require an additional shift right 1 for a 0..63 range, is already implicitly multiplied by two (0,2,4,....126). With a two-dimensional array it takes one additional instruction

Code: Select all

```
BYTE arrFirstRankAttacks64x8[64][8]; // 512 Bytes = 1/2KByte
U64 rankAttacks(U64 occ, enumSquare sq) {
unsigned int file = sq & 7;
unsigned int rkx8 = sq & 56; // rank * 8
occ = ((occ >> rkx8) >> 1) & 63; // occ = (occ >> (rkx8+1)) & 63;
U64 attacks = arrFirstRankAttacks64x8[occ][file]; // aka 8*occ + file
return attacks << rkx8;
}
```

Posted: **Thu Oct 10, 2019 1:30 pm**

Thanks Gerd. Appreciate this level of clarity described in this CPW page/section.Gerd Isenberg wrote: ↑Thu Oct 10, 2019 9:38 amBecause of the inner six bit mask with 01111110b, which would usually require an additional shift right 1 for a 0..63 range, is already implicitly multiplied by two (0,2,4,....126). With a two-dimensional array it takes one additional instruction

Code: Select all

`BYTE arrFirstRankAttacks64x8[64][8]; // 512 Bytes = 1/2KByte U64 rankAttacks(U64 occ, enumSquare sq) { unsigned int file = sq & 7; unsigned int rkx8 = sq & 56; // rank * 8 occ = ((occ >> rkx8) >> 1) & 63; // occ = (occ >> (rkx8+1)) & 63; U64 attacks = arrFirstRankAttacks64x8[occ][file]; // aka 8*occ + file return attacks << rkx8; }`