From 7d2e346ee60d342c590ef49e7428fc29cc964310 Mon Sep 17 00:00:00 2001 From: Fabian Fichter Date: Sat, 15 Aug 2020 11:58:26 +0200 Subject: [PATCH] Support Game of the Amazons https://en.wikipedia.org/wiki/Game_of_the_Amazons bench: 4413936 --- Readme.md | 1 + src/movegen.cpp | 13 +++++++++++++ src/parser.cpp | 1 + src/piece.cpp | 8 ++++++++ src/position.cpp | 8 ++++---- src/position.h | 6 ++++++ src/types.h | 21 +++++++++++---------- src/uci.cpp | 4 ++++ src/variant.cpp | 16 ++++++++++++++++ src/variant.h | 1 + src/variants.ini | 2 ++ tests/perft.sh | 1 + 12 files changed, 68 insertions(+), 14 deletions(-) diff --git a/Readme.md b/Readme.md index ab4ab78..a0ce3f8 100644 --- 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) diff --git a/src/movegen.cpp b/src/movegen.cpp index bcad30c..aca70b2 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -28,6 +28,19 @@ namespace { template 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(from, to, pt_gating, pop_lsb(&b)); + } + return moveList; + } + *moveList++ = make(from, to); // Gating moves diff --git a/src/parser.cpp b/src/parser.cpp index 405487f..51caf97 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -269,6 +269,7 @@ Variant* VariantParser::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); diff --git a/src/piece.cpp b/src/piece.cpp index 63344d5..5824fc4 100644 --- a/src/piece.cpp +++ b/src/piece.cpp @@ -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()); diff --git a/src/position.cpp b/src/position.cpp index 84cd5f4..f153ad7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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(*this).contains(m); // Handle the case where a mandatory piece promotion/demotion is not taken diff --git a/src/position.h b/src/position.h index eae5c66..4afd049 100644 --- a/src/position.h +++ b/src/position.h @@ -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; diff --git a/src/types.h b/src/types.h index ac46f2a..9f0229d 100644 --- a/src/types.h +++ b/src/types.h @@ -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, diff --git a/src/uci.cpp b/src/uci.cpp index 3997d42..d3a891b 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -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; } diff --git a/src/variant.cpp b/src/variant.cpp index 999198d..3e4bddf 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -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()); diff --git a/src/variant.h b/src/variant.h index ad51aa5..60c9e20 100644 --- a/src/variant.h +++ b/src/variant.h @@ -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; diff --git a/src/variants.ini b/src/variants.ini index d0f62e9..7ad48f3 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -74,6 +74,7 @@ # breakthrough # immobile (piece without moves) # ataxx (mDNA) +# quietQueen # cannon # janggiCannon # soldier @@ -150,6 +151,7 @@ # 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] diff --git a/tests/perft.sh b/tests/perft.sh index 5606723..faf594f 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -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 -- 1.7.0.4