From: Fabian Fichter Date: Thu, 14 Nov 2019 16:19:48 +0000 (+0100) Subject: Support New Zealand chess X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=f24582e827438f8d21b7e5060df3dcce61d1b8b5;p=fairystockfish.git Support New Zealand chess Allow castling with one non-rook piece type and add New Zealand chess (#52). bench: 4470822 --- diff --git a/Readme.md b/Readme.md index a5e44e0..a989b78 100644 --- a/Readme.md +++ b/Readme.md @@ -23,11 +23,12 @@ The games currently supported besides chess are listed below. Fairy-Stockfish ca ### Chess variants - [Capablanca](https://en.wikipedia.org/wiki/Capablanca_Chess), [Janus](https://en.wikipedia.org/wiki/Janus_Chess), [Modern](https://en.wikipedia.org/wiki/Modern_Chess_(chess_variant)), [Chancellor](https://en.wikipedia.org/wiki/Chancellor_Chess), [Embassy](https://en.wikipedia.org/wiki/Embassy_Chess), [Gothic](https://www.chessvariants.com/large.dir/gothicchess.html), [Capablanca random chess](https://en.wikipedia.org/wiki/Capablanca_Random_Chess) -- [Grand](https://en.wikipedia.org/wiki/Grand_Chess), [Shako](https://www.chessvariants.com/large.dir/shako.html) +- [Grand](https://en.wikipedia.org/wiki/Grand_Chess), [Shako](https://www.chessvariants.com/large.dir/shako.html), [Centaur](https://www.chessvariants.com/large.dir/contest/royalcourt.html) - [Chess960](https://en.wikipedia.org/wiki/Chess960), [Placement/Pre-Chess](https://www.chessvariants.com/link/placement-chess) - [Crazyhouse](https://en.wikipedia.org/wiki/Crazyhouse), [Loop](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Chessgi](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Pocket Knight](http://www.chessvariants.com/other.dir/pocket.html), Capablanca-Crazyhouse - [Seirawan](https://en.wikipedia.org/wiki/Seirawan_chess), Seirawan-Crazyhouse -- [Amazon](https://en.wikipedia.org/wiki/Amazon_(chess)), [Chigorin](https://en.wikipedia.org/wiki/Chigorin_Chess), [Almost chess](https://en.wikipedia.org/wiki/Almost_Chess), [Hoppel-Poppel](http://www.chessvariants.com/diffmove.dir/hoppel-poppel.html) +- [Amazon](https://en.wikipedia.org/wiki/Amazon_(chess)), [Chigorin](https://en.wikipedia.org/wiki/Chigorin_Chess), [Almost chess](https://en.wikipedia.org/wiki/Almost_Chess) +- [Hoppel-Poppel](http://www.chessvariants.com/diffmove.dir/hoppel-poppel.html), New Zealand - [Antichess](https://lichess.org/variant/antichess), [Giveaway](http://www.chessvariants.com/diffobjective.dir/giveaway.old.html), [Losers](https://www.chessclub.com/help/Wild17), [Codrus](http://www.binnewirtz.com/Schlagschach1.htm) - [Extinction](https://en.wikipedia.org/wiki/Extinction_chess), [Kinglet](https://en.wikipedia.org/wiki/V._R._Parton#Kinglet_Chess) - [King of the Hill](https://en.wikipedia.org/wiki/King_of_the_Hill_(chess)), [Racing Kings](https://en.wikipedia.org/wiki/V._R._Parton#Racing_Kings) diff --git a/src/parser.cpp b/src/parser.cpp index 8c70e76..0d91f0f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -156,6 +156,16 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("castlingKingsideFile", v->castlingKingsideFile); parse_attribute("castlingQueensideFile", v->castlingQueensideFile); parse_attribute("castlingRank", v->castlingRank); + // castling rook piece type + const auto& it_castling_rook_piece = config.find("castlingRookPiece"); + if (it_castling_rook_piece != config.end()) + { + char token; + size_t idx; + std::stringstream ss(it_castling_rook_piece->second); + if (ss >> token && (idx = v->pieceToChar.find(token)) != std::string::npos) + v->castlingRookPiece = PieceType(idx); + } parse_attribute("checking", v->checking); parse_attribute("mustCapture", v->mustCapture); parse_attribute("mustDrop", v->mustDrop); diff --git a/src/piece.cpp b/src/piece.cpp index 78c0ad4..60f6f83 100644 --- a/src/piece.cpp +++ b/src/piece.cpp @@ -179,6 +179,26 @@ namespace { p->sliderCapture = {}; return p; } + PieceInfo* kniroo_piece() { + PieceInfo* p = rook_piece(); + p->name = "kniroo"; + PieceInfo* p2 = knight_piece(); + p->merge(p2); + delete p2; + p->stepsCapture = {}; + p->sliderQuiet = {}; + return p; + } + PieceInfo* rookni_piece() { + PieceInfo* p = rook_piece(); + p->name = "rookni"; + PieceInfo* p2 = knight_piece(); + p->merge(p2); + delete p2; + p->stepsQuiet = {}; + p->sliderCapture = {}; + return p; + } PieceInfo* shogi_pawn_piece() { PieceInfo* p = new PieceInfo(); p->name = "shogi_pawn"; @@ -295,6 +315,8 @@ void PieceMap::init() { add(AMAZON, amazon_piece()); add(KNIBIS, knibis_piece()); add(BISKNI, biskni_piece()); + add(KNIROO, kniroo_piece()); + add(ROOKNI, rookni_piece()); add(SHOGI_PAWN, shogi_pawn_piece()); add(LANCE, lance_piece()); add(SHOGI_KNIGHT, shogi_knight_piece()); diff --git a/src/position.cpp b/src/position.cpp index 015d81b..0279eff 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -356,7 +356,7 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, { Square rsq; Color c = islower(token) ? BLACK : WHITE; - Piece rook = make_piece(c, ROOK); + Piece rook = make_piece(c, castling_rook_piece()); token = char(toupper(token)); @@ -393,7 +393,7 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, for (Color c : {WHITE, BLACK}) if ((gates(c) & pieces(KING)) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand())) { - Bitboard castling_rooks = gates(c) & pieces(ROOK); + Bitboard castling_rooks = gates(c) & pieces(castling_rook_piece()); while (castling_rooks) set_castling_right(c, pop_lsb(&castling_rooks)); } @@ -1094,8 +1094,8 @@ bool Position::gives_check(Move m) const { Square kto = make_square(rfrom > kfrom ? castling_kingside_file() : castling_queenside_file(), castling_rank(sideToMove)); Square rto = kto + (rfrom > kfrom ? WEST : EAST); - return (PseudoAttacks[sideToMove][ROOK][rto] & square(~sideToMove)) - && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~sideToMove)); + return (PseudoAttacks[sideToMove][type_of(piece_on(rfrom))][rto] & square(~sideToMove)) + && (attacks_bb(sideToMove, type_of(piece_on(rfrom)), rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~sideToMove)); } default: assert(false); @@ -1154,7 +1154,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { if (type_of(m) == CASTLING) { assert(type_of(pc) != NO_PIECE_TYPE); - assert(captured == make_piece(us, ROOK)); + assert(captured == make_piece(us, castling_rook_piece())); Square rfrom, rto; do_castling(us, from, to, rfrom, rto); @@ -1251,13 +1251,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { { if (type_of(pc) == KING && file_of(to) == FILE_E) { - Bitboard castling_rooks = pieces(us, ROOK) + Bitboard castling_rooks = pieces(us, castling_rook_piece()) & rank_bb(castling_rank(us)) & (file_bb(FILE_A) | file_bb(max_file())); while (castling_rooks) set_castling_right(us, pop_lsb(&castling_rooks)); } - else if (type_of(pc) == ROOK) + else if (type_of(pc) == castling_rook_piece()) { if ( (file_of(to) == FILE_A || file_of(to) == max_file()) && piece_on(make_square(FILE_E, castling_rank(us))) == make_piece(us, KING)) @@ -1560,12 +1560,13 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ rto = to + (kingSide ? WEST : EAST); // Remove both pieces first since squares could overlap in Chess960 - Piece castling_piece = piece_on(Do ? from : to); - remove_piece(castling_piece, Do ? from : to); - remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); + Piece castlingKingPiece = piece_on(Do ? from : to); + Piece castlingRookPiece = piece_on(Do ? rfrom : rto); + remove_piece(castlingKingPiece, Do ? from : to); + remove_piece(castlingRookPiece, Do ? rfrom : rto); board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us - put_piece(castling_piece, Do ? to : from); - put_piece(make_piece(us, ROOK), Do ? rto : rfrom); + put_piece(castlingKingPiece, Do ? to : from); + put_piece(castlingRookPiece, Do ? rto : rfrom); } @@ -2105,7 +2106,7 @@ bool Position::pos_is_ok() const { if (!can_castle(cr)) continue; - if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK) + if ( piece_on(castlingRookSquare[cr]) != make_piece(c, castling_rook_piece()) || castlingRightsMask[castlingRookSquare[cr]] != cr || (count(c) && (castlingRightsMask[square(c)] & cr) != cr)) assert(0 && "pos_is_ok: Castling"); diff --git a/src/position.h b/src/position.h index 67a98d2..d0eb490 100644 --- a/src/position.h +++ b/src/position.h @@ -118,6 +118,7 @@ public: File castling_kingside_file() const; File castling_queenside_file() const; Rank castling_rank(Color c) const; + PieceType castling_rook_piece() const; bool checking_permitted() const; bool must_capture() const; bool must_drop() const; @@ -431,6 +432,11 @@ inline Rank Position::castling_rank(Color c) const { return relative_rank(c, var->castlingRank, max_rank()); } +inline PieceType Position::castling_rook_piece() const { + assert(var != nullptr); + return var->castlingRookPiece; +} + inline bool Position::checking_permitted() const { assert(var != nullptr); return var->checking; diff --git a/src/psqt.cpp b/src/psqt.cpp index 0e003b9..985c618 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -27,13 +27,13 @@ Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg, - ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, + ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg, ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg, ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, CannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, WazirValueMg, CommonerValueMg, CentaurValueMg }, { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg, - ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, + ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg, ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg, ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, CannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, WazirValueEg, CommonerValueEg, CentaurValueEg } diff --git a/src/types.h b/src/types.h index e38a0bb..4858ccc 100644 --- a/src/types.h +++ b/src/types.h @@ -326,6 +326,8 @@ enum Value : int { AmazonValueMg = 3000, AmazonValueEg = 3000, KnibisValueMg = 1100, KnibisValueEg = 1200, BiskniValueMg = 750, BiskniValueEg = 700, + KnirooValueMg = 900, KnirooValueEg = 900, + RookniValueMg = 900, RookniValueEg = 900, ShogiPawnValueMg = 90, ShogiPawnValueEg = 100, LanceValueMg = 350, LanceValueEg = 250, ShogiKnightValueMg = 350, ShogiKnightValueEg = 300, @@ -351,7 +353,7 @@ constexpr int PIECE_TYPE_BITS = 6; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS) enum PieceType { NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, FERS, MET = FERS, ALFIL, FERS_ALFIL, SILVER, KHON = SILVER, AIWOK, BERS, DRAGON = BERS, - ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI, + ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI, KNIROO, ROOKNI, SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, DRAGON_HORSE, CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, CANNON, SOLDIER, HORSE, ELEPHANT, WAZIR, COMMONER, CENTAUR, KING, ALL_PIECES = 0, diff --git a/src/variant.cpp b/src/variant.cpp index d6da856..c45fca1 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -133,6 +133,16 @@ namespace { v->promotionPieceTypes = {QUEEN, ROOK, BISKNI, KNIBIS}; return v; } + Variant* newzealand_variant() { + Variant* v = chess_variant(); + v->remove_piece(ROOK); + v->remove_piece(KNIGHT); + v->add_piece(ROOKNI, 'r'); + v->add_piece(KNIROO, 'n'); + v->castlingRookPiece = ROOKNI; + v->promotionPieceTypes = {QUEEN, ROOKNI, BISHOP, KNIROO}; + return v; + } Variant* kingofthehill_variant() { Variant* v = fairy_variant_base(); v->flagPiece = KING; @@ -737,6 +747,7 @@ void VariantMap::init() { add("shatranj", shatranj_variant()); add("amazon", amazon_variant()); add("hoppelpoppel", hoppelpoppel_variant()); + add("newzealand", newzealand_variant()); add("kingofthehill", kingofthehill_variant()); add("racingkings", racingkings_variant()); add("losers", losers_variant()); diff --git a/src/variant.h b/src/variant.h index 3300ede..747463c 100644 --- a/src/variant.h +++ b/src/variant.h @@ -61,6 +61,7 @@ struct Variant { File castlingKingsideFile = FILE_G; File castlingQueensideFile = FILE_C; Rank castlingRank = RANK_1; + PieceType castlingRookPiece = ROOK; bool checking = true; bool mustCapture = false; bool mustDrop = false; diff --git a/src/variants.ini b/src/variants.ini index aa20f14..0d70262 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -60,6 +60,8 @@ # amazon (=queen+knight) # knibis # biskni +# kniroo +# rookni # shogi_pawn # lance # shogi_knight @@ -113,6 +115,7 @@ # castlingKingsideFile: destination file of king after kingside castling [File] (default: g) # castlingQueensideFile: destination file of king after queenside castling [File] (default: c) # castlingRank: relative rank of castling [Rank] (default: 1) +# castlingRookPiece: piece type that participates in castling [PieceType] (default: r) # checking: allow checks [bool] (default: true) # mustCapture: captures are mandatory (check evasion still takes precedence) [bool] (default: false) # mustDrop: drops are mandatory (e.g., for Sittuyin setup phase) [bool] (default: false) diff --git a/tests/perft.sh b/tests/perft.sh index 8c564c9..ea46d12 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -39,6 +39,8 @@ if [[ $1 == "" || $1 == "variant" ]]; then expect perft.exp amazon startpos 5 9319911 > /dev/null expect perft.exp makruk startpos 5 6223994 > /dev/null expect perft.exp shatranj startpos 5 1164248 > /dev/null + expect perft.exp hoppelpoppel startpos 5 5056643 > /dev/null + expect perft.exp newzealand startpos 5 4987426 > /dev/null expect perft.exp loop startpos 5 4888832 > /dev/null expect perft.exp chessgi startpos 5 4889167 > /dev/null expect perft.exp racingkings startpos 5 9472927 > /dev/null