Support New Zealand chess
authorFabian Fichter <ianfab@users.noreply.github.com>
Thu, 14 Nov 2019 16:19:48 +0000 (17:19 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Thu, 14 Nov 2019 16:19:48 +0000 (17:19 +0100)
Allow castling with one non-rook piece type and add New Zealand chess (#52).

bench: 4470822

Readme.md
src/parser.cpp
src/piece.cpp
src/position.cpp
src/position.h
src/psqt.cpp
src/types.h
src/variant.cpp
src/variant.h
src/variants.ini
tests/perft.sh

index a5e44e0..a989b78 100644 (file)
--- 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)
index 8c70e76..0d91f0f 100644 (file)
@@ -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);
index 78c0ad4..60f6f83 100644 (file)
@@ -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());
index 015d81b..0279eff 100644 (file)
@@ -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<KING>(~sideToMove))
-            && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
+      return   (PseudoAttacks[sideToMove][type_of(piece_on(rfrom))][rto] & square<KING>(~sideToMove))
+            && (attacks_bb(sideToMove, type_of(piece_on(rfrom)), rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~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<true>(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<KING>(c) && (castlingRightsMask[square<KING>(c)] & cr) != cr))
               assert(0 && "pos_is_ok: Castling");
index 67a98d2..d0eb490 100644 (file)
@@ -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;
index 0e003b9..985c618 100644 (file)
 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 }
index e38a0bb..4858ccc 100644 (file)
@@ -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,
index d6da856..c45fca1 100644 (file)
@@ -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());
index 3300ede..747463c 100644 (file)
@@ -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;
index aa20f14..0d70262 100644 (file)
@@ -60,6 +60,8 @@
 # amazon (=queen+knight)
 # knibis
 # biskni
+# kniroo
+# rookni
 # shogi_pawn
 # lance
 # shogi_knight
 # 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)
index 8c564c9..ea46d12 100755 (executable)
@@ -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