
It is a bitboard move generator that does not make a move list. It simply generates and stores all the bitboards by [ply] and by [fs]. The philosophy is why spin all those bits into a move list before the moves are needed and lose all that information in the process. Meaning that if a killer move is being tried it is quick to verify the move by looking at gbb[ply][fs] to see if the ts bit is set, etc. It will work most efficiently when the position is legal. That is because the check for legality has become a branchless calculation rather than using an if statement. As in return 1 - (king[1 - stm] & abb[ply]);. abb[ply] is the attacked squares bitboard that is very useful in other parts of the code like for move ordering, etc. Rather than keeping castling rights in other parts of the code it just uses pseudo pieces like WKC (white king that can castle) and WRC. When a WKC moves it puts a WK on the board and castling cost no more clock cycles after that. And WRC becomes just WR when a WRC moves.
So as of now there is not a single if statement in, GenAllMoves(), GenQuiescent(), MakeMove() and TakeBack(), and that is very pleasing.

It is using Bob's classical approach which I have spent much time optimizing. However, it can be changed to use any sliding piece generators. And the pawn code is much faster than in RomiChess. I also spent a lot of time on that.
Code: Select all
bool GenAllMoves(Thread* t) {
s08 fs, ft;
u64 b, atk;
u64 pieces = piece[stm];
u64 notme = ~pieces;
u64 occ = pieces | piece[1 - stm];
u64 empty = ~occ;
u64 enemy = notme ^ empty;
abb[ply] = 0;
do {
fs = std::countr_zero(pieces);
ft = board[fs];
switch (ft) {
case ES:
// can't get here
break;
case WP:
switch (fs >> 3) {
case RANK1:
// can't get here
break;
case RANK2:
gbb[ply][fs] = ((0x0000000000000100ull << fs) & empty);
gbb[ply][fs] |= ((gbb[ply][fs] << 8) & empty);
atk = wPawnCapts[fs];
gbb[ply][fs] = atk & enemy;
abb[ply] |= atk;
break;
case RANK3:
case RANK4:
gbb[ply][fs] = ((0x0000000000000100ull << fs) & empty);
atk = wPawnCapts[fs];
gbb[ply][fs] = atk & enemy;
abb[ply] |= atk;
break;
case RANK5:
gbb[ply][fs] = ((0x0000000000000100ull << fs) & empty);
atk = wPawnCapts[fs];
gbb[ply][fs] |= (atk & (enemy | epbit[ply]));
abb[ply] |= atk;
break;
case RANK6:
case RANK7:
gbb[ply][fs] = ((0x0000000000000100ull << fs) & empty);
atk = wPawnCapts[fs];
gbb[ply][fs] = atk & enemy;
abb[ply] |= atk;
break;
}
break;
case WN:
gbb[ply][fs] = knightMoves[fs] & notme;
abb[ply] |= knightMoves[fs];
break;
case WB:
gbb[ply][fs]
= ray[std::countr_zero(ray[fs].NW & occ)].NW
| ray[std::countr_zero(ray[fs].NE & occ)].NE
| ray[std::countl_zero(ray[fs].SE & occ)].SE
| ray[std::countl_zero(ray[fs].SW & occ)].SW
^ bishopMoves[fs];
abb[ply] |= gbb[ply][fs];
gbb[ply][fs] &= notme;
break;
case WRC:
case WR:
gbb[ply][fs]
= ray[std::countr_zero(ray[fs].NN & occ)].NN
| ray[std::countr_zero(ray[fs].EE & occ)].EE
| ray[std::countl_zero(ray[fs].SS & occ)].SS
| ray[std::countl_zero(ray[fs].WW & occ)].WW
^ bishopMoves[fs];
abb[ply] |= gbb[ply][fs];
gbb[ply][fs] &= notme;
break;
case WQ:
gbb[ply][fs]
= ray[std::countr_zero(ray[fs].NW & occ)].NW
| ray[std::countr_zero(ray[fs].NE & occ)].NE
| ray[std::countl_zero(ray[fs].SE & occ)].SE
| ray[std::countl_zero(ray[fs].SW & occ)].SW
| ray[std::countr_zero(ray[fs].NN & occ)].NN
| ray[std::countr_zero(ray[fs].EE & occ)].EE
| ray[std::countl_zero(ray[fs].SS & occ)].SS
| ray[std::countl_zero(ray[fs].WW & occ)].WW
^ queenMoves[fs];
abb[ply] |= gbb[ply][fs];
gbb[ply][fs] &= notme;
break;
case WKC:
gbb[ply][fs] = kingMoves[fs] & notme;
abb[ply] |= kingMoves[fs];
gbb[ply][fs] |= (u64)((((board[h1] == WRC) + !(occ & SWCS) + !AttackedByBlack(t, AWCS)) == 3) << g1);
gbb[ply][fs] |= (u64)((((board[a1] == WRC) + !(occ & SWCL) + !AttackedByBlack(t, AWCL)) == 3) << c1);
break;
case WK:
gbb[ply][fs] = kingMoves[fs] & notme;
abb[ply] |= kingMoves[fs];
break;
case BP:
switch (fs >> 3) {
case RANK1:
// can't get here
break;
case RANK2:
case RANK3:
gbb[ply][fs] = (1ull << (fs - 8)) & empty;
atk = bPawnCapts[fs];
gbb[ply][fs] |= atk & enemy;
abb[ply] |= atk;
break;
case RANK4:
gbb[ply][fs] = (1ull << (fs - 8)) & empty;
atk = bPawnCapts[fs];
gbb[ply][fs] |= (atk & (enemy | epbit[ply]));
abb[ply] |= atk;
break;
case RANK5:
case RANK6:
gbb[ply][fs] = (1ull << (fs - 8)) & empty;
atk = bPawnCapts[fs];
gbb[ply][fs] |= atk & enemy;
abb[ply] |= atk;
break;
case RANK7:
gbb[ply][fs] = (1ull << (fs - 8)) & empty;
gbb[ply][fs] |= ((gbb[ply][fs] >> 8) & empty);
atk = bPawnCapts[fs];
gbb[ply][fs] |= atk & enemy;
abb[ply] |= atk;
break;
}
break;
case BN:
gbb[ply][fs] = knightMoves[fs] & notme;
abb[ply] |= knightMoves[fs];
break;
case BB:
gbb[ply][fs]
= ray[std::countr_zero(ray[fs].NW & occ)].NW
| ray[std::countr_zero(ray[fs].NE & occ)].NE
| ray[std::countl_zero(ray[fs].SE & occ)].SE
| ray[std::countl_zero(ray[fs].SW & occ)].SW
^ bishopMoves[fs];
abb[ply] |= gbb[ply][fs];
gbb[ply][fs] &= notme;
break;
case BRC:
case BR:
gbb[ply][fs]
= ray[std::countr_zero(ray[fs].NN & occ)].NN
| ray[std::countr_zero(ray[fs].EE & occ)].EE
| ray[std::countl_zero(ray[fs].SS & occ)].SS
| ray[std::countl_zero(ray[fs].WW & occ)].WW
^ bishopMoves[fs];
abb[ply] |= gbb[ply][fs];
gbb[ply][fs] &= notme;
break;
case BQ:
gbb[ply][fs]
= ray[std::countr_zero(ray[fs].NW & occ)].NW
| ray[std::countr_zero(ray[fs].NE & occ)].NE
| ray[std::countl_zero(ray[fs].SE & occ)].SE
| ray[std::countl_zero(ray[fs].SW & occ)].SW
| ray[std::countr_zero(ray[fs].NN & occ)].NN
| ray[std::countr_zero(ray[fs].EE & occ)].EE
| ray[std::countl_zero(ray[fs].SS & occ)].SS
| ray[std::countl_zero(ray[fs].WW & occ)].WW
^ queenMoves[fs];
abb[ply] |= gbb[ply][fs];
gbb[ply][fs] &= notme;
break;
case BKC:
gbb[ply][fs] = kingMoves[fs] & notme;
abb[ply] |= kingMoves[fs];
gbb[ply][fs] |= ((((board[h8] == BRC) + !(occ & SBCS) + !AttackedByWhite(t, ABCS)) == 3) << g8);
gbb[ply][fs] |= ((((board[a8] == BRC) + !(occ & SBCL) + !AttackedByWhite(t, ABCL)) == 3) << c8);
break;
case BK:
gbb[ply][fs] = kingMoves[fs] & notme;
abb[ply] |= kingMoves[fs];
break;
}
pieces ^= 1ull << fs;
} while (pieces);
return 1 - (king[1 - stm] & abb[ply]);
}