Closes #52.
return s;
}
/// Returns all square positions for a given piece
- std::vector<CharSquare> get_squares_for_piece(char piece) const {
+ std::vector<CharSquare> get_squares_for_pieces(Color color, PieceSet ps, const std::string& pieceChars) const {
std::vector<CharSquare> squares;
+ size_t pcIdx;
for (int r = 0; r < nbRanks; ++r)
for (int c = 0; c < nbFiles; ++c)
- if (get_piece(r, c) == piece)
+ if ((pcIdx = pieceChars.find(get_piece(r, c))) != std::string::npos && (ps & type_of(Piece(pcIdx))) && color_of(Piece(pcIdx)) == color)
squares.emplace_back(CharSquare(r, c));
return squares;
}
for (Color c : {WHITE, BLACK})
{
const Rank castlingRank = relative_rank(c, v->castlingRank, v->maxRank);
- char rookChar = v->pieceToChar[make_piece(c, v->castlingRookPiece)];
for (char castlingFlag : castlingInfoSplitted[c])
{
if (tolower(castlingFlag) == 'k' || tolower(castlingFlag) == 'q')
}
bool kingside = tolower(castlingFlag) == 'k';
bool castlingRook = false;
+ size_t pcIdx;
for (int f = kingside ? board.get_nb_files() - 1 : 0; f != kingPositions[c].fileIdx; kingside ? f-- : f++)
- if (board.get_piece(castlingRank, f) == rookChar)
+ if ( (pcIdx = v->pieceToChar.find(board.get_piece(castlingRank, f))) != std::string::npos
+ && (v->castlingRookPieces[c] & type_of(Piece(pcIdx)))
+ && color_of(Piece(pcIdx)) == c)
{
castlingRook = true;
break;
{
CharSquare rookStartingSquare = castling == QUEEN_SIDE ? rookPositionsStart[c][0] : rookPositionsStart[c][1];
char targetChar = castling == QUEEN_SIDE ? 'q' : 'k';
- char rookChar = v->pieceToChar[make_piece(c, v->castlingRookPiece)];
+ size_t pcIdx;
if (castlingInfoSplitted[c].find(targetChar) != std::string::npos)
{
- if (board.get_piece(rookStartingSquare.rowIdx, rookStartingSquare.fileIdx) != rookChar)
+ if ( (pcIdx = v->pieceToChar.find(board.get_piece(rookStartingSquare.rowIdx, rookStartingSquare.fileIdx))) == std::string::npos
+ || !(v->castlingRookPieces[c] & type_of(Piece(pcIdx)))
+ || color_of(Piece(pcIdx)) != c)
{
std::cerr << "The " << color_to_string(c) << " ROOK on the "<< castling_rights_to_string(castling) << " has moved. "
<< castling_rights_to_string(castling) << " castling is no longer valid for " << color_to_string(c) << "." << std::endl;
if (castlingInfoSplitted[WHITE].size() != 0 || castlingInfoSplitted[BLACK].size() != 0)
{
std::array<CharSquare, 2> kingPositions;
- kingPositions[WHITE] = board.get_square_for_piece(toupper(v->pieceToChar[v->castlingKingPiece]));
- kingPositions[BLACK] = board.get_square_for_piece(tolower(v->pieceToChar[v->castlingKingPiece]));
+ kingPositions[WHITE] = board.get_square_for_piece(toupper(v->pieceToChar[v->castlingKingPiece[WHITE]]));
+ kingPositions[BLACK] = board.get_square_for_piece(tolower(v->pieceToChar[v->castlingKingPiece[BLACK]]));
CharBoard startBoard(board.get_nb_ranks(), board.get_nb_files());
fill_char_board(startBoard, v->startFen, validSpecialCharactersFirstField, v);
if (!v->chess960 && !v->castlingDroppedPiece && !chess960)
{
std::array<CharSquare, 2> kingPositionsStart;
- kingPositionsStart[WHITE] = startBoard.get_square_for_piece(v->pieceToChar[make_piece(WHITE, v->castlingKingPiece)]);
- kingPositionsStart[BLACK] = startBoard.get_square_for_piece(v->pieceToChar[make_piece(BLACK, v->castlingKingPiece)]);
+ kingPositionsStart[WHITE] = startBoard.get_square_for_piece(v->pieceToChar[make_piece(WHITE, v->castlingKingPiece[WHITE])]);
+ kingPositionsStart[BLACK] = startBoard.get_square_for_piece(v->pieceToChar[make_piece(BLACK, v->castlingKingPiece[BLACK])]);
std::array<std::vector<CharSquare>, 2> rookPositionsStart;
- rookPositionsStart[WHITE] = startBoard.get_squares_for_piece(v->pieceToChar[make_piece(WHITE, v->castlingRookPiece)]);
- rookPositionsStart[BLACK] = startBoard.get_squares_for_piece(v->pieceToChar[make_piece(BLACK, v->castlingRookPiece)]);
+ rookPositionsStart[WHITE] = startBoard.get_squares_for_pieces(WHITE, v->castlingRookPieces[WHITE], v->pieceToChar);
+ rookPositionsStart[BLACK] = startBoard.get_squares_for_pieces(BLACK, v->castlingRookPieces[BLACK], v->pieceToChar);
if (check_standard_castling(castlingInfoSplitted, board, kingPositions, kingPositionsStart, rookPositionsStart, v) == NOK)
return FEN_INVALID_CASTLING_INFO;
}
parse_attribute<false>("whiteFlag", v->flagRegion[WHITE]);
parse_attribute<false>("blackFlag", v->flagRegion[BLACK]);
+ parse_attribute<false>("castlingRookPiece", v->castlingRookPieces[WHITE], v->pieceToChar);
+ parse_attribute<false>("castlingRookPiece", v->castlingRookPieces[BLACK], v->pieceToChar);
// Parse aliases
parse_attribute("pawnTypes", v->promotionPawnType[WHITE], v->pieceToChar);
parse_attribute("castlingQueensideFile", v->castlingQueensideFile);
parse_attribute("castlingRank", v->castlingRank);
parse_attribute("castlingKingFile", v->castlingKingFile);
- parse_attribute("castlingKingPiece", v->castlingKingPiece, v->pieceToChar);
- parse_attribute("castlingRookPiece", v->castlingRookPiece, v->pieceToChar);
+ parse_attribute("castlingKingPiece", v->castlingKingPiece[WHITE], v->pieceToChar);
+ parse_attribute("castlingKingPiece", v->castlingKingPiece[BLACK], v->pieceToChar);
+ parse_attribute("castlingKingPieceWhite", v->castlingKingPiece[WHITE], v->pieceToChar);
+ parse_attribute("castlingKingPieceBlack", v->castlingKingPiece[BLACK], v->pieceToChar);
+ parse_attribute("castlingRookPieces", v->castlingRookPieces[WHITE], v->pieceToChar);
+ parse_attribute("castlingRookPieces", v->castlingRookPieces[BLACK], v->pieceToChar);
+ parse_attribute("castlingRookPiecesWhite", v->castlingRookPieces[WHITE], v->pieceToChar);
+ parse_attribute("castlingRookPiecesBlack", v->castlingRookPieces[BLACK], v->pieceToChar);
parse_attribute("checking", v->checking);
parse_attribute("dropChecks", v->dropChecks);
parse_attribute("mustCapture", v->mustCapture);
{
Square rsq;
Color c = islower(token) ? BLACK : WHITE;
- Piece rook = make_piece(c, castling_rook_piece());
token = char(toupper(token));
if (token == 'K')
- for (rsq = make_square(FILE_MAX, castling_rank(c)); piece_on(rsq) != rook; --rsq) {}
+ for (rsq = make_square(FILE_MAX, castling_rank(c)); !(castling_rook_pieces(c) & type_of(piece_on(rsq))) || color_of(piece_on(rsq)) != c; --rsq) {}
else if (token == 'Q')
- for (rsq = make_square(FILE_A, castling_rank(c)); piece_on(rsq) != rook; ++rsq) {}
+ for (rsq = make_square(FILE_A, castling_rank(c)); !(castling_rook_pieces(c) & type_of(piece_on(rsq))) || color_of(piece_on(rsq)) != c; ++rsq) {}
else if (token >= 'A' && token <= 'A' + max_file())
rsq = make_square(File(token - 'A'), castling_rank(c));
// Determine castling "king" position
if (castling_enabled() && st->castlingKingSquare[c] == SQ_NONE)
{
- Bitboard castlingKings = pieces(c, castling_king_piece()) & rank_bb(castling_rank(c));
+ Bitboard castlingKings = pieces(c, castling_king_piece(c)) & rank_bb(castling_rank(c));
// Ambiguity resolution for 960 variants with more than one "king"
// e.g., EAH means that an e-file king can castle with a- and h-file rooks
- st->castlingKingSquare[c] = isChess960 && piece_on(rsq) == make_piece(c, castling_king_piece()) ? rsq
+ st->castlingKingSquare[c] = isChess960 && piece_on(rsq) == make_piece(c, castling_king_piece(c)) ? rsq
: castlingKings && (!more_than_one(castlingKings) || isChess960) ? lsb(castlingKings)
: make_square(castling_king_file(), castling_rank(c));
}
continue;
}
- if (castling_enabled() && piece_on(rsq) == rook)
+ if (castling_enabled() && (castling_rook_pieces(c) & type_of(piece_on(rsq))) && color_of(piece_on(rsq)) == c)
set_castling_right(c, rsq);
}
// Set castling rights for 960 gating variants
if (gating() && castling_enabled())
for (Color c : {WHITE, BLACK})
- if ((gates(c) & pieces(castling_king_piece())) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) > 0 || captures_to_hand()))
+ if ((gates(c) & pieces(castling_king_piece(c))) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) > 0 || captures_to_hand()))
{
- Bitboard castling_rooks = gates(c) & pieces(castling_rook_piece());
+ Bitboard castling_rooks = gates(c) & pieces(c);
while (castling_rooks)
- set_castling_right(c, pop_lsb(castling_rooks));
+ {
+ Square s = pop_lsb(castling_rooks);
+ if (castling_rook_pieces(c) & type_of(piece_on(s)))
+ set_castling_right(c, s);
+ }
}
// counting limit
ss << (sideToMove == WHITE ? " w " : " b ");
// Disambiguation for chess960 "king" square
- if (chess960 && can_castle(WHITE_CASTLING) && popcount(pieces(WHITE, castling_king_piece()) & rank_bb(castling_rank(WHITE))) > 1)
+ if (chess960 && can_castle(WHITE_CASTLING) && popcount(pieces(WHITE, castling_king_piece(WHITE)) & rank_bb(castling_rank(WHITE))) > 1)
ss << char('A' + castling_king_square(WHITE));
if (can_castle(WHITE_OO))
ss << char('A' + f);
// Disambiguation for chess960 "king" square
- if (chess960 && can_castle(BLACK_CASTLING) && popcount(pieces(BLACK, castling_king_piece()) & rank_bb(castling_rank(BLACK))) > 1)
+ if (chess960 && can_castle(BLACK_CASTLING) && popcount(pieces(BLACK, castling_king_piece(BLACK)) & rank_bb(castling_rank(BLACK))) > 1)
ss << char('a' + castling_king_square(BLACK));
if (can_castle(BLACK_OO))
if (type_of(m) == CASTLING)
{
assert(type_of(pc) != NO_PIECE_TYPE);
- assert(captured == make_piece(us, castling_rook_piece()));
+ assert(castling_rook_pieces(us) & type_of(captured));
Square rfrom, rto;
do_castling<true>(us, from, to, rfrom, rto);
// Set castling rights for dropped king or rook
if (castling_dropped_piece() && rank_of(to) == castling_rank(us))
{
- if (type_of(pc) == castling_king_piece() && file_of(to) == castling_king_file())
+ if (type_of(pc) == castling_king_piece(us) && file_of(to) == castling_king_file())
{
st->castlingKingSquare[us] = to;
- Bitboard castling_rooks = pieces(us, castling_rook_piece())
+ Bitboard castling_rooks = pieces(us)
& rank_bb(castling_rank(us))
& (file_bb(FILE_A) | file_bb(max_file()));
while (castling_rooks)
- set_castling_right(us, pop_lsb(castling_rooks));
+ {
+ Square s = pop_lsb(castling_rooks);
+ if (castling_rook_pieces(us) & type_of(piece_on(s)))
+ set_castling_right(us, s);
+ }
}
- else if (type_of(pc) == castling_rook_piece())
+ else if (castling_rook_pieces(us) & type_of(pc))
{
if ( (file_of(to) == FILE_A || file_of(to) == max_file())
- && piece_on(make_square(castling_king_file(), castling_rank(us))) == make_piece(us, castling_king_piece()))
+ && piece_on(make_square(castling_king_file(), castling_rank(us))) == make_piece(us, castling_king_piece(us)))
{
st->castlingKingSquare[us] = make_square(castling_king_file(), castling_rank(us));
set_castling_right(us, to);
if (!can_castle(cr))
continue;
- if ( piece_on(castlingRookSquare[cr]) != make_piece(c, castling_rook_piece())
+ if ( !(castling_rook_pieces(c) & type_of(piece_on(castlingRookSquare[cr])))
|| castlingRightsMask[castlingRookSquare[cr]] != cr
|| (count<KING>(c) && (castlingRightsMask[square<KING>(c)] & cr) != cr))
assert(0 && "pos_is_ok: Castling");
File castling_queenside_file() const;
Rank castling_rank(Color c) const;
File castling_king_file() const;
- PieceType castling_king_piece() const;
- PieceType castling_rook_piece() const;
+ PieceType castling_king_piece(Color c) const;
+ PieceSet castling_rook_pieces(Color c) const;
PieceType king_type() const;
PieceType nnue_king() const;
Square nnue_king_square(Color c) const;
return var->castlingKingFile;
}
-inline PieceType Position::castling_king_piece() const {
+inline PieceType Position::castling_king_piece(Color c) const {
assert(var != nullptr);
- return var->castlingKingPiece;
+ return var->castlingKingPiece[c];
}
-inline PieceType Position::castling_rook_piece() const {
+inline PieceSet Position::castling_rook_pieces(Color c) const {
assert(var != nullptr);
- return var->castlingRookPiece;
+ return var->castlingRookPieces[c];
}
inline PieceType Position::king_type() const {
v->remove_piece(KNIGHT);
v->add_piece(ROOKNI, 'r');
v->add_piece(KNIROO, 'n');
- v->castlingRookPiece = ROOKNI;
+ v->castlingRookPieces[WHITE] = v->castlingRookPieces[BLACK] = piece_set(ROOKNI);
v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | ROOKNI | BISHOP | KNIROO;
v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOKNI | BISHOP | KNIROO;
return v;
v->remove_piece(KNIGHT);
v->startFen = "rmbqkbmr/pppppppp/8/8/8/8/PPPPPPPP/RMBQKBMR w KQkq - 0 1";
v->kingType = KNIGHT;
- v->castlingKingPiece = KING;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = KING;
v->promotionPieceTypes[WHITE] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP;
v->promotionPieceTypes[BLACK] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP;
return v;
v->variantTemplate = "giveaway";
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
- v->castlingKingPiece = COMMONER;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->promotionPieceTypes[WHITE] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
v->promotionPieceTypes[BLACK] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
v->stalemateValue = VALUE_MATE;
Variant* v = chess_variant_base()->init();
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
- v->castlingKingPiece = COMMONER;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->promotionPieceTypes[WHITE] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
v->promotionPieceTypes[BLACK] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
v->extinctionValue = -VALUE_MATE;
Variant* v = chess_variant_base()->init();
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
- v->castlingKingPiece = COMMONER;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->startFen = "knbqkbnk/pppppppp/8/8/8/8/PPPPPPPP/KNBQKBNK w - - 0 1";
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = piece_set(COMMONER);
v->variantTemplate = "atomic";
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
- v->castlingKingPiece = COMMONER;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = piece_set(COMMONER);
v->blastOnCapture = true;
Variant* v = chess_variant_base()->init();
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
- v->castlingKingPiece = COMMONER;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = piece_set(COMMONER);
v->duckGating = true;
Variant* v = bughouse_variant()->init();
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
- v->castlingKingPiece = COMMONER;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->mustDrop = true;
v->mustDropType = COMMONER;
v->extinctionValue = -VALUE_MATE;
v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOK | BISHOP;
return v;
}
+ // Perfect chess
+ // https://www.chessvariants.com/diffmove.dir/perfectchess.html
+ Variant* perfect_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->add_piece(CHANCELLOR, 'c');
+ v->add_piece(ARCHBISHOP, 'm');
+ v->add_piece(AMAZON, 'g');
+ v->startFen = "cmqgkbnr/pppppppp/8/8/8/8/PPPPPPPP/CMQGKBNR w KQkq - 0 1";
+ v->promotionPieceTypes[WHITE] = piece_set(AMAZON) | CHANCELLOR | ARCHBISHOP | QUEEN | ROOK | BISHOP | KNIGHT;
+ v->promotionPieceTypes[BLACK] = piece_set(AMAZON) | CHANCELLOR | ARCHBISHOP | QUEEN | ROOK | BISHOP | KNIGHT;
+ v->castlingRookPieces[WHITE] = v->castlingRookPieces[BLACK] |= piece_set(CHANCELLOR);
+ return v;
+ }
// Spartan chess
// https://www.chessvariants.com/rules/spartan-chess
Variant* spartan_variant() {
v->capturesToHand = false;
v->pieceDrops = false;
v->promotedPieceType[CUSTOM_PIECES] = COMMONER;
- v->castlingKingPiece = COMMONER;
+ v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = piece_set(COMMONER);
v->extinctionPseudoRoyal = true;
add("gardner", gardner_variant());
add("almost", almost_variant());
add("chigorin", chigorin_variant());
+ add("perfect", perfect_variant());
add("spartan", spartan_variant());
add("shatar", shatar_variant());
add("coregal", coregal_variant());
File castlingQueensideFile = FILE_C;
Rank castlingRank = RANK_1;
File castlingKingFile = FILE_E;
- PieceType castlingKingPiece = KING;
- PieceType castlingRookPiece = ROOK;
+ PieceType castlingKingPiece[COLOR_NB] = {KING, KING};
+ PieceSet castlingRookPieces[COLOR_NB] = {piece_set(ROOK), piece_set(ROOK)};
PieceType kingType = KING;
bool checking = true;
bool dropChecks = true;
# castlingRank: relative rank of castling [Rank] (default: 1)
# castlingKingFile: starting file of the castlingKingPiece if there can be more than one of that type [File] (default: e)
# castlingKingPiece: first piece type that participates in castling [PieceType] (default: k)
-# castlingRookPiece: second piece type that participates in castling [PieceType] (default: r)
+# castlingRookPieces: second piece type that participates in castling [PieceSet] (default: r)
# checking: allow checks [bool] (default: true)
# dropChecks: allow checks by piece drops [bool] (default: true)
# mustCapture: captures are mandatory (check evasion still takes precedence) [bool] (default: false)
expect perft.exp sittuyin "fen 8/6s1/5P2/3n4/pR2K2S/1P6/1k4p1/8[] w - - 1 50" 4 268869 > /dev/null
expect perft.exp sittuyin "fen 1k5K/3r2P1/8/8/8/8/8/8[] w - - 0 1" 5 68662 > /dev/null
expect perft.exp chigorin "fen 8/7P/2k5/8/8/5K2/p7/8 w - - 0 1" 2 120 > /dev/null
+ expect perft.exp perfect startpos 3 15082 > /dev/null
+ expect perft.exp perfect "fen c3k2r/pppppppp/8/8/8/8/PPPPPPPP/C3K2R w KQkq - 0 1" 3 17500 > /dev/null
expect perft.exp spartan startpos 3 14244 > /dev/null
# duple check & mate
expect perft.exp spartan "fen k6k/hh2Q2h/8/8/8/8/8/4K3 w - - 0 1" 3 6130 > /dev/null