mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-24 19:16:49 +08:00
Add optional move validation to training data conversion. No longer rely on static initialization order for magics initialization.
This commit is contained in:
@@ -3132,7 +3132,11 @@ namespace chess
|
||||
};
|
||||
}
|
||||
|
||||
static const EnumArray2<PieceType, Square, Bitboard> pseudoAttacks = generatePseudoAttacks();
|
||||
static const EnumArray2<PieceType, Square, Bitboard>& pseudoAttacks()
|
||||
{
|
||||
static const EnumArray2<PieceType, Square, Bitboard> s_pseudoAttacks = generatePseudoAttacks();
|
||||
return s_pseudoAttacks;
|
||||
}
|
||||
|
||||
[[nodiscard]] static Bitboard generatePositiveRayAttacks(Direction dir, Square fromSq)
|
||||
{
|
||||
@@ -3187,24 +3191,29 @@ namespace chess
|
||||
return bbs;
|
||||
}
|
||||
|
||||
static const std::array<EnumArray<Square, Bitboard>, 8> positiveRayAttacks = generatePositiveRayAttacks();
|
||||
|
||||
static const std::array<EnumArray<Square, Bitboard>, 8>& positiveRayAttacks()
|
||||
{
|
||||
static const std::array<EnumArray<Square, Bitboard>, 8> s_positiveRayAttacks = generatePositiveRayAttacks();
|
||||
return s_positiveRayAttacks;
|
||||
}
|
||||
|
||||
template <Direction DirV>
|
||||
[[nodiscard]] static Bitboard slidingAttacks(Square sq, Bitboard occupied)
|
||||
{
|
||||
assert(sq.isOk());
|
||||
|
||||
Bitboard attacks = positiveRayAttacks[DirV][sq];
|
||||
Bitboard attacks = positiveRayAttacks()[DirV][sq];
|
||||
|
||||
if constexpr (DirV == NorthWest || DirV == North || DirV == NorthEast || DirV == East)
|
||||
{
|
||||
Bitboard blocker = (attacks & occupied) | h8; // set highest bit (H8) so msb never fails
|
||||
return attacks ^ positiveRayAttacks[DirV][blocker.first()];
|
||||
return attacks ^ positiveRayAttacks()[DirV][blocker.first()];
|
||||
}
|
||||
else
|
||||
{
|
||||
Bitboard blocker = (attacks & occupied) | a1;
|
||||
return attacks ^ positiveRayAttacks[DirV][blocker.last()];
|
||||
return attacks ^ positiveRayAttacks()[DirV][blocker.last()];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3290,10 +3299,10 @@ namespace chess
|
||||
{
|
||||
for (PieceType pt : { PieceType::Bishop, PieceType::Rook })
|
||||
{
|
||||
const Bitboard s1Attacks = pseudoAttacks[pt][s1];
|
||||
const Bitboard s1Attacks = pseudoAttacks()[pt][s1];
|
||||
if (s1Attacks.isSet(s2))
|
||||
{
|
||||
const Bitboard s2Attacks = pseudoAttacks[pt][s2];
|
||||
const Bitboard s2Attacks = pseudoAttacks()[pt][s2];
|
||||
return (s1Attacks & s2Attacks) | s1 | s2;
|
||||
}
|
||||
}
|
||||
@@ -3420,14 +3429,14 @@ namespace chess
|
||||
|
||||
assert(sq.isOk());
|
||||
|
||||
return detail::pseudoAttacks[PieceTypeV][sq];
|
||||
return detail::pseudoAttacks()[PieceTypeV][sq];
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Bitboard pseudoAttacks(PieceType pt, Square sq)
|
||||
{
|
||||
assert(sq.isOk());
|
||||
|
||||
return detail::pseudoAttacks[pt][sq];
|
||||
return detail::pseudoAttacks()[pt][sq];
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Bitboard pawnAttacks(Bitboard pawns, Color color)
|
||||
@@ -4373,6 +4382,22 @@ namespace chess
|
||||
std::uint64_t low;
|
||||
};
|
||||
|
||||
struct Position;
|
||||
|
||||
struct MoveLegalityChecker
|
||||
{
|
||||
MoveLegalityChecker(const Position& position);
|
||||
|
||||
[[nodiscard]] bool isPseudoLegalMoveLegal(const Move& move) const;
|
||||
|
||||
private:
|
||||
const Position* m_position;
|
||||
Bitboard m_checkers;
|
||||
Bitboard m_ourBlockersForKing;
|
||||
Bitboard m_potentialCheckRemovals;
|
||||
Square m_ksq;
|
||||
};
|
||||
|
||||
struct Position : public Board
|
||||
{
|
||||
using BaseType = Board;
|
||||
@@ -4412,6 +4437,11 @@ namespace chess
|
||||
|
||||
[[nodiscard]] inline std::string fen() const;
|
||||
|
||||
[[nodiscard]] MoveLegalityChecker moveLegalityChecker() const
|
||||
{
|
||||
return { *this };
|
||||
}
|
||||
|
||||
constexpr void setEpSquareUnchecked(Square sq)
|
||||
{
|
||||
m_epSquare = sq;
|
||||
@@ -4498,6 +4528,8 @@ namespace chess
|
||||
|
||||
[[nodiscard]] inline bool isCheckAfterMove(Move move) const;
|
||||
|
||||
[[nodiscard]] inline bool isMoveLegal(Move move) const;
|
||||
|
||||
[[nodiscard]] inline bool isPseudoLegalMoveLegal(Move move) const;
|
||||
|
||||
[[nodiscard]] inline bool isMovePseudoLegal(Move move) const;
|
||||
@@ -4665,6 +4697,592 @@ namespace chess
|
||||
std::uint8_t m_packedState[16];
|
||||
};
|
||||
|
||||
namespace movegen
|
||||
{
|
||||
// For a pseudo-legal move the following are true:
|
||||
// - the moving piece has the pos.sideToMove() color
|
||||
// - the destination square is either empty or has a piece of the opposite color
|
||||
// - if it is a pawn move it is valid (but may be illegal due to discovered checks)
|
||||
// - if it is not a pawn move then the destination square is contained in attacks()
|
||||
// - if it is a castling it is legal
|
||||
// - a move other than castling may create a discovered attack on the king
|
||||
// - a king may walk into a check
|
||||
|
||||
template <typename FuncT>
|
||||
inline void forEachPseudoLegalPawnMove(const Position& pos, Square from, FuncT&& f)
|
||||
{
|
||||
const Color sideToMove = pos.sideToMove();
|
||||
const Square epSquare = pos.epSquare();
|
||||
const Bitboard ourPieces = pos.piecesBB(sideToMove);
|
||||
const Bitboard theirPieces = pos.piecesBB(!sideToMove);
|
||||
const Bitboard occupied = ourPieces | theirPieces;
|
||||
|
||||
Bitboard attackTargets = theirPieces;
|
||||
if (epSquare != Square::none())
|
||||
{
|
||||
attackTargets |= epSquare;
|
||||
}
|
||||
|
||||
const Bitboard attacks = bb::pawnAttacks(Bitboard::square(from), sideToMove) & attackTargets;
|
||||
|
||||
const Rank secondToLastRank = sideToMove == Color::White ? rank7 : rank2;
|
||||
const auto forward = sideToMove == Color::White ? FlatSquareOffset(0, 1) : FlatSquareOffset(0, -1);
|
||||
|
||||
// promotions
|
||||
if (from.rank() == secondToLastRank)
|
||||
{
|
||||
// capture promotions
|
||||
for (Square toSq : attacks)
|
||||
{
|
||||
for (PieceType pt : { PieceType::Knight, PieceType::Bishop, PieceType::Rook, PieceType::Queen })
|
||||
{
|
||||
Move move{ from, toSq, MoveType::Promotion, Piece(pt, sideToMove) };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
|
||||
// push promotions
|
||||
const Square toSq = from + forward;
|
||||
if (!occupied.isSet(toSq))
|
||||
{
|
||||
for (PieceType pt : { PieceType::Knight, PieceType::Bishop, PieceType::Rook, PieceType::Queen })
|
||||
{
|
||||
Move move{ from, toSq, MoveType::Promotion, Piece(pt, sideToMove) };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// captures
|
||||
for (Square toSq : attacks)
|
||||
{
|
||||
Move move{ from, toSq, (toSq == epSquare) ? MoveType::EnPassant : MoveType::Normal };
|
||||
f(move);
|
||||
}
|
||||
|
||||
const Square toSq = from + forward;
|
||||
|
||||
// single push
|
||||
if (!occupied.isSet(toSq))
|
||||
{
|
||||
const Rank startRank = sideToMove == Color::White ? rank2 : rank7;
|
||||
if (from.rank() == startRank)
|
||||
{
|
||||
// double push
|
||||
const Square toSq2 = toSq + forward;
|
||||
if (!occupied.isSet(toSq2))
|
||||
{
|
||||
Move move{ from, toSq2 };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
|
||||
Move move{ from, toSq };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <Color SideToMoveV, typename FuncT>
|
||||
inline void forEachPseudoLegalPawnMove(const Position& pos, FuncT&& f)
|
||||
{
|
||||
const Square epSquare = pos.epSquare();
|
||||
const Bitboard ourPieces = pos.piecesBB(SideToMoveV);
|
||||
const Bitboard theirPieces = pos.piecesBB(!SideToMoveV);
|
||||
const Bitboard occupied = ourPieces | theirPieces;
|
||||
const Bitboard pawns = pos.piecesBB(Piece(PieceType::Pawn, SideToMoveV));
|
||||
|
||||
const Bitboard secondToLastRank = SideToMoveV == Color::White ? bb::rank7 : bb::rank2;
|
||||
const Bitboard secondRank = SideToMoveV == Color::White ? bb::rank2 : bb::rank7;
|
||||
|
||||
const auto singlePawnMoveDestinationOffset = SideToMoveV == Color::White ? FlatSquareOffset(0, 1) : FlatSquareOffset(0, -1);
|
||||
const auto doublePawnMoveDestinationOffset = SideToMoveV == Color::White ? FlatSquareOffset(0, 2) : FlatSquareOffset(0, -2);
|
||||
|
||||
{
|
||||
const int backward = SideToMoveV == Color::White ? -1 : 1;
|
||||
const int backward2 = backward * 2;
|
||||
|
||||
const Bitboard doublePawnMoveStarts =
|
||||
pawns
|
||||
& secondRank
|
||||
& ~(occupied.shiftedVertically(backward) | occupied.shiftedVertically(backward2));
|
||||
|
||||
const Bitboard singlePawnMoveStarts =
|
||||
pawns
|
||||
& ~secondToLastRank
|
||||
& ~occupied.shiftedVertically(backward);
|
||||
|
||||
for (Square from : doublePawnMoveStarts)
|
||||
{
|
||||
const Square to = from + doublePawnMoveDestinationOffset;
|
||||
f(Move::normal(from, to));
|
||||
}
|
||||
|
||||
for (Square from : singlePawnMoveStarts)
|
||||
{
|
||||
const Square to = from + singlePawnMoveDestinationOffset;
|
||||
f(Move::normal(from, to));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Bitboard lastRank = SideToMoveV == Color::White ? bb::rank8 : bb::rank1;
|
||||
const FlatSquareOffset westCaptureOffset = SideToMoveV == Color::White ? FlatSquareOffset(-1, 1) : FlatSquareOffset(-1, -1);
|
||||
const FlatSquareOffset eastCaptureOffset = SideToMoveV == Color::White ? FlatSquareOffset(1, 1) : FlatSquareOffset(1, -1);
|
||||
|
||||
const Bitboard pawnsWithWestCapture = bb::eastPawnAttacks(theirPieces & ~lastRank, !SideToMoveV) & pawns;
|
||||
const Bitboard pawnsWithEastCapture = bb::westPawnAttacks(theirPieces & ~lastRank, !SideToMoveV) & pawns;
|
||||
|
||||
for (Square from : pawnsWithWestCapture)
|
||||
{
|
||||
f(Move::normal(from, from + westCaptureOffset));
|
||||
}
|
||||
|
||||
for (Square from : pawnsWithEastCapture)
|
||||
{
|
||||
f(Move::normal(from, from + eastCaptureOffset));
|
||||
}
|
||||
}
|
||||
|
||||
if (epSquare != Square::none())
|
||||
{
|
||||
const Bitboard pawnsThatCanCapture = bb::pawnAttacks(Bitboard::square(epSquare), !SideToMoveV) & pawns;
|
||||
for (Square from : pawnsThatCanCapture)
|
||||
{
|
||||
f(Move::enPassant(from, epSquare));
|
||||
}
|
||||
}
|
||||
|
||||
for (Square from : pawns & secondToLastRank)
|
||||
{
|
||||
const Bitboard attacks = bb::pawnAttacks(Bitboard::square(from), SideToMoveV) & theirPieces;
|
||||
|
||||
// capture promotions
|
||||
for (Square to : attacks)
|
||||
{
|
||||
for (PieceType pt : { PieceType::Knight, PieceType::Bishop, PieceType::Rook, PieceType::Queen })
|
||||
{
|
||||
Move move{ from, to, MoveType::Promotion, Piece(pt, SideToMoveV) };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
|
||||
// push promotions
|
||||
const Square to = from + singlePawnMoveDestinationOffset;
|
||||
if (!occupied.isSet(to))
|
||||
{
|
||||
for (PieceType pt : { PieceType::Knight, PieceType::Bishop, PieceType::Rook, PieceType::Queen })
|
||||
{
|
||||
Move move{ from, to, MoveType::Promotion, Piece(pt, SideToMoveV) };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FuncT>
|
||||
inline void forEachPseudoLegalPawnMove(const Position& pos, FuncT&& f)
|
||||
{
|
||||
if (pos.sideToMove() == Color::White)
|
||||
{
|
||||
forEachPseudoLegalPawnMove<Color::White>(pos, std::forward<FuncT>(f));
|
||||
}
|
||||
else
|
||||
{
|
||||
forEachPseudoLegalPawnMove<Color::Black>(pos, std::forward<FuncT>(f));
|
||||
}
|
||||
}
|
||||
|
||||
template <PieceType PieceTypeV, typename FuncT>
|
||||
inline void forEachPseudoLegalPieceMove(const Position& pos, Square from, FuncT&& f)
|
||||
{
|
||||
static_assert(PieceTypeV != PieceType::None);
|
||||
|
||||
if constexpr (PieceTypeV == PieceType::Pawn)
|
||||
{
|
||||
forEachPseudoLegalPawnMove(pos, from, f);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Color sideToMove = pos.sideToMove();
|
||||
const Bitboard ourPieces = pos.piecesBB(sideToMove);
|
||||
const Bitboard theirPieces = pos.piecesBB(!sideToMove);
|
||||
const Bitboard occupied = ourPieces | theirPieces;
|
||||
const Bitboard attacks = bb::attacks<PieceTypeV>(from, occupied) & ~ourPieces;
|
||||
|
||||
for (Square toSq : attacks)
|
||||
{
|
||||
Move move{ from, toSq };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <PieceType PieceTypeV, typename FuncT>
|
||||
inline void forEachPseudoLegalPieceMove(const Position& pos, FuncT&& f)
|
||||
{
|
||||
static_assert(PieceTypeV != PieceType::None);
|
||||
|
||||
if constexpr (PieceTypeV == PieceType::Pawn)
|
||||
{
|
||||
forEachPseudoLegalPawnMove(pos, f);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Color sideToMove = pos.sideToMove();
|
||||
const Bitboard ourPieces = pos.piecesBB(sideToMove);
|
||||
const Bitboard theirPieces = pos.piecesBB(!sideToMove);
|
||||
const Bitboard occupied = ourPieces | theirPieces;
|
||||
const Bitboard pieces = pos.piecesBB(Piece(PieceTypeV, sideToMove));
|
||||
for (Square fromSq : pieces)
|
||||
{
|
||||
const Bitboard attacks = bb::attacks<PieceTypeV>(fromSq, occupied) & ~ourPieces;
|
||||
for (Square toSq : attacks)
|
||||
{
|
||||
Move move{ fromSq, toSq };
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FuncT>
|
||||
inline void forEachCastlingMove(const Position& pos, FuncT&& f)
|
||||
{
|
||||
CastlingRights rights = pos.castlingRights();
|
||||
if (rights == CastlingRights::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Color sideToMove = pos.sideToMove();
|
||||
const Bitboard ourPieces = pos.piecesBB(sideToMove);
|
||||
const Bitboard theirPieces = pos.piecesBB(!sideToMove);
|
||||
const Bitboard occupied = ourPieces | theirPieces;
|
||||
|
||||
// we first reduce the set of legal castlings by checking the paths for pieces
|
||||
if (sideToMove == Color::White)
|
||||
{
|
||||
if ((CastlingTraits::castlingPath[Color::White][CastleType::Short] & occupied).any()) rights &= ~CastlingRights::WhiteKingSide;
|
||||
if ((CastlingTraits::castlingPath[Color::White][CastleType::Long] & occupied).any()) rights &= ~CastlingRights::WhiteQueenSide;
|
||||
rights &= ~CastlingRights::Black;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((CastlingTraits::castlingPath[Color::Black][CastleType::Short] & occupied).any()) rights &= ~CastlingRights::BlackKingSide;
|
||||
if ((CastlingTraits::castlingPath[Color::Black][CastleType::Long] & occupied).any()) rights &= ~CastlingRights::BlackQueenSide;
|
||||
rights &= ~CastlingRights::White;
|
||||
}
|
||||
|
||||
if (rights == CastlingRights::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// King must not be in check. Done here because it is quite expensive.
|
||||
const Square ksq = pos.kingSquare(sideToMove);
|
||||
if (pos.isSquareAttacked(ksq, !sideToMove))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through all possible castlings.
|
||||
for (CastleType castlingType : values<CastleType>())
|
||||
{
|
||||
const CastlingRights right = CastlingTraits::castlingRights[sideToMove][castlingType];
|
||||
|
||||
if (!contains(rights, right))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have this castling right
|
||||
// we check whether the king passes an attacked square.
|
||||
const Square passedSquare = CastlingTraits::squarePassedByKing[sideToMove][castlingType];
|
||||
if (pos.isSquareAttacked(passedSquare, !sideToMove))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's a castling move then the change in square occupation
|
||||
// cannot have an effect because otherwise there would be
|
||||
// a slider attacker attacking the castling king.
|
||||
if (pos.isSquareAttacked(CastlingTraits::kingDestination[sideToMove][castlingType], !sideToMove))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If not we can castle.
|
||||
Move move = Move::castle(castlingType, sideToMove);
|
||||
f(move);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls a given function for all pseudo legal moves for the position.
|
||||
// `pos` must be a legal chess position
|
||||
template <typename FuncT>
|
||||
inline void forEachPseudoLegalMove(const Position& pos, FuncT&& func)
|
||||
{
|
||||
forEachPseudoLegalPieceMove<PieceType::Pawn>(pos, func);
|
||||
forEachPseudoLegalPieceMove<PieceType::Knight>(pos, func);
|
||||
forEachPseudoLegalPieceMove<PieceType::Bishop>(pos, func);
|
||||
forEachPseudoLegalPieceMove<PieceType::Rook>(pos, func);
|
||||
forEachPseudoLegalPieceMove<PieceType::Queen>(pos, func);
|
||||
forEachPseudoLegalPieceMove<PieceType::King>(pos, func);
|
||||
forEachCastlingMove(pos, func);
|
||||
}
|
||||
|
||||
// Calls a given function for all legal moves for the position.
|
||||
// `pos` must be a legal chess position
|
||||
template <typename FuncT>
|
||||
inline void forEachLegalMove(const Position& pos, FuncT&& func)
|
||||
{
|
||||
auto funcIfLegal = [&func, checker = pos.moveLegalityChecker()](Move move) {
|
||||
if (checker.isPseudoLegalMoveLegal(move))
|
||||
{
|
||||
func(move);
|
||||
}
|
||||
};
|
||||
|
||||
forEachPseudoLegalPieceMove<PieceType::Pawn>(pos, funcIfLegal);
|
||||
forEachPseudoLegalPieceMove<PieceType::Knight>(pos, funcIfLegal);
|
||||
forEachPseudoLegalPieceMove<PieceType::Bishop>(pos, funcIfLegal);
|
||||
forEachPseudoLegalPieceMove<PieceType::Rook>(pos, funcIfLegal);
|
||||
forEachPseudoLegalPieceMove<PieceType::Queen>(pos, funcIfLegal);
|
||||
forEachPseudoLegalPieceMove<PieceType::King>(pos, funcIfLegal);
|
||||
forEachCastlingMove(pos, func);
|
||||
}
|
||||
|
||||
// Generates all pseudo legal moves for the position.
|
||||
// `pos` must be a legal chess position
|
||||
[[nodiscard]] std::vector<Move> generatePseudoLegalMoves(const Position& pos);
|
||||
|
||||
// Generates all legal moves for the position.
|
||||
// `pos` must be a legal chess position
|
||||
[[nodiscard]] std::vector<Move> generateLegalMoves(const Position& pos);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool Position::isCheck() const
|
||||
{
|
||||
return BaseType::isSquareAttacked(kingSquare(m_sideToMove), !m_sideToMove);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Bitboard Position::checkers() const
|
||||
{
|
||||
return BaseType::attackers(kingSquare(m_sideToMove), !m_sideToMove);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool Position::isCheckAfterMove(Move move) const
|
||||
{
|
||||
return BaseType::isSquareAttackedAfterMove(move, kingSquare(!m_sideToMove), m_sideToMove);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool Position::isMoveLegal(Move move) const
|
||||
{
|
||||
return
|
||||
isMovePseudoLegal(move)
|
||||
&& isPseudoLegalMoveLegal(move);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool Position::isPseudoLegalMoveLegal(Move move) const
|
||||
{
|
||||
return
|
||||
(move.type == MoveType::Castle)
|
||||
|| !isOwnKingAttackedAfterMove(move);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool Position::isMovePseudoLegal(Move move) const
|
||||
{
|
||||
if (!move.from.isOk() || !move.to.isOk())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (move.from == move.to)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (move.type != MoveType::Promotion && move.promotedPiece != Piece::none())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const Piece movedPiece = pieceAt(move.from);
|
||||
if (movedPiece == Piece::none())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (movedPiece.color() != m_sideToMove)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const Bitboard occupied = piecesBB();
|
||||
const Bitboard ourPieces = piecesBB(m_sideToMove);
|
||||
const bool isNormal = move.type == MoveType::Normal;
|
||||
|
||||
switch (movedPiece.type())
|
||||
{
|
||||
case PieceType::Pawn:
|
||||
{
|
||||
bool isValid = false;
|
||||
// TODO: use iterators so we don't loop over all moves
|
||||
// when we can avoid it.
|
||||
movegen::forEachPseudoLegalPawnMove(*this, move.from, [&isValid, &move](const Move& genMove) {
|
||||
if (move == genMove)
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
});
|
||||
return isValid;
|
||||
}
|
||||
|
||||
case PieceType::Bishop:
|
||||
return isNormal && (bb::attacks<PieceType::Bishop>(move.from, occupied) & ~ourPieces).isSet(move.to);
|
||||
|
||||
case PieceType::Knight:
|
||||
return isNormal && (bb::pseudoAttacks<PieceType::Knight>(move.from) & ~ourPieces).isSet(move.to);
|
||||
|
||||
case PieceType::Rook:
|
||||
return isNormal && (bb::attacks<PieceType::Rook>(move.from, occupied) & ~ourPieces).isSet(move.to);
|
||||
|
||||
case PieceType::Queen:
|
||||
return isNormal && (bb::attacks<PieceType::Queen>(move.from, occupied) & ~ourPieces).isSet(move.to);
|
||||
|
||||
case PieceType::King:
|
||||
{
|
||||
if (move.type == MoveType::Castle)
|
||||
{
|
||||
bool isValid = false;
|
||||
movegen::forEachCastlingMove(*this, [&isValid, &move](const Move& genMove) {
|
||||
if (move == genMove)
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
});
|
||||
return isValid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return isNormal && (bb::pseudoAttacks<PieceType::King>(move.from) & ~ourPieces).isSet(move.to);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Bitboard Position::blockersForKing(Color color) const
|
||||
{
|
||||
const Color attackerColor = !color;
|
||||
|
||||
const Bitboard occupied = piecesBB();
|
||||
|
||||
const Bitboard bishops = piecesBB(Piece(PieceType::Bishop, attackerColor));
|
||||
const Bitboard rooks = piecesBB(Piece(PieceType::Rook, attackerColor));
|
||||
const Bitboard queens = piecesBB(Piece(PieceType::Queen, attackerColor));
|
||||
|
||||
const Square ksq = kingSquare(color);
|
||||
|
||||
const Bitboard opponentBishopLikePieces = (bishops | queens);
|
||||
const Bitboard bishopPseudoAttacks = bb::pseudoAttacks<PieceType::Bishop>(ksq);
|
||||
|
||||
const Bitboard opponentRookLikePieces = (rooks | queens);
|
||||
const Bitboard rookPseudoAttacks = bb::pseudoAttacks<PieceType::Rook>(ksq);
|
||||
|
||||
const Bitboard xrayers =
|
||||
(bishopPseudoAttacks & opponentBishopLikePieces)
|
||||
| (rookPseudoAttacks & opponentRookLikePieces);
|
||||
|
||||
Bitboard allBlockers = Bitboard::none();
|
||||
|
||||
for (Square xrayer : xrayers)
|
||||
{
|
||||
const Bitboard blockers = bb::between(xrayer, ksq) & occupied;
|
||||
if (blockers.exactlyOne())
|
||||
{
|
||||
allBlockers |= blockers;
|
||||
}
|
||||
}
|
||||
|
||||
return allBlockers;
|
||||
}
|
||||
|
||||
inline MoveLegalityChecker::MoveLegalityChecker(const Position& position) :
|
||||
m_position(&position),
|
||||
m_checkers(position.checkers()),
|
||||
m_ourBlockersForKing(
|
||||
position.blockersForKing(position.sideToMove())
|
||||
& position.piecesBB(position.sideToMove())
|
||||
),
|
||||
m_ksq(position.kingSquare(position.sideToMove()))
|
||||
{
|
||||
if (m_checkers.exactlyOne())
|
||||
{
|
||||
const Bitboard knightCheckers = m_checkers & bb::pseudoAttacks<PieceType::Knight>(m_ksq);
|
||||
if (knightCheckers.any())
|
||||
{
|
||||
// We're checked by a knight, we have to remove it or move the king.
|
||||
m_potentialCheckRemovals = knightCheckers;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're not checked by a knight we can block it.
|
||||
m_potentialCheckRemovals = bb::between(m_ksq, m_checkers.first()) | m_checkers;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Double check, king has to move.
|
||||
m_potentialCheckRemovals = Bitboard::none();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool MoveLegalityChecker::isPseudoLegalMoveLegal(const Move& move) const
|
||||
{
|
||||
if (m_checkers.any())
|
||||
{
|
||||
if (move.from == m_ksq || move.type == MoveType::EnPassant)
|
||||
{
|
||||
return m_position->isPseudoLegalMoveLegal(move);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This means there's only one check and we either
|
||||
// blocked it or removed the piece that attacked
|
||||
// our king. So the only threat is if it's a discovered check.
|
||||
return
|
||||
m_potentialCheckRemovals.isSet(move.to)
|
||||
&& !m_ourBlockersForKing.isSet(move.from);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (move.from == m_ksq)
|
||||
{
|
||||
return m_position->isPseudoLegalMoveLegal(move);
|
||||
}
|
||||
else if (move.type == MoveType::EnPassant)
|
||||
{
|
||||
return !m_position->createsDiscoveredAttackOnOwnKing(move);
|
||||
}
|
||||
else if (m_ourBlockersForKing.isSet(move.from))
|
||||
{
|
||||
// If it was a blocker it may have only moved in line with our king.
|
||||
// Otherwise it's a discovered check.
|
||||
return bb::line(m_ksq, move.from).isSet(move.to);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(sizeof(CompressedPosition) == 24);
|
||||
static_assert(std::is_trivially_copyable_v<CompressedPosition>);
|
||||
|
||||
@@ -5483,57 +6101,6 @@ namespace chess
|
||||
return { move, captured, oldEpSquare, oldCastlingRights };
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool Position::isCheck() const
|
||||
{
|
||||
return BaseType::isSquareAttacked(kingSquare(m_sideToMove), !m_sideToMove);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Bitboard Position::checkers() const
|
||||
{
|
||||
return BaseType::attackers(kingSquare(m_sideToMove), !m_sideToMove);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Position::isCheckAfterMove(Move move) const
|
||||
{
|
||||
return BaseType::isSquareAttackedAfterMove(move, kingSquare(!m_sideToMove), m_sideToMove);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Bitboard Position::blockersForKing(Color color) const
|
||||
{
|
||||
const Color attackerColor = !color;
|
||||
|
||||
const Bitboard occupied = piecesBB();
|
||||
|
||||
const Bitboard bishops = piecesBB(Piece(PieceType::Bishop, attackerColor));
|
||||
const Bitboard rooks = piecesBB(Piece(PieceType::Rook, attackerColor));
|
||||
const Bitboard queens = piecesBB(Piece(PieceType::Queen, attackerColor));
|
||||
|
||||
const Square ksq = kingSquare(color);
|
||||
|
||||
const Bitboard opponentBishopLikePieces = (bishops | queens);
|
||||
const Bitboard bishopPseudoAttacks = bb::pseudoAttacks<PieceType::Bishop>(ksq);
|
||||
|
||||
const Bitboard opponentRookLikePieces = (rooks | queens);
|
||||
const Bitboard rookPseudoAttacks = bb::pseudoAttacks<PieceType::Rook>(ksq);
|
||||
|
||||
const Bitboard xrayers =
|
||||
(bishopPseudoAttacks & opponentBishopLikePieces)
|
||||
| (rookPseudoAttacks & opponentRookLikePieces);
|
||||
|
||||
Bitboard allBlockers = Bitboard::none();
|
||||
|
||||
for (Square xrayer : xrayers)
|
||||
{
|
||||
const Bitboard blockers = bb::between(xrayer, ksq) & occupied;
|
||||
if (blockers.exactlyOne())
|
||||
{
|
||||
allBlockers |= blockers;
|
||||
}
|
||||
}
|
||||
|
||||
return allBlockers;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Position Position::afterMove(Move move) const
|
||||
{
|
||||
Position cpy(*this);
|
||||
@@ -5756,6 +6323,25 @@ namespace binpack
|
||||
return chess::Move{from, to, type};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toString() const
|
||||
{
|
||||
const chess::Square to = static_cast<chess::Square>((m_raw & (0b111111 << 0) >> 0));
|
||||
const chess::Square from = static_cast<chess::Square>((m_raw & (0b111111 << 6)) >> 6);
|
||||
|
||||
const unsigned promotionIndex = (m_raw & (0b11 << 12)) >> 12;
|
||||
const chess::PieceType promotionType = static_cast<chess::PieceType>(static_cast<int>(chess::PieceType::Knight) + promotionIndex);
|
||||
|
||||
std::string r;
|
||||
chess::parser_bits::appendSquareToString(from, r);
|
||||
chess::parser_bits::appendSquareToString(to, r);
|
||||
if (promotionType != chess::PieceType::None)
|
||||
{
|
||||
r += chess::EnumTraits<chess::PieceType>::toChar(promotionType, chess::Color::Black);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint16_t m_raw;
|
||||
};
|
||||
@@ -6233,6 +6819,11 @@ namespace binpack
|
||||
std::int16_t score;
|
||||
std::uint16_t ply;
|
||||
std::int16_t result;
|
||||
|
||||
[[nodiscard]] bool isValid() const
|
||||
{
|
||||
return pos.isMoveLegal(move);
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] inline TrainingDataEntry packedSfenValueToTrainingDataEntry(const nodchip::PackedSfenValue& psv)
|
||||
@@ -6921,7 +7512,7 @@ namespace binpack
|
||||
buffer.insert(buffer.end(), data, data+sizeof(psv));
|
||||
}
|
||||
|
||||
inline void convertPlainToBinpack(std::string inputPath, std::string outputPath, std::ios_base::openmode om)
|
||||
inline void convertPlainToBinpack(std::string inputPath, std::string outputPath, std::ios_base::openmode om, bool validate)
|
||||
{
|
||||
constexpr std::size_t reportEveryNPositions = 100'000;
|
||||
|
||||
@@ -6949,6 +7540,11 @@ namespace binpack
|
||||
if (key == "e"sv)
|
||||
{
|
||||
e.move = chess::uci::uciToMove(e.pos, move);
|
||||
if (validate && !e.isValid())
|
||||
{
|
||||
std::cerr << "Illegal move " << chess::uci::moveToUci(e.pos, e.move) << " for position " << e.pos.fen() << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
writer.addTrainingDataEntry(e);
|
||||
|
||||
@@ -6975,7 +7571,7 @@ namespace binpack
|
||||
std::cout << "Finished. Converted " << numProcessedPositions << " positions.\n";
|
||||
}
|
||||
|
||||
inline void convertBinpackToPlain(std::string inputPath, std::string outputPath, std::ios_base::openmode om)
|
||||
inline void convertBinpackToPlain(std::string inputPath, std::string outputPath, std::ios_base::openmode om, bool validate)
|
||||
{
|
||||
constexpr std::size_t bufferSize = MiB;
|
||||
|
||||
@@ -6990,7 +7586,14 @@ namespace binpack
|
||||
|
||||
while(reader.hasNext())
|
||||
{
|
||||
emitPlainEntry(buffer, reader.next());
|
||||
auto e = reader.next();
|
||||
if (validate && !e.isValid())
|
||||
{
|
||||
std::cerr << "Illegal move " << chess::uci::moveToUci(e.pos, e.move) << " for position " << e.pos.fen() << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
emitPlainEntry(buffer, e);
|
||||
|
||||
++numProcessedPositions;
|
||||
|
||||
@@ -7016,7 +7619,7 @@ namespace binpack
|
||||
}
|
||||
|
||||
|
||||
inline void convertBinToBinpack(std::string inputPath, std::string outputPath, std::ios_base::openmode om)
|
||||
inline void convertBinToBinpack(std::string inputPath, std::string outputPath, std::ios_base::openmode om, bool validate)
|
||||
{
|
||||
constexpr std::size_t reportEveryNPositions = 100'000;
|
||||
|
||||
@@ -7037,7 +7640,15 @@ namespace binpack
|
||||
break;
|
||||
}
|
||||
|
||||
writer.addTrainingDataEntry(packedSfenValueToTrainingDataEntry(psv));
|
||||
auto e = packedSfenValueToTrainingDataEntry(psv);
|
||||
if (validate && !e.isValid())
|
||||
{
|
||||
std::cerr << "Illegal move " << chess::uci::moveToUci(e.pos, e.move) << " for position " << e.pos.fen() << '\n';
|
||||
std::cerr << static_cast<int>(e.move.type) << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
writer.addTrainingDataEntry(e);
|
||||
|
||||
++numProcessedPositions;
|
||||
const auto cur = inputFile.tellg();
|
||||
@@ -7050,7 +7661,7 @@ namespace binpack
|
||||
std::cout << "Finished. Converted " << numProcessedPositions << " positions.\n";
|
||||
}
|
||||
|
||||
inline void convertBinpackToBin(std::string inputPath, std::string outputPath, std::ios_base::openmode om)
|
||||
inline void convertBinpackToBin(std::string inputPath, std::string outputPath, std::ios_base::openmode om, bool validate)
|
||||
{
|
||||
constexpr std::size_t bufferSize = MiB;
|
||||
|
||||
@@ -7065,7 +7676,14 @@ namespace binpack
|
||||
|
||||
while(reader.hasNext())
|
||||
{
|
||||
emitBinEntry(buffer, reader.next());
|
||||
auto e = reader.next();
|
||||
if (validate && !e.isValid())
|
||||
{
|
||||
std::cerr << "Illegal move " << chess::uci::moveToUci(e.pos, e.move) << " for position " << e.pos.fen() << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
emitBinEntry(buffer, e);
|
||||
|
||||
++numProcessedPositions;
|
||||
|
||||
@@ -7090,7 +7708,7 @@ namespace binpack
|
||||
std::cout << "Finished. Converted " << numProcessedPositions << " positions.\n";
|
||||
}
|
||||
|
||||
inline void convertBinToPlain(std::string inputPath, std::string outputPath, std::ios_base::openmode om)
|
||||
inline void convertBinToPlain(std::string inputPath, std::string outputPath, std::ios_base::openmode om, bool validate)
|
||||
{
|
||||
constexpr std::size_t bufferSize = MiB;
|
||||
|
||||
@@ -7113,7 +7731,14 @@ namespace binpack
|
||||
break;
|
||||
}
|
||||
|
||||
emitPlainEntry(buffer, packedSfenValueToTrainingDataEntry(psv));
|
||||
auto e = packedSfenValueToTrainingDataEntry(psv);
|
||||
if (validate && !e.isValid())
|
||||
{
|
||||
std::cerr << "Illegal move " << chess::uci::moveToUci(e.pos, e.move) << " for position " << e.pos.fen() << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
emitPlainEntry(buffer, e);
|
||||
|
||||
++numProcessedPositions;
|
||||
|
||||
@@ -7138,7 +7763,7 @@ namespace binpack
|
||||
std::cout << "Finished. Converted " << numProcessedPositions << " positions.\n";
|
||||
}
|
||||
|
||||
inline void convertPlainToBin(std::string inputPath, std::string outputPath, std::ios_base::openmode om)
|
||||
inline void convertPlainToBin(std::string inputPath, std::string outputPath, std::ios_base::openmode om, bool validate)
|
||||
{
|
||||
constexpr std::size_t bufferSize = MiB;
|
||||
|
||||
@@ -7169,6 +7794,11 @@ namespace binpack
|
||||
if (key == "e"sv)
|
||||
{
|
||||
e.move = chess::uci::uciToMove(e.pos, move);
|
||||
if (validate && !e.isValid())
|
||||
{
|
||||
std::cerr << "Illegal move " << chess::uci::moveToUci(e.pos, e.move) << " for position " << e.pos.fen() << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
emitBinEntry(buffer, e);
|
||||
|
||||
|
||||
@@ -525,7 +525,7 @@ namespace Learner
|
||||
&& ends_with(output_path, expected_output_extension);
|
||||
}
|
||||
|
||||
using ConvertFunctionType = void(std::string inputPath, std::string outputPath, std::ios_base::openmode om);
|
||||
using ConvertFunctionType = void(std::string inputPath, std::string outputPath, std::ios_base::openmode om, bool validate);
|
||||
|
||||
static ConvertFunctionType* get_convert_function(const std::string& input_path, const std::string& output_path)
|
||||
{
|
||||
@@ -547,7 +547,7 @@ namespace Learner
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void convert(const std::string& input_path, const std::string& output_path, std::ios_base::openmode om)
|
||||
static void convert(const std::string& input_path, const std::string& output_path, std::ios_base::openmode om, bool validate)
|
||||
{
|
||||
if(!file_exists(input_path))
|
||||
{
|
||||
@@ -558,7 +558,7 @@ namespace Learner
|
||||
auto func = get_convert_function(input_path, output_path);
|
||||
if (func != nullptr)
|
||||
{
|
||||
func(input_path, output_path, om);
|
||||
func(input_path, output_path, om, validate);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -568,20 +568,22 @@ namespace Learner
|
||||
|
||||
static void convert(const std::vector<std::string>& args)
|
||||
{
|
||||
if (args.size() < 2 || args.size() > 3)
|
||||
if (args.size() < 2 || args.size() > 4)
|
||||
{
|
||||
std::cerr << "Invalid arguments.\n";
|
||||
std::cerr << "Usage: convert from_path to_path [append]\n";
|
||||
std::cerr << "Usage: convert from_path to_path [append] [validate]\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const bool append = (args.size() == 3) && (args[2] == "append");
|
||||
const bool append = std::find(args.begin() + 2, args.end(), "append") != args.end();
|
||||
const bool validate = std::find(args.begin() + 2, args.end(), "validate") != args.end();
|
||||
|
||||
const std::ios_base::openmode openmode =
|
||||
append
|
||||
? std::ios_base::app
|
||||
: std::ios_base::trunc;
|
||||
|
||||
convert(args[0], args[1], openmode);
|
||||
convert(args[0], args[1], openmode, validate);
|
||||
}
|
||||
|
||||
void convert(istringstream& is)
|
||||
|
||||
Reference in New Issue
Block a user