From: RainRat Date: Wed, 23 Aug 2023 12:02:05 +0000 (-0700) Subject: add Atomar. Optimize connect. (#695) X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=430c3128c9f0034803356e4ae46e5328452c4c9c;p=fairystockfish.git add Atomar. Optimize connect. (#695) --- diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 734d117..c6c7ef3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1280,22 +1280,8 @@ namespace { // Connect-n if (pos.connect_n() > 0) { - std::vector connect_directions; + for (const Direction& d : pos.getConnectDirections()) - if (pos.connect_horizontal()) - { - connect_directions.push_back(EAST); - } - if (pos.connect_vertical()) - { - connect_directions.push_back(NORTH); - } - if (pos.connect_diagonal()) - { - connect_directions.push_back(NORTH_EAST); - connect_directions.push_back(SOUTH_EAST); - } - for (Direction d : connect_directions) { // Find sufficiently large gaps Bitboard b = pos.board_bb() & ~pos.pieces(Them); diff --git a/src/parser.cpp b/src/parser.cpp index 7623e5a..9be7e00 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -357,6 +357,8 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("mandatoryPiecePromotion", v->mandatoryPiecePromotion); parse_attribute("pieceDemotion", v->pieceDemotion); parse_attribute("blastOnCapture", v->blastOnCapture); + parse_attribute("blastImmuneTypes", v->blastImmuneTypes, v->pieceToChar); + parse_attribute("mutuallyImmuneTypes", v->mutuallyImmuneTypes, v->pieceToChar); parse_attribute("petrifyOnCapture", v->petrifyOnCapture); parse_attribute("doubleStep", v->doubleStep); parse_attribute("doubleStepRegionWhite", v->doubleStepRegion[WHITE]); @@ -523,6 +525,7 @@ Variant* VariantParser::parse(Variant* v) { // Check for limitations if (v->pieceDrops && (v->arrowGating || v->duckGating || v->staticGating || v->pastGating)) std::cerr << "pieceDrops and arrowGating/duckGating are incompatible." << std::endl; + // Options incompatible with royal kings if (v->pieceTypes & KING) { @@ -546,6 +549,17 @@ Variant* VariantParser::parse(Variant* v) { std::cerr << piece_name(v->kingType) << " is not supported as kingType." << std::endl; } } + // Options incompatible with royal kings OR pseudo-royal kings. Possible in theory though: + // 1. In blast variants, moving a (pseudo-)royal blastImmuneType into another piece is legal. + // 2. In blast variants, capturing a piece next to a (pseudo-)royal blastImmuneType is legal. + // 3. Moving a (pseudo-)royal mutuallyImmuneType into a square threatened by the same type is legal. + if ((v->extinctionPseudoRoyal) || (v->pieceTypes & KING)) + { + if (v->blastImmuneTypes) + std::cerr << "Can not use kings or pseudo-royal with blastImmuneTypes." << std::endl; + if (v->mutuallyImmuneTypes) + std::cerr << "Can not use kings or pseudo-royal with mutuallyImmuneTypes." << std::endl; + } } return v; } diff --git a/src/position.cpp b/src/position.cpp index c1734e4..21ff18c 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1176,6 +1176,14 @@ bool Position::legal(Move m) const { if (var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) == KING) return false; + // mutuallyImmuneTypes (diplomacy in Atomar)-- In no-check Atomic, kings can be beside each other, but in Atomar, this prevents them from actually taking. + // Generalized to allow a custom set of pieces that can't capture a piece of the same type. + if (capture(m) && + (mutually_immune_types() & type_of(moved_piece(m))) && + (type_of(moved_piece(m)) == type_of(piece_on(to))) + ) + return false; + // En passant captures are a tricky special case. Because they are rather // uncommon, we do it simply by testing whether the king is attacked after // the move is made. @@ -1921,13 +1929,19 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { if (cambodian_moves() && type_of(pc) == ROOK && (square(them) & gates(them) & attacks_bb(to))) st->gatesBB[them] ^= square(them); + // Remove the blast pieces if (captured && (blast_on_capture() || var->petrifyOnCapture)) { std::memset(st->unpromotedBycatch, 0, sizeof(st->unpromotedBycatch)); st->demotedBycatch = st->promotedBycatch = 0; - Bitboard blast = blast_on_capture() ? (attacks_bb(to) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | to - : type_of(pc) != PAWN ? square_bb(to) : Bitboard(0); + Bitboard blastImmune = 0; + for (PieceSet ps = blast_immune_types(); ps;){ + PieceType pt = pop_lsb(ps); + blastImmune |= pieces(pt); + }; + Bitboard blast = blast_on_capture() ? ((attacks_bb(to) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | to) + & (pieces() ^ blastImmune) : type_of(pc) != PAWN ? square_bb(to) : Bitboard(0); while (blast) { Square bsq = pop_lsb(blast); @@ -2717,22 +2731,8 @@ bool Position::is_immediate_game_end(Value& result, int ply) const { if (connect_n() > 0) { Bitboard b; - std::vector connect_directions; - if (connect_horizontal()) - { - connect_directions.push_back(EAST); - } - if (connect_vertical()) - { - connect_directions.push_back(NORTH); - } - if (connect_diagonal()) - { - connect_directions.push_back(NORTH_EAST); - connect_directions.push_back(SOUTH_EAST); - } - for (Direction d : connect_directions) + for (Direction d : var->connect_directions) { b = pieces(~sideToMove); for (int i = 1; i < connect_n() && b; i++) diff --git a/src/position.h b/src/position.h index f0a5073..fcdd6ca 100644 --- a/src/position.h +++ b/src/position.h @@ -140,6 +140,8 @@ public: bool mandatory_piece_promotion() const; bool piece_demotion() const; bool blast_on_capture() const; + PieceSet blast_immune_types() const; + PieceSet mutually_immune_types() const; bool endgame_eval() const; Bitboard double_step_region(Color c) const; Bitboard triple_step_region(Color c) const; @@ -206,6 +208,7 @@ public: bool connect_horizontal() const; bool connect_vertical() const; bool connect_diagonal() const; + const std::vector& getConnectDirections() const; CheckCount checks_remaining(Color c) const; MaterialCounting material_counting() const; @@ -489,6 +492,16 @@ inline bool Position::blast_on_capture() const { return var->blastOnCapture; } +inline PieceSet Position::blast_immune_types() const { + assert(var != nullptr); + return var->blastImmuneTypes; +} + +inline PieceSet Position::mutually_immune_types() const { + assert(var != nullptr); + return var->mutuallyImmuneTypes; +} + inline bool Position::endgame_eval() const { assert(var != nullptr); return var->endgameEval && !count_in_hand(ALL_PIECES) && count() == 2; @@ -965,6 +978,10 @@ inline bool Position::connect_diagonal() const { return var->connectDiagonal; } +inline const std::vector& Position::getConnectDirections() const { + assert(var != nullptr); + return var->connect_directions; +} inline CheckCount Position::checks_remaining(Color c) const { return st->checksRemaining[c]; @@ -1391,18 +1408,18 @@ inline bool Position::allow_virtual_drop(Color c, PieceType pt) const { } inline Value Position::material_counting_result() const { - auto weigth_count = [this](PieceType pt, int v){ return v * (count(WHITE, pt) - count(BLACK, pt)); }; + auto weight_count = [this](PieceType pt, int v){ return v * (count(WHITE, pt) - count(BLACK, pt)); }; int materialCount; Value result; switch (var->materialCounting) { case JANGGI_MATERIAL: - materialCount = weigth_count(ROOK, 13) - + weigth_count(JANGGI_CANNON, 7) - + weigth_count(HORSE, 5) - + weigth_count(JANGGI_ELEPHANT, 3) - + weigth_count(WAZIR, 3) - + weigth_count(SOLDIER, 2) + materialCount = weight_count(ROOK, 13) + + weight_count(JANGGI_CANNON, 7) + + weight_count(HORSE, 5) + + weight_count(JANGGI_ELEPHANT, 3) + + weight_count(WAZIR, 3) + + weight_count(SOLDIER, 2) - 1; result = materialCount > 0 ? VALUE_MATE : -VALUE_MATE; break; diff --git a/src/variant.cpp b/src/variant.cpp index d477eb6..a0ce815 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -482,6 +482,15 @@ namespace { return v; } + // Atomar chess + // https://web.archive.org/web/20230519082613/https://chronatog.com/wp-content/uploads/2021/09/atomar-chess-rules.pdf + Variant* atomar_variant() { + Variant* v = nocheckatomic_variant()->init(); + v->blastImmuneTypes = piece_set(COMMONER); + v->mutuallyImmuneTypes = piece_set(COMMONER); + return v; + } + #ifdef ALLVARS // Duck chess Variant* duck_variant() { @@ -1802,6 +1811,7 @@ void VariantMap::init() { add("isolation7x7", isolation7x7_variant()); add("snailtrail", snailtrail_variant()); add("fox-and-hounds", fox_and_hounds_variant()); + add("atomar", atomar_variant()); #ifdef ALLVARS add("duck", duck_variant()); #endif @@ -2016,6 +2026,21 @@ Variant* Variant::conclude() { break; } + connect_directions.clear(); + if (connectHorizontal) + { + connect_directions.push_back(EAST); + } + if (connectVertical) + { + connect_directions.push_back(NORTH); + } + if (connectDiagonal) + { + connect_directions.push_back(NORTH_EAST); + connect_directions.push_back(SOUTH_EAST); + } + return this; } diff --git a/src/variant.h b/src/variant.h index bc4862d..77c3443 100644 --- a/src/variant.h +++ b/src/variant.h @@ -64,6 +64,8 @@ struct Variant { bool mandatoryPiecePromotion = false; bool pieceDemotion = false; bool blastOnCapture = false; + PieceSet blastImmuneTypes = NO_PIECE_SET; + PieceSet mutuallyImmuneTypes = NO_PIECE_SET; bool petrifyOnCapture = false; bool doubleStep = true; Bitboard doubleStepRegion[COLOR_NB] = {Rank2BB, Rank7BB}; @@ -167,6 +169,7 @@ struct Variant { int nnueMaxPieces; bool endgameEval = false; bool shogiStylePromotions = false; + std::vector connect_directions; void add_piece(PieceType pt, char c, std::string betza = "", char c2 = ' ') { // Avoid ambiguous definition by removing existing piece with same letter diff --git a/src/variants.ini b/src/variants.ini index 5b139d8..b4c3097 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -166,6 +166,8 @@ # mandatoryPiecePromotion: piece promotion (and demotion if enabled) is mandatory [bool] (default: false) # pieceDemotion: enable demotion of pieces (e.g., Kyoto shogi) [bool] (default: false) # blastOnCapture: captures explode all adjacent non-pawn pieces (e.g., atomic chess) [bool] (default: false) +# blastImmuneTypes: pieces completely immune to explosions (even at ground zero) [PieceSet] (default: none) +# mutuallyImmuneTypes: pieces that can't capture another piece of same types (e.g., kings (commoners) in atomar) [PieceSet] (default: none) # petrifyOnCapture: non-pawn pieces are turned into wall squares when capturing [bool] (default: false) # doubleStep: enable pawn double step [bool] (default: true) # doubleStepRegionWhite: region where pawn double steps are allowed for white [Bitboard] (default: *2)