diff --git a/src/movegen.cpp b/src/movegen.cpp index 0c14dd75b..b658619d3 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -41,13 +41,23 @@ namespace { } if (T == EN_PASSANT) b ^= pos.capture_square(to); - if (pos.variant()->arrowWalling) + + if (pos.walling_rule() == ARROW) b &= moves_bb(us, type_of(pos.piece_on(from)), to, pos.pieces() ^ from); - if ((pos.variant()->staticWalling)||(pos.variant()->duckWalling)) - b &= pos.variant()->wallingRegion[us]; - if (pos.variant()->pastWalling) + + //Any current or future wall variant must follow the walling region rule if set: + b &= pos.variant()->wallingRegion[us]; + + if (pos.walling_rule() == PAST) b &= square_bb(from); + if (pos.walling_rule() == EDGE) + { + Bitboard wallsquares = pos.state()->wallSquares; + b &= (FileABB | file_bb(pos.max_file()) | Rank1BB | rank_bb(pos.max_rank())) | + ( shift(wallsquares) | shift(wallsquares) + | shift(wallsquares) | shift(wallsquares)); + } while (b) *moveList++ = make_gating(from, to, pt, pop_lsb(b)); return moveList; diff --git a/src/parser.cpp b/src/parser.cpp index 4871446b6..3d822ebb4 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -111,6 +111,16 @@ namespace { return value == "reversi" || value == "ataxx" || value == "quadwrangle" || value =="snort" || value == "none"; } + template <> bool set(const std::string& value, WallingRule& target) { + target = value == "arrow" ? ARROW + : value == "duck" ? DUCK + : value == "edge" ? EDGE + : value == "past" ? PAST + : value == "static" ? STATIC + : NO_WALLING; + return value == "arrow" || value == "duck" || value == "edge" || value =="past" || value == "static" || value == "none"; + } + template <> bool set(const std::string& value, Bitboard& target) { char file; int rank; @@ -181,6 +191,7 @@ template bool VariantParser::parse_attribute(co : std::is_same() ? "EnclosingRule" : std::is_same() ? "Bitboard" : std::is_same() ? "CastlingRights" + : std::is_same() ? "WallingRule" : typeid(T).name(); std::cerr << key << " - Invalid value " << it->second << " for type " << typeName << std::endl; } @@ -434,14 +445,11 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("dropNoDoubledCount", v->dropNoDoubledCount); parse_attribute("immobilityIllegal", v->immobilityIllegal); parse_attribute("gating", v->gating); - parse_attribute("arrowWalling", v->arrowWalling); - parse_attribute("duckWalling", v->duckWalling); + parse_attribute("wallingRule", v->wallingRule); parse_attribute("wallingRegionWhite", v->wallingRegion[WHITE]); parse_attribute("wallingRegionBlack", v->wallingRegion[BLACK]); parse_attribute("wallingRegion", v->wallingRegion[WHITE]); parse_attribute("wallingRegion", v->wallingRegion[BLACK]); - parse_attribute("staticWalling", v->staticWalling); - parse_attribute("pastWalling", v->pastWalling); parse_attribute("seirawanGating", v->seirawanGating); parse_attribute("cambodianMoves", v->cambodianMoves); parse_attribute("diagonalLines", v->diagonalLines); @@ -553,8 +561,8 @@ Variant* VariantParser::parse(Variant* v) { std::cerr << "Inconsistent settings: castlingQueensideFile > castlingKingsideFile." << std::endl; // Check for limitations - if (v->pieceDrops && (v->arrowWalling || v->duckWalling || v->staticWalling || v->pastWalling)) - std::cerr << "pieceDrops and arrowWalling/duckWalling are incompatible." << std::endl; + if (v->pieceDrops && v->wallingRule) + std::cerr << "pieceDrops and any walling are incompatible." << std::endl; // Options incompatible with royal kings if (v->pieceTypes & KING) @@ -563,8 +571,8 @@ Variant* VariantParser::parse(Variant* v) { std::cerr << "Can not use kings with blastOnCapture." << std::endl; if (v->flipEnclosedPieces) std::cerr << "Can not use kings with flipEnclosedPieces." << std::endl; - if (v->duckWalling) - std::cerr << "Can not use kings with duckWalling." << std::endl; + if (v->wallingRule==DUCK) + std::cerr << "Can not use kings with wallingRule = duck." << std::endl; // We can not fully check support for custom king movements at this point, // since custom pieces are only initialized on loading of the variant. // We will assume this is valid, but it might cause problems later if it's not. diff --git a/src/position.cpp b/src/position.cpp index 70912854a..39f4888e2 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1104,7 +1104,7 @@ bool Position::legal(Move m) const { { Square kto = to; Bitboard occupied = (type_of(m) != DROP ? pieces() ^ from : pieces()); - if (var->duckWalling) + if (walling_rule() == DUCK) occupied ^= st->wallSquares; if (walling() || is_gating(m)) occupied |= gating_square(m); @@ -1303,15 +1303,29 @@ bool Position::pseudo_legal(const Move m) const { return checkers() ? MoveList< EVASIONS>(*this).contains(m) : MoveList(*this).contains(m); - // Illegal wall square placement - if (walling() && !((board_bb() & ~((pieces() ^ from) | to)) & gating_square(m))) - return false; - if (var->arrowWalling && !(moves_bb(us, type_of(pc), to, pieces() ^ from) & gating_square(m))) - return false; - if (var->pastWalling && (from != gating_square(m))) - return false; - if ((var->staticWalling || var->duckWalling) && !(var->wallingRegion[us] & gating_square(m))) - return false; + if (walling()) + { + Bitboard wallsquares = st->wallSquares; + + // Illegal wall square placement + if (!((board_bb() & ~((pieces() ^ from) | to)) & gating_square(m))) + return false; + if (!(var->wallingRegion[us] & gating_square(m)) || //putting a wall on disallowed square + wallsquares & gating_square(m)) //or square already with a wall + return false; + if (walling_rule() == ARROW && !(moves_bb(us, type_of(pc), to, pieces() ^ from) & gating_square(m))) + return false; + if (walling_rule() == PAST && (from != gating_square(m))) + return false; + if (walling_rule() == EDGE) + { + Bitboard validsquares = board_bb() & + ((FileABB | file_bb(max_file()) | Rank1BB | rank_bb(max_rank())) | + ( shift(wallsquares) | shift(wallsquares) + | shift(wallsquares) | shift(wallsquares))); + if (!(validsquares & gating_square(m))) return false; + }; + } // Handle the case where a mandatory piece promotion/demotion is not taken if ( mandatory_piece_promotion() @@ -2034,7 +2048,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { if (walling()) { // Reset wall squares for duck walling - if (var->duckWalling) + if (walling_rule() == DUCK) { Bitboard b = st->previous->wallSquares; byTypeBB[ALL_PIECES] ^= b; @@ -2476,7 +2490,7 @@ bool Position::see_ge(Move m, Value threshold) const { stmAttackers &= ~blockers_for_king(stm); // Ignore distant sliders - if (var->duckWalling) + if (walling_rule() == DUCK) stmAttackers &= attacks_bb(to) | ~(pieces(BISHOP, ROOK) | pieces(QUEEN)); if (!stmAttackers) @@ -2969,7 +2983,7 @@ bool Position::has_game_cycle(int ply) const { int end = captures_to_hand() ? st->pliesFromNull : std::min(st->rule50, st->pliesFromNull); - if (end < 3 || var->nFoldValue != VALUE_DRAW || var->perpetualCheckIllegal || var->materialCounting || var->moveRepetitionIllegal || var->duckWalling) + if (end < 3 || var->nFoldValue != VALUE_DRAW || var->perpetualCheckIllegal || var->materialCounting || var->moveRepetitionIllegal || walling_rule() == DUCK) return false; Key originalKey = st->key; diff --git a/src/position.h b/src/position.h index 34b09763b..2311776da 100644 --- a/src/position.h +++ b/src/position.h @@ -179,6 +179,7 @@ class Position { bool immobility_illegal() const; bool gating() const; bool walling() const; + WallingRule walling_rule() const; bool seirawan_gating() const; bool cambodian_moves() const; Bitboard diagonal_lines() const; @@ -761,7 +762,12 @@ inline bool Position::gating() const { inline bool Position::walling() const { assert(var != nullptr); - return var->arrowWalling || var->duckWalling || var->staticWalling || var->pastWalling; + return var->wallingRule != NO_WALLING; +} + +inline WallingRule Position::walling_rule() const { + assert(var != nullptr); + return var->wallingRule; } inline bool Position::seirawan_gating() const { diff --git a/src/search.cpp b/src/search.cpp index 2502fcc2c..77686ed0d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1185,7 +1185,7 @@ namespace { continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.variant()->duckWalling && !pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18) + 10 * !!pos.flag_region(pos.side_to_move())) * lmrDepth * lmrDepth))) + if (!(pos.walling_rule() == DUCK) && !pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18) + 10 * !!pos.flag_region(pos.side_to_move())) * lmrDepth * lmrDepth))) continue; } } diff --git a/src/types.h b/src/types.h index 99c5e1aa3..2fa03e7c4 100644 --- a/src/types.h +++ b/src/types.h @@ -305,6 +305,10 @@ enum EnclosingRule { NO_ENCLOSING, REVERSI, ATAXX, QUADWRANGLE, SNORT }; +enum WallingRule { + NO_WALLING, ARROW, DUCK, EDGE, PAST, STATIC +}; + enum OptBool { NO_VALUE, VALUE_FALSE, VALUE_TRUE }; diff --git a/src/variant.cpp b/src/variant.cpp index 2fc1c9c49..8a4246817 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -515,7 +515,7 @@ namespace { v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER; v->extinctionValue = -VALUE_MATE; v->extinctionPieceTypes = piece_set(COMMONER); - v->duckWalling = true; + v->wallingRule = DUCK; v->stalemateValue = VALUE_MATE; return v; } @@ -529,7 +529,7 @@ namespace { v->add_piece(CUSTOM_PIECE_1, 'p', "mK"); //move as a King, but can't capture v->startFen = "3p2/6/6/6/6/6/6/2P3 w - - 0 1"; v->stalemateValue = -VALUE_MATE; - v->staticWalling = true; + v->wallingRule = STATIC; v->wallingRegion[WHITE] = v->wallingRegion[BLACK] = AllSquares ^ make_bitboard(SQ_C1, SQ_D8); return v; } @@ -551,7 +551,7 @@ namespace { v->add_piece(CUSTOM_PIECE_1, 'p', "mK"); //move as a King, but can't capture v->startFen = "6p/7/7/7/7/7/P6 w - - 0 1"; v->stalemateValue = -VALUE_MATE; - v->pastWalling = true; + v->wallingRule = PAST; return v; } @@ -562,7 +562,7 @@ namespace { v->add_piece(CUSTOM_PIECE_1, 'n', "mN"); //move as a Knight, but can't capture v->startFen = "8/8/8/4n3/3N4/8/8/8 w - - 0 1"; v->stalemateValue = -VALUE_MATE; - v->pastWalling = true; + v->wallingRule = PAST; return v; } @@ -1668,7 +1668,7 @@ namespace { v->add_piece(CUSTOM_PIECE_1, 'q', "mQ"); v->startFen = "3q2q3/10/10/q8q/10/10/Q8Q/10/10/3Q2Q3 w - - 0 1"; v->stalemateValue = -VALUE_MATE; - v->arrowWalling = true; + v->wallingRule = ARROW; return v; } #endif diff --git a/src/variant.h b/src/variant.h index 2a0269582..cb2fe0514 100644 --- a/src/variant.h +++ b/src/variant.h @@ -107,10 +107,7 @@ struct Variant { int dropNoDoubledCount = 1; bool immobilityIllegal = false; bool gating = false; - bool arrowWalling = false; - bool duckWalling = false; - bool staticWalling = false; - bool pastWalling = false; + WallingRule wallingRule = NO_WALLING; Bitboard wallingRegion[COLOR_NB] = {AllSquares, AllSquares}; bool seirawanGating = false; bool cambodianMoves = false; diff --git a/src/variants.ini b/src/variants.ini index 8afb5561e..e3bdf0dad 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -131,6 +131,12 @@ # [CountingRule]: makruk, cambodian, or ASEAN counting rules [makruk, cambodian, asean, none] # [ChasingRule]: xiangqi chasing rules [axf, none] # [EnclosingRule]: reversi or ataxx enclosing rules [reversi, ataxx, quadwrangle, snort, none] +# [WallingRule]: wall-placing rule [arrow, duck, edge, past, static, none] +# - arrow: copies piece movement (ie. Game of the Amazons) +# - duck: mobile square (ie. Duck chess) +# - edge: edges of board, opening up new edges (ie. Atlantis) +# - past: previous square (ie. Snailtrail) +# - static: unchanging mask (ie. Isolation) ### Additional options relevant for usage in Winboard/XBoard # A few options only have the purpose of improving compatibility with Winboard/Xboard. @@ -213,12 +219,9 @@ # dropNoDoubledCount: specifies the count of already existing pieces for dropNoDoubled [int] (default: 1) # 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) -# arrowWalling: walling squares in Game of the Amazons style [bool] (default: false) -# duckWalling: walling square in Duck chess style [bool] (default: false) -# staticWalling: walling squares in Isolation style [bool] (default: false) +# wallingRule: rule on where wall can be placed [WallingRule] (default: none) # wallingRegionWhite: mask where wall squares (including duck) can be placed by white [Bitboard] (default: all squares) # wallingRegionBlack: mask where wall squares (including duck) can be placed by black [Bitboard] (default: all squares) -# pastWalling: walling of previous square in Snailtrail 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] @@ -1349,7 +1352,7 @@ pieceToCharTable = P...Q..AH..ECTDY....LKp...q..ah..ectdy....lk # Atomic + duck chess hybrid. # Playable as a custom variant in chess.com [atomicduck:atomic] -duckWalling = true +wallingRule = duck stalemateValue = win #https://www.chessvariants.com/diffmove.dir/checkers.html @@ -1769,3 +1772,10 @@ extinctionValue = loss extinctionPieceTypes = kq extinctionPseudoRoyal = true stalemateValue = loss + +#https://www.chessvariants.com/boardrules.dir/atlantis.html +[atlantis:chess] +wallingRule = edge +#not ready yet. Other wall variants are "move and wall", this is "move or wall". +#need to figure out way to do this ie. write code for: +#wallOrMove = true \ No newline at end of file