Page 2 of 2

Re: Understanding first rank attack state generation

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

Re: Understanding first rank attack state generation

Posted: Thu Oct 10, 2019 5:21 am
by kalyan4code
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:

Code: Select all

generate_rank_attacks(2 * sq, file);
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:

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;
}
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.

Code: Select all

4*occ + file

Re: Understanding first rank attack state generation

Posted: Thu Oct 10, 2019 7:52 am
by Gerd Isenberg
kalyan4code wrote: Thu Oct 10, 2019 5:21 am

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;
}
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.

Code: Select all

4*occ + file
Sorry for the confusion. I will comment the routine a little bit more ...

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 

Re: Understanding first rank attack state generation

Posted: Thu Oct 10, 2019 8:04 am
by kalyan4code
Sorry for the confusion. I will comment the routine a little bit more ...
Thanks Gerd. That was clear to me already. But still my question remains unanswered:
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

Re: Understanding first rank attack state generation

Posted: Thu Oct 10, 2019 11:38 am
by Gerd Isenberg
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;
}

Re: Understanding first rank attack state generation

Posted: Thu Oct 10, 2019 3:30 pm
by kalyan4code
Gerd Isenberg wrote: Thu Oct 10, 2019 11: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;
}
Thanks Gerd. Appreciate this level of clarity described in this CPW page/section.