<< std::setfill(' ') << std::dec << "\nCheckers: ";
for (Bitboard b = pos.checkers(); b; )
- os << UCI::square(pop_lsb(&b)) << " ";
+ os << UCI::square(pop_lsb(b)) << " ";
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
&& !pos.can_castle(ANY_CASTLING))
{
StateInfo st;
- ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize);
+ ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
Position p;
p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
set_castling_right(c, rsq);
}
- set_state(st);
-
// 4. En passant square.
// Ignore if square is invalid or not on side to move relative rank 6.
bool enpassant = false;
// a) side to move have a pawn threatening epSquare
// b) there is an enemy pawn in front of epSquare
// c) there is no piece on epSquare or behind epSquare
- // d) enemy pawn didn't block a check of its own color by moving forward
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
- && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))))
- && ( file_of(square<KING>(sideToMove)) == file_of(st->epSquare)
- || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove))));
+ && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
}
- // It's necessary for st->previous to be intialized in this way because legality check relies on its existence
- if (enpassant) {
- st->previous = new StateInfo();
- remove_piece(st->epSquare - pawn_push(sideToMove));
- st->previous->checkersBB = attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove);
- st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), st->previous->pinners[BLACK]);
- st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), st->previous->pinners[WHITE]);
- put_piece(make_piece(~sideToMove, PAWN), st->epSquare - pawn_push(sideToMove));
- }
- else
+ if (!enpassant)
st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number
chess960 = isChess960;
thisThread = th;
- st->accumulator.state[WHITE] = Eval::NNUE::INIT;
- st->accumulator.state[BLACK] = Eval::NNUE::INIT;
+ set_state(st);
assert(pos_is_ok());
Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
- castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
+ castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto))
& ~(kfrom | rfrom);
}
for (Bitboard b = pieces(); b; )
{
- Square s = pop_lsb(&b);
+ Square s = pop_lsb(b);
Piece pc = piece_on(s);
si->key ^= Zobrist::psq[pc][s];
while (snipers)
{
- Square sniperSq = pop_lsb(&snipers);
+ Square sniperSq = pop_lsb(snipers);
Bitboard b = between_bb(s, sniperSq) & occupancy;
if (b && !more_than_one(b))
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
- // st->previous->blockersForKing consider capsq as empty.
- // If pinned, it has to move along the king ray.
+ // En passant captures are a tricky special case. Because they are rather
+ // uncommon, we do it simply by testing whether the king is attacked after
+ // the move is made.
if (type_of(m) == EN_PASSANT)
- return !(st->previous->blockersForKing[sideToMove] & from)
- || aligned(from, to, square<KING>(us));
+ {
+ Square ksq = square<KING>(us);
+ Square capsq = to - pawn_push(us);
+ Bitboard occupied = (pieces() ^ from ^ capsq) | to;
+
+ assert(to == ep_square());
+ assert(moved_piece(m) == make_piece(us, PAWN));
+ assert(piece_on(capsq) == make_piece(~us, PAWN));
+ assert(piece_on(to) == NO_PIECE);
+
+ return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
+ && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
+ }
// Castling moves generation does not check if the castling path is clear of
// enemy attacks, it is delayed at a later time: now!
// If the moving piece is a king, check whether the destination square is
// attacked by the opponent.
if (type_of(piece_on(from)) == KING)
- return !(attackers_to(to) & pieces(~us));
+ return !(attackers_to(to, pieces() ^ from) & pieces(~us));
// A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king.
if (more_than_one(checkers()))
return false;
- // Our move must be a blocking evasion or a capture of the checking piece
- if (!((between_bb(lsb(checkers()), square<KING>(us)) | checkers()) & to))
+ // Our move must be a blocking interposition or a capture of the checking piece
+ if (!(between_bb(square<KING>(us), lsb(checkers())) & to))
return false;
}
// In case of king moves under check we have to remove king so as to catch
case PROMOTION:
return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
- // The double-pushed pawn blocked a check? En Passant will remove the blocker.
- // The only discovery check that wasn't handle is through capsq and fromsq
- // So the King must be in the same rank as fromsq to consider this possibility.
- // st->previous->blockersForKing consider capsq as empty.
+ // En passant capture with check? We have already handled the case
+ // of direct checks and ordinary discovered check, so the only case we
+ // need to handle is the unusual case of a discovered check through
+ // the captured pawn.
case EN_PASSANT:
- return st->previous->checkersBB
- || ( rank_of(square<KING>(~sideToMove)) == rank_of(from)
- && st->previous->blockersForKing[~sideToMove] & from);
+ {
+ Square capsq = make_square(file_of(to), rank_of(from));
+ Bitboard b = (pieces() ^ from ^ capsq) | to;
+ return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
+ | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
+ }
default: //CASTLING
{
// Castling is encoded as 'king captures the rook'
++st->pliesFromNull;
// Used by NNUE
- st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
- st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
+ st->accumulator.computed[WHITE] = false;
+ st->accumulator.computed[BLACK] = false;
auto& dp = st->dirtyPiece;
dp.dirty_num = 1;
}
-/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips
+/// Position::do_null_move() is used to do a "null move": it flips
/// the side to move without executing any move on the board.
void Position::do_null_move(StateInfo& newSt) {
st->dirtyPiece.dirty_num = 0;
st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
- st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
- st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
+ st->accumulator.computed[WHITE] = false;
+ st->accumulator.computed[BLACK] = false;
if (st->epSquare != SQ_NONE)
{
assert(pos_is_ok());
}
+
+/// Position::undo_null_move() must be used to undo a "null move"
+
void Position::undo_null_move() {
assert(!checkers());
if (swap <= 0)
return true;
+ assert(color_of(piece_on(from)) == sideToMove);
Bitboard occupied = pieces() ^ from ^ to;
- Color stm = color_of(piece_on(from));
+ Color stm = sideToMove;
Bitboard attackers = attackers_to(to, occupied);
Bitboard stmAttackers, bb;
int res = 1;
if (!(stmAttackers = attackers & pieces(stm)))
break;
- // Don't allow pinned pieces to attack (except the king) as long as
- // there are pinners on their original square.
+ // Don't allow pinned pieces to attack as long as there are
+ // pinners on their original square.
if (pinners(~stm) & occupied)
stmAttackers &= ~blockers_for_king(stm);
if ((swap = PawnValueMg - swap) < res)
break;
- occupied ^= lsb(bb);
+ occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
if ((swap = KnightValueMg - swap) < res)
break;
- occupied ^= lsb(bb);
+ occupied ^= least_significant_square_bb(bb);
}
else if ((bb = stmAttackers & pieces(BISHOP)))
if ((swap = BishopValueMg - swap) < res)
break;
- occupied ^= lsb(bb);
+ occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
if ((swap = RookValueMg - swap) < res)
break;
- occupied ^= lsb(bb);
+ occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
}
if ((swap = QueenValueMg - swap) < res)
break;
- occupied ^= lsb(bb);
+ occupied ^= least_significant_square_bb(bb);
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
}
Square s1 = from_sq(move);
Square s2 = to_sq(move);
- if (!(between_bb(s1, s2) & pieces()))
+ if (!((between_bb(s1, s2) ^ s2) & pieces()))
{
if (ply > i)
return true;
assert(0 && "pos_is_ok: Bitboards");
StateInfo si = *st;
- ASSERT_ALIGNED(&si, Eval::NNUE::kCacheLineSize);
+ ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize);
set_state(&si);
if (std::memcmp(&si, st, sizeof(StateInfo)))