Support Game of the Amazons
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 15 Aug 2020 09:58:26 +0000 (11:58 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 15 Aug 2020 16:48:38 +0000 (18:48 +0200)
https://en.wikipedia.org/wiki/Game_of_the_Amazons

bench: 4413936

12 files changed:
Readme.md
src/movegen.cpp
src/parser.cpp
src/piece.cpp
src/position.cpp
src/position.h
src/types.h
src/uci.cpp
src/variant.cpp
src/variant.h
src/variants.ini
tests/perft.sh

index ab4ab78..a0ce3f8 100644 (file)
--- a/Readme.md
+++ b/Readme.md
@@ -54,6 +54,7 @@ The games currently supported besides chess are listed below. Fairy-Stockfish ca
 - [Dobutsu shogi](https://en.wikipedia.org/wiki/Dōbutsu_shōgi), [Goro goro shogi](https://en.wikipedia.org/wiki/D%C5%8Dbutsu_sh%C5%8Dgi#Variation)
 
 ### Related games
+- [Amazons](https://en.wikipedia.org/wiki/Game_of_the_Amazons)
 - [Ataxx](https://en.wikipedia.org/wiki/Ataxx)
 - [Breakthrough](https://en.wikipedia.org/wiki/Breakthrough_(board_game))
 - [Clobber](https://en.wikipedia.org/wiki/Clobber)
index bcad30c..aca70b2 100644 (file)
@@ -28,6 +28,19 @@ namespace {
   template<MoveType T>
   ExtMove* make_move_and_gating(const Position& pos, ExtMove* moveList, Color us, Square from, Square to) {
 
+    // Arrow gating moves
+    if (pos.arrow_gating())
+    {
+        for (PieceType pt_gating : pos.piece_types())
+            if (pos.count_in_hand(us, pt_gating))
+            {
+                Bitboard b = pos.drop_region(us, pt_gating) & moves_bb(us, type_of(pos.piece_on(from)), to, pos.pieces() ^ from) & ~(pos.pieces() ^ from);
+                while (b)
+                    *moveList++ = make_gating<T>(from, to, pt_gating, pop_lsb(&b));
+            }
+        return moveList;
+    }
+
     *moveList++ = make<T>(from, to);
 
     // Gating moves
index 405487f..51caf97 100644 (file)
@@ -269,6 +269,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("shogiDoubledPawn", v->shogiDoubledPawn);
     parse_attribute("immobilityIllegal", v->immobilityIllegal);
     parse_attribute("gating", v->gating);
+    parse_attribute("arrowGating", v->arrowGating);
     parse_attribute("seirawanGating", v->seirawanGating);
     parse_attribute("cambodianMoves", v->cambodianMoves);
     parse_attribute("diagonalLines", v->diagonalLines);
index 63344d5..5824fc4 100644 (file)
@@ -299,6 +299,13 @@ namespace {
                        2 * SOUTH_WEST, 2 * SOUTH + WEST, 2 * SOUTH, 2 * SOUTH + EAST, 2 * SOUTH_EAST};
       return p;
   }
+  PieceInfo* quiet_queen_piece() {
+      PieceInfo* p = queen_piece();
+      p->name = "quietQueen";
+      p->betza = "mQ";
+      p->sliderCapture = {};
+      return p;
+  }
   PieceInfo* cannon_piece() {
       PieceInfo* p = new PieceInfo();
       p->name = "cannon";
@@ -403,6 +410,7 @@ void PieceMap::init() {
   add(BREAKTHROUGH_PIECE, breakthrough_piece());
   add(IMMOBILE_PIECE, immobile_piece());
   add(ATAXX_PIECE, ataxx_piece());
+  add(QUIET_QUEEN, quiet_queen_piece());
   add(CANNON, cannon_piece());
   add(JANGGI_CANNON, janggi_cannon_piece());
   add(SOLDIER, soldier_piece());
index 84cd5f4..f153ad7 100644 (file)
@@ -72,7 +72,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
               os << " *";
           else
               os << "  ";
-          if (pos.piece_drops() || pos.seirawan_gating())
+          if (pos.piece_drops() || pos.seirawan_gating() || pos.arrow_gating())
           {
               os << " [";
               for (PieceType pt = KING; pt >= PAWN; --pt)
@@ -546,7 +546,7 @@ void Position::set_state(StateInfo* si) const {
           for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
               si->materialKey ^= Zobrist::psq[pc][cnt];
 
-          if (piece_drops() || seirawan_gating())
+          if (piece_drops() || seirawan_gating() || arrow_gating())
               si->key ^= Zobrist::inHand[pc][pieceCountInHand[c][pt]];
       }
 
@@ -637,7 +637,7 @@ const string Position::fen(bool sfen, bool showPromoted, int countStarted, std::
   }
 
   // pieces in hand
-  if (piece_drops() || seirawan_gating())
+  if (piece_drops() || seirawan_gating() || arrow_gating())
   {
       ss << '[';
       if (holdings != "-")
@@ -1001,7 +1001,7 @@ bool Position::pseudo_legal(const Move m) const {
                 || (drop_promoted() && type_of(pc) == promoted_piece_type(in_hand_piece_type(m))));
 
   // Use a slower but simpler function for uncommon cases
-  if (type_of(m) != NORMAL || is_gating(m))
+  if (type_of(m) != NORMAL || is_gating(m) || arrow_gating())
       return MoveList<LEGAL>(*this).contains(m);
 
   // Handle the case where a mandatory piece promotion/demotion is not taken
index eae5c66..4afd049 100644 (file)
@@ -142,6 +142,7 @@ public:
   bool shogi_doubled_pawn() const;
   bool immobility_illegal() const;
   bool gating() const;
+  bool arrow_gating() const;
   bool seirawan_gating() const;
   bool cambodian_moves() const;
   Bitboard diagonal_lines() const;
@@ -612,6 +613,11 @@ inline bool Position::gating() const {
   return var->gating;
 }
 
+inline bool Position::arrow_gating() const {
+  assert(var != nullptr);
+  return var->arrowGating;
+}
+
 inline bool Position::seirawan_gating() const {
   assert(var != nullptr);
   return var->seirawanGating;
index ac46f2a..9f0229d 100644 (file)
@@ -218,7 +218,7 @@ typedef uint64_t Bitboard;
 constexpr int SQUARE_BITS = 6;
 #endif
 
-constexpr int MAX_MOVES = 1024;
+constexpr int MAX_MOVES = 4096;
 constexpr int MAX_PLY   = 246;
 
 /// A move needs 16 bits to be stored
@@ -349,6 +349,7 @@ enum Value : int {
   BreakthroughPieceValueMg = 300,   BreakthroughPieceValueEg = 300,
   ImmobilePieceValueMg     = 50,    ImmobilePieceValueEg     = 50,
   AtaxxPieceValueMg        = 100,   AtaxxPieceValueEg        = 100,
+  QuietQueenPieceValueMg   = 400,   QuietQueenPieceValueEg   = 400,
   CannonPieceValueMg       = 800,   CannonPieceValueEg       = 700,
   JanggiCannonPieceValueMg = 800,   JanggiCannonPieceValueEg = 600,
   SoldierValueMg           = 200,   SoldierValueEg           = 270,
@@ -371,7 +372,7 @@ enum PieceType {
   FERS, MET = FERS, ALFIL, FERS_ALFIL, SILVER, KHON = SILVER, AIWOK, BERS, DRAGON = BERS,
   ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI, KNIROO, ROOKNI,
   SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, DRAGON_HORSE,
-  CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, ATAXX_PIECE, CANNON, JANGGI_CANNON,
+  CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, ATAXX_PIECE, QUIET_QUEEN, CANNON, JANGGI_CANNON,
   SOLDIER, HORSE, ELEPHANT, JANGGI_ELEPHANT, BANNER,
   WAZIR, COMMONER, CENTAUR, KING,
   ALL_PIECES = 0,
@@ -408,9 +409,9 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
     FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
     ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
     ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
-    ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg,
+    ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg, QuietQueenPieceValueMg,
     CannonPieceValueMg, JanggiCannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg,
-    WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO, VALUE_ZERO,
+    WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
@@ -420,9 +421,9 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
     FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
     ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
     ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
-    ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg,
+    ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, AtaxxPieceValueMg, QuietQueenPieceValueMg,
     CannonPieceValueMg, JanggiCannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg,
-    WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO, VALUE_ZERO,
+    WazirValueMg, CommonerValueMg, CentaurValueMg, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
@@ -432,9 +433,9 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
     FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
     ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
     ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
-    ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg,
+    ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg, QuietQueenPieceValueEg,
     CannonPieceValueEg, JanggiCannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg,
-    WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO, VALUE_ZERO,
+    WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
@@ -444,9 +445,9 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
     FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
     ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
     ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
-    ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg,
+    ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, AtaxxPieceValueEg, QuietQueenPieceValueEg,
     CannonPieceValueEg, JanggiCannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg,
-    WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO, VALUE_ZERO,
+    WazirValueEg, CommonerValueEg, CentaurValueEg, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
     VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
index 3997d42..d3a891b 100644 (file)
@@ -409,7 +409,11 @@ string UCI::move(const Position& pos, Move m) {
   else if (type_of(m) == PIECE_DEMOTION)
       move += '-';
   else if (is_gating(m))
+  {
       move += pos.piece_to_char()[make_piece(BLACK, gating_type(m))];
+      if (gating_square(m) != from)
+          move += UCI::square(pos, gating_square(m));
+  }
 
   return move;
 }
index 999198d..3e4bddf 100644 (file)
@@ -837,6 +837,21 @@ namespace {
                       "pPpPpPpPpP/PpPpPpPpPp/pPpPpPpPpP/PpPpPpPpPp/pPpPpPpPpP w 0 1";
         return v;
     }
+    // Game of the Amazons
+    // https://en.wikipedia.org/wiki/Game_of_the_Amazons
+    Variant* amazons_variant() {
+        Variant* v = fairy_variant_base();
+        v->pieceToCharTable = "P...Q.................p...q.................";
+        v->maxRank = RANK_10;
+        v->maxFile = FILE_J;
+        v->reset_pieces();
+        v->add_piece(QUIET_QUEEN, 'q');
+        v->add_piece(IMMOBILE_PIECE, 'p');
+        v->startFen = "3q2q3/10/10/q8q/10/10/Q8Q/10/10/3Q2Q3[PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPpppppppppppppppppppppppppppppppppppppppppppppp] w - - 0 1";
+        v->stalemateValue = -VALUE_MATE;
+        v->arrowGating = true;
+        return v;
+    }
     Variant* xiangqi_variant() {
         Variant* v = minixiangqi_variant();
         v->pieceToCharTable = "PN.R.AB..K.C..........pn.r.ab..k.c..........";
@@ -1006,6 +1021,7 @@ void VariantMap::init() {
     add("grand", grand_variant());
     add("shako", shako_variant());
     add("clobber10", clobber10_variant());
+    add("amazons", amazons_variant());
     add("xiangqi", xiangqi_variant());
     add("manchu", manchu_variant());
     add("supply", supply_variant());
index ad51aa5..60c9e20 100644 (file)
@@ -86,6 +86,7 @@ struct Variant {
   bool shogiDoubledPawn = true;
   bool immobilityIllegal = false;
   bool gating = false;
+  bool arrowGating = false;
   bool seirawanGating = false;
   bool cambodianMoves = false;
   Bitboard diagonalLines = 0;
index d0f62e9..7ad48f3 100644 (file)
@@ -74,6 +74,7 @@
 # breakthrough
 # immobile (piece without moves)
 # ataxx (mDNA)
+# quietQueen
 # cannon
 # janggiCannon
 # soldier
 # shogiDoubledPawn: allow shogi pawns to be doubled [bool] (default: true)
 # immobilityIllegal: pieces may not move to squares where they can never move from [bool] (default: false)
 # gating: maintain squares on backrank with extra rights in castling field of FEN [bool] (default: false)
+# arrowGating: allow gating in Game of the Amazons style [bool] (default: false)
 # seirawanGating: allow gating of pieces in hand like in S-Chess, requires "gating = true" [bool] (default: false)
 # cambodianMoves: enable special moves of cambodian chess, requires "gating = true" [bool] (default: false)
 # diagonalLines: enable special moves along diagonal for specific squares (Janggi) [Bitboard]
index 5606723..faf594f 100755 (executable)
@@ -81,6 +81,7 @@ if [[ $1 == "largeboard" ]]; then
   expect perft.exp janggi startpos 4 1065277 > /dev/null
   expect perft.exp janggi "fen 1n1kaabn1/cr2N4/5C1c1/p1pNp3p/9/9/P1PbP1P1P/3r1p3/4A4/R1BA1KB1R b - - 0 1" 4 76763 > /dev/null
   expect perft.exp janggi "fen 1Pbcka3/3nNn1c1/N2CaC3/1pB6/9/9/5P3/9/4K4/9 w - - 0 23" 4 151202 > /dev/null
+  expect perft.exp amazons startpos 1 2176 > /dev/null
 fi
 
 rm perft.exp