Mike Sherwin wrote: ↑Thu Aug 25, 2022 6:52 am
I forgot to mention that when verifying a move I cycled through all the piece bits on purpose to find a matching fs. I did it that way just to verify that the structure was sound. The much faster way is just to make sure a piece of the correct type is on the fs and then check gbb[ply[fs] to see if the ts bit is set which is way faster.
Your post inspired me to improve work speed on my projects. I have been working on optimal chess code for a long time now and here is what currently works best (matching the theme of branchlessness)
I use this in my code:
Code: Select all
struct Position; //Canonical Quadboard. 256bits
struct BinaryMove
{
uint64_t clear[4];
uint64_t set[4];
};
Position apply_move(Position& pos, const BinaryMove& move)
{
const uint64_t* source = (uint64_t*)(&pos);
return
{
(source[0] & move.clear[0]) | move.set[0],
(source[1] & move.clear[1]) | move.set[1],
(source[2] & move.clear[2]) | move.set[2],
(source[3] & move.clear[3]) | move.set[3]
};
}
Which compiles down to this when used in actual code:
Code: Select all
vandps ymm0, ymm0, ymmword ptr [rsi]
vorps ymm0, ymm0, ymmword ptr [rdx + 32]
It includes promotion, enpassant, castling and all other special cases as well as these flags: Color, 4 Castling bits, Enpassant target.
So truly a code with no special cases - applicable when you generate all possible movelists accessible from a square.
The movegenerator returns the approriate pointer to the prepared movelist which is also
presorted to have captures first.
For example a move from or to H1 can unconditionally clear the castling ability for that side - which correspons to move.clear[0] |= 1ull << 56. This is done at compiletime - or initialisation time.
Master expample:
Branchless can I capture Enpassant looks like this:
Code: Select all
const bool ep_l = ((p_l << 1) & ep) != 0;
const bool epl_ok = (own_king_info.see_H(occ & ~((p_l << 1) | p_l)) & enemy_HV) == 0;
const bool ep_r = ((p_r >> 1) & ep) != 0;
const bool epr_ok = (own_king_info.see_H(occ & ~((p_r >> 1) | p_r)) & enemy_HV) == 0;
When combining the two OK bits into an index. You can have a prepared MoveList*[4] you can directly index into from that calculated branchless index. The MoveList at that location will be 0 to 2 BinaryMove