I meant up and down-dating the hashes, material score and phase, which takes time and is not needed for perft(). This is not about a mine is bigger than yours discussion, but I just want to show that you don't need assembler to get good performance.
There is nothing secret about my move generator, it is a very straightforward bit-board implementation, I used some meta-programming, this can make it somewhat difficult to comprehend.
Code: Select all
namespace movegen
{
enum : int { mvNW = 7, mvFW = 8, mvNE = 9, mvF2 = 16 };
template <int S, int N> constexpr bb_t bbFile = S == White ? masks::bbFile<N> : masks::bbFile<7 - N>;
template <int S, int N> constexpr bb_t bbRank = S == White ? masks::bbRank<N> : masks::bbRank<7 - N>;
template <int S> constexpr bb_t bbNW = ~bbFile<S, 0> & ~bbRank<S, 7>;
template <int S> constexpr bb_t bbNE = ~bbFile<S, 7> & ~bbRank<S, 7>;
template <int S> constexpr bb_t bbFW = ~bbRank<S, 7>;
template <int S, int N> constexpr bb_t SHL(bb_t bb) { return S == White ? bb << N : bb >> N; }
template <int S, int N> constexpr bb_t SHR(bb_t bb) { return S == White ? bb >> N : bb << N; }
template <int S, int N>
constexpr void gen_promotion(position_t* pos, bb_t src, bb_t tgt, move_t*& move, int type)
{
switch (N)
{
case mvNW: src &= bbNW<S> & SHR<S, N>(tgt); break;
case mvFW: src &= bbFW<S> & SHR<S, N>(tgt); break;
case mvNE: src &= bbNE<S> & SHR<S, N>(tgt); break;
}
while (src)
{
int fsq = LSBR(src);
int tsq = S == White ? fsq + N : fsq - N;
move++->pack(fsq, tsq, type, S | wQueen);
move++->pack(fsq, tsq, type, S | wKnight);
move++->pack(fsq, tsq, type, S | wRook);
move++->pack(fsq, tsq, type, S | wBishop);
}
}
template <int S, int N>
constexpr void gen_pawn(position_t* pos, bb_t src, bb_t tgt, move_t*& move, int type)
{
switch (N)
{
case mvNW: src &= bbNW<S> & SHR<S, N>(tgt); break;
case mvFW: src &= bbFW<S> & SHR<S, N>(tgt); break;
case mvNE: src &= bbNE<S> & SHR<S, N>(tgt); break;
case mvF2: src &= bbRank<S, 1> & SHR<S, mvFW>(pos->empty())& SHR<S, N>(tgt); break;
}
while (src)
{
int fsq = LSBR(src);
S == White ? move++->pack(fsq, fsq + N, type) : move++->pack(fsq, fsq - N, type);
}
}
template <int S, int T>
constexpr void gen_piece(position_t* pos, bb_t tgt, move_t*& move, int type)
{
bb_t src = 0;
switch (T)
{
case Knight: src = pos->knights(S); break;
case Bishop: src = pos->bishops_queens(S); break;
case Rook: src = pos->rooks_queens(S); break;
case King: src = pos->king(S); break;
}
while (src)
{
int fsq = LSBR(src);
bb_t dst = 0;
switch (T)
{
case Knight: dst = tgt & masks::knight_attacks[fsq]; break;
case Bishop: dst = tgt & magics::bishop_attacks(fsq, pos->occupied()); break;
case Rook: dst = tgt & magics::rook_attacks(fsq, pos->occupied()); break;
case King: dst = tgt & masks::king_attacks[fsq]; break;
}
while (dst) move++->pack(fsq, LSBR(dst), type);
}
}
template <int S>
constexpr void gen_castling(position_t* pos, move_t*& move)
{
if (S == White)
{
if (Bit(H1) & pos->castling() && !(0x60ULL & pos->occupied()) && !(0x70ULL & pos->attacked()))
move++->pack(E1, G1, mtQuiet | mtKing | mtCastle);
if (Bit(A1) & pos->castling() && !(0x0EULL & pos->occupied()) && !(0x1CULL & pos->attacked()))
move++->pack(E1, C1, mtQuiet | mtKing | mtCastle);
}
else
{
if (Bit(H8) & pos->castling() && !(0x60ULL << 56 & pos->occupied()) && !(0x70ULL << 56 & pos->attacked()))
move++->pack(E8, G8, mtQuiet | mtKing | mtCastle);
if (Bit(A8) & pos->castling() && !(0x0EULL << 56 & pos->occupied()) && !(0x1CULL << 56 & pos->attacked()))
move++->pack(E8, C8, mtQuiet | mtKing | mtCastle);
}
}
template <int S>
void gen_captures(position_t* pos, move_t*& move)
{
bb_t src = bbRank<S, 6> & pos->pawns(S);
bb_t tgt = pos->occupied(Opp(S));
gen_promotion<S, mvNW>(pos, src, tgt, move, mtPromote | mtCapture | mtPawn);
gen_promotion<S, mvNE>(pos, src, tgt, move, mtPromote | mtCapture | mtPawn);
gen_promotion<S, mvFW>(pos, src, pos->empty(), move, mtPromote | mtPawn);
src = ~bbRank<S, 6> & pos->pawns(S);
gen_pawn<S, mvNW>(pos, src, tgt, move, mtCapture | mtPawn);
gen_pawn<S, mvNE>(pos, src, tgt, move, mtCapture | mtPawn);
if (pos->enpassant_square() > 0)
{
bb_t epb = Bit(pos->enpassant_square());
gen_pawn<S, mvNW>(pos, src, epb, move, mtCapture | mtEnpcapt | mtPawn);
gen_pawn<S, mvNE>(pos, src, epb, move, mtCapture | mtEnpcapt | mtPawn);
}
gen_piece<S, Knight>(pos, tgt, move, mtCapture);
gen_piece<S, Bishop>(pos, tgt, move, mtCapture);
gen_piece<S, Rook>(pos, tgt, move, mtCapture);
gen_piece<S, King>(pos, tgt & ~pos->attacked(), move, mtCapture | mtKing);
}
template <int S>
void gen_quiet_moves(position_t* pos, move_t*& move)
{
bb_t src = ~bbRank<S, 6> & pos->pawns(S);
bb_t tgt = pos->empty();
gen_pawn<S, mvFW>(pos, src, tgt, move, mtQuiet | mtPawn);
gen_pawn<S, mvF2>(pos, src, tgt, move, mtQuiet | mtPawn | mtPawn2);
gen_piece<S, Knight>(pos, tgt, move, mtQuiet);
gen_piece<S, Bishop>(pos, tgt, move, mtQuiet);
gen_piece<S, Rook>(pos, tgt, move, mtQuiet);
gen_piece<S, King>(pos, tgt & ~pos->attacked(), move, mtQuiet | mtKing);
gen_castling<S>(pos, move);
}
template <int S>
void gen_captures_to_target(position_t* pos, bb_t tgt, move_t*& move)
{
bb_t src = bbRank<S, 6> & pos->pawns(S);
gen_promotion<S, mvNW>(pos, src, tgt, move, mtPromote | mtCapture | mtPawn);
gen_promotion<S, mvNE>(pos, src, tgt, move, mtPromote | mtCapture | mtPawn);
src = ~bbRank<S, 6> & pos->pawns(S);
gen_pawn<S, mvNW>(pos, src, tgt, move, mtCapture | mtPawn);
gen_pawn<S, mvNE>(pos, src, tgt, move, mtCapture | mtPawn);
if (pos->enpassant_square() > 0)
{
bb_t epb = Bit(pos->enpassant_square());
if (tgt & SHR<S, 8>(epb))
{
gen_pawn<S, mvNW>(pos, src, epb, move, mtCapture | mtEnpcapt | mtPawn);
gen_pawn<S, mvNE>(pos, src, epb, move, mtCapture | mtEnpcapt | mtPawn);
}
}
gen_piece<S, Knight>(pos, tgt, move, mtCapture);
gen_piece<S, Bishop>(pos, tgt, move, mtCapture);
gen_piece<S, Rook>(pos, tgt, move, mtCapture);
}
template <int S>
void gen_interpositions(position_t* pos, bb_t tgt, move_t*& move)
{
gen_promotion<S, mvFW>(pos, bbRank<S, 6> & pos->pawns(S), tgt, move, mtPromote | mtPawn);
bb_t src = ~bbRank<S, 6> & pos->pawns(S);
gen_pawn<S, mvFW>(pos, src, tgt, move, mtQuiet | mtPawn);
gen_pawn<S, mvF2>(pos, src, tgt, move, mtQuiet | mtPawn | mtPawn2);
gen_piece<S, Knight>(pos, tgt, move, mtQuiet);
gen_piece<S, Bishop>(pos, tgt, move, mtQuiet);
gen_piece<S, Rook>(pos, tgt, move, mtQuiet);
}
template <int S>
void gen_evasion_captures(position_t* pos, move_t*& move)
{
gen_piece<S, King>(pos, pos->occupied(Opp(S)) & ~pos->attacked(), move, mtCapture | mtKing);
if (POPCNT(pos->checkers()) == 1)
gen_captures_to_target<S>(pos, pos->checkers(), move);
}
template <int S>
void gen_evasion_moves(position_t* pos, move_t*& move)
{
gen_piece<S, King>(pos, pos->empty() & ~pos->attacked(), move, mtQuiet | mtKing);
if (POPCNT(pos->checkers()) == 1 && (pos->checkers() & (pos->pawns(Opp(S)) | pos->knights(Opp(S)))) == 0)
gen_interpositions<S>(pos, masks::between[pos->king_square(S)][LSB(pos->checkers())], move);
}
move_t* gen_captures(position_t* pos, move_t* moves)
{
pos->own() == White ? gen_captures<White>(pos, moves) : gen_captures<Black>(pos, moves);
return moves;
}
move_t* gen_quiet_moves(position_t* pos, move_t* moves)
{
pos->own() == White ? gen_quiet_moves<White>(pos, moves) : gen_quiet_moves<Black>(pos, moves);
return moves;
}
move_t* gen_evasion_captures(position_t* pos, move_t* moves)
{
pos->own() == White ? gen_evasion_captures<White>(pos, moves) : gen_evasion_captures<Black>(pos, moves);
return moves;
}
move_t* gen_evasion_moves(position_t* pos, move_t* moves)
{
pos->own() == White ? gen_evasion_moves<White>(pos, moves) : gen_evasion_moves<Black>(pos, moves);
return moves;
}
move_t* gen_all_moves(position_t* pos, move_t* moves)
{
if (pos->own() == White)
{
gen_captures<White>(pos, moves);
gen_quiet_moves<White>(pos, moves);
}
else
{
gen_captures<Black>(pos, moves);
gen_quiet_moves<Black>(pos, moves);
}
return moves;
}
move_t* gen_all_evasions(position_t* pos, move_t* moves)
{
if (pos->own() == White)
{
gen_evasion_captures<White>(pos, moves);
gen_evasion_moves<White>(pos, moves);
}
else
{
gen_evasion_captures<Black>(pos, moves);
gen_evasion_moves<Black>(pos, moves);
}
return moves;
}
}