diff --git a/src/bitboard.cpp b/src/bitboard.cpp index b4671f12..3939296c 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -64,7 +64,7 @@ namespace { // Some magics need to be split in order to reduce memory consumption. // Otherwise on a 12x10 board they can be >100 MB. #ifdef LARGEBOARDS - Bitboard RookTableH[0x11800]; // To store horizontalrook attacks + Bitboard RookTableH[0x11800]; // To store horizontal rook attacks Bitboard RookTableV[0x4800]; // To store vertical rook attacks Bitboard BishopTable[0x33C00]; // To store bishop attacks Bitboard CannonTableH[0x11800]; // To store horizontal cannon attacks @@ -220,7 +220,7 @@ std::string Bitboards::pretty(Bitboard b) { s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+---+---+---+---+\n"; } - s += " a b c d e f g h i j k\n"; + s += " a b c d e f g h i j k l\n"; return s; } diff --git a/src/parser.cpp b/src/parser.cpp index 078c3387..4871446b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -106,8 +106,9 @@ namespace { target = value == "reversi" ? REVERSI : value == "ataxx" ? ATAXX : value == "quadwrangle" ? QUADWRANGLE + : value == "snort" ? SNORT : NO_ENCLOSING; - return value == "reversi" || value == "ataxx" || value == "quadwrangle" || value == "none"; + return value == "reversi" || value == "ataxx" || value == "quadwrangle" || value =="snort" || value == "none"; } template <> bool set(const std::string& value, Bitboard& target) { @@ -382,7 +383,7 @@ Variant* VariantParser::parse(Variant* v) { 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("petrifyOnCaptureTypes", v->petrifyOnCaptureTypes, v->pieceToChar); parse_attribute("petrifyBlastPieces", v->petrifyBlastPieces); parse_attribute("doubleStep", v->doubleStep); parse_attribute("doubleStepRegionWhite", v->doubleStepRegion[WHITE]); diff --git a/src/position.cpp b/src/position.cpp index a55d50b9..70912854 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1129,6 +1129,9 @@ bool Position::legal(Move m) const { occupied &= ~square_bb(capture_square(kto)); if (capture(m) && blast_on_capture()) occupied &= ~((attacks_bb(kto) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | kto); + // Petrifying a pseudo-royal piece is illegal + if (capture(m) && (var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && (st->pseudoRoyals & from)) + return false; Bitboard pseudoRoyals = st->pseudoRoyals & pieces(sideToMove); Bitboard pseudoRoyalsTheirs = st->pseudoRoyals & pieces(~sideToMove); if (is_ok(from) && (pseudoRoyals & from)) @@ -1145,6 +1148,10 @@ bool Position::legal(Move m) const { // Self-explosions are illegal if (pseudoRoyals & ~occupied) return false; + // Petrifiable pseudo-royals can't capture + Bitboard attackerCandidatesTheirs = occupied & ~square_bb(kto); + for (PieceSet ps = var->petrifyOnCaptureTypes & extinction_piece_types(); ps;) + attackerCandidatesTheirs &= ~pieces(~us, pop_lsb(ps)); // Check for legality unless we capture a pseudo-royal piece if (!(pseudoRoyalsTheirs & ~occupied)) while (pseudoRoyals) @@ -1152,7 +1159,7 @@ bool Position::legal(Move m) const { Square sr = pop_lsb(pseudoRoyals); // Touching pseudo-royal pieces are immune if ( !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb(sr))) - && (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto)))) + && (attackers_to(sr, occupied, ~us) & attackerCandidatesTheirs)) return false; } // Look for duple check @@ -1169,7 +1176,7 @@ bool Position::legal(Move m) const { Square sr = pop_lsb(pseudoRoyalCandidates); // Touching pseudo-royal pieces are immune if (!( !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb(sr))) - && (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto))))) + && (attackers_to(sr, occupied, ~us) & attackerCandidatesTheirs))) allCheck = false; } if (allCheck) @@ -1177,10 +1184,6 @@ bool Position::legal(Move m) const { } } - // Petrifying the king is illegal - 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) && @@ -1405,7 +1408,7 @@ bool Position::gives_check(Move m) const { // Is there a direct check? if (type_of(m) != PROMOTION && type_of(m) != PIECE_PROMOTION && type_of(m) != PIECE_DEMOTION && type_of(m) != CASTLING - && !(var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) != PAWN)) + && !((var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && capture(m))) { PieceType pt = type_of(moved_piece(m)); if (pt == JANGGI_CANNON) @@ -1433,7 +1436,7 @@ bool Position::gives_check(Move m) const { return true; // Petrified piece can't give check - if (var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) != PAWN) + if ((var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && capture(m)) return false; // Is there a check by special diagonal moves? @@ -1946,7 +1949,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Remove the blast pieces - if (captured && (blast_on_capture() || var->petrifyOnCapture)) + if (captured && (blast_on_capture() || var->petrifyOnCaptureTypes)) { std::memset(st->unpromotedBycatch, 0, sizeof(st->unpromotedBycatch)); st->demotedBycatch = st->promotedBycatch = 0; @@ -1956,7 +1959,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { 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); + & (pieces() ^ blastImmune) : var->petrifyOnCaptureTypes & type_of(pc) ? square_bb(to) : Bitboard(0); while (blast) { Square bsq = pop_lsb(blast); @@ -2018,7 +2021,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } // Make a wall square where the piece was - if (bsq == to ? var->petrifyOnCapture : var->petrifyBlastPieces) + if (bsq == to ? bool(var->petrifyOnCaptureTypes & type_of(bpc)) : var->petrifyBlastPieces) { st->wallSquares |= bsq; byTypeBB[ALL_PIECES] |= bsq; @@ -2116,7 +2119,7 @@ void Position::undo_move(Move m) { byTypeBB[ALL_PIECES] ^= st->wallSquares ^ st->previous->wallSquares; // Add the blast pieces - if (st->capturedPiece && (blast_on_capture() || var->petrifyOnCapture)) + if (st->capturedPiece && (blast_on_capture() || var->petrifyOnCaptureTypes)) { Bitboard blast = attacks_bb(to) | to; while (blast) @@ -2435,6 +2438,10 @@ bool Position::see_ge(Move m, Value threshold) const { if (swap <= 0) return true; + // Petrification ends SEE + if (var->petrifyOnCaptureTypes & type_of(moved_piece(m)) && capture(m)) + return false; + Bitboard occupied = (type_of(m) != DROP ? pieces() ^ from : pieces()) ^ to; Color stm = color_of(moved_piece(m)); Bitboard attackers = attackers_to(to, occupied); diff --git a/src/position.h b/src/position.h index 22791350..34b09763 100644 --- a/src/position.h +++ b/src/position.h @@ -708,6 +708,12 @@ inline Bitboard Position::drop_region(Color c, PieceType pt) const { b ^= s; } } + else if (enclosing_drop() == SNORT) + { + Bitboard theirs = pieces(~c); + b &= ~(shift(theirs) | shift(theirs) + | shift(theirs) | shift(theirs)); + } else { assert(enclosing_drop() == ATAXX); diff --git a/src/types.h b/src/types.h index a59f8678..99c5e1aa 100644 --- a/src/types.h +++ b/src/types.h @@ -302,7 +302,7 @@ enum ChasingRule { }; enum EnclosingRule { - NO_ENCLOSING, REVERSI, ATAXX, QUADWRANGLE + NO_ENCLOSING, REVERSI, ATAXX, QUADWRANGLE, SNORT }; enum OptBool { diff --git a/src/variant.cpp b/src/variant.cpp index e25d7ba0..2fc1c9c4 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -460,6 +460,20 @@ namespace { v->extinctionPieceTypes = piece_set(ALL_PIECES); return v; } + // Petrified + // Sideways pawns + petrification on capture + // https://www.chess.com/variants/petrified + Variant* petrified_variant() { + Variant* v = pawnsideways_variant()->init(); + v->remove_piece(KING); + v->add_piece(COMMONER, 'k'); + v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER; + v->extinctionValue = -VALUE_MATE; + v->extinctionPieceTypes = piece_set(COMMONER); + v->extinctionPseudoRoyal = true; + v->petrifyOnCaptureTypes = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT; + return v; + } // Atomic chess without checks (ICC rules) // https://www.chessclub.com/help/atomic Variant* nocheckatomic_variant() { @@ -493,6 +507,7 @@ namespace { #ifdef ALLVARS // Duck chess + // https://duckchess.com/ Variant* duck_variant() { Variant* v = chess_variant_base()->init(); v->remove_piece(KING); @@ -1805,6 +1820,7 @@ void VariantMap::init() { add("kinglet", kinglet_variant()); add("threekings", threekings_variant()); add("horde", horde_variant()); + add("petrified", petrified_variant()); add("nocheckatomic", nocheckatomic_variant()); add("atomic", atomic_variant()); add("atomar", atomar_variant()); @@ -2013,6 +2029,7 @@ Variant* Variant::conclude() { && !makpongRule && !connectN && !blastOnCapture + && !petrifyOnCaptureTypes && !capturesToHand && !twoBoards && !restrictedMobility diff --git a/src/variant.h b/src/variant.h index dd6036a3..2a026958 100644 --- a/src/variant.h +++ b/src/variant.h @@ -66,7 +66,7 @@ struct Variant { bool blastOnCapture = false; PieceSet blastImmuneTypes = NO_PIECE_SET; PieceSet mutuallyImmuneTypes = NO_PIECE_SET; - bool petrifyOnCapture = false; + PieceSet petrifyOnCaptureTypes = NO_PIECE_SET; bool petrifyBlastPieces = false; bool doubleStep = true; Bitboard doubleStepRegion[COLOR_NB] = {Rank2BB, Rank7BB}; diff --git a/src/variants.ini b/src/variants.ini index 7d0b36ff..8afb5561 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -130,7 +130,7 @@ # [MaterialCounting]: material counting rules for adjudication [janggi, unweighted, whitedrawodds, blackdrawodds, none] # [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, none] +# [EnclosingRule]: reversi or ataxx enclosing rules [reversi, ataxx, quadwrangle, snort, none] ### Additional options relevant for usage in Winboard/XBoard # A few options only have the purpose of improving compatibility with Winboard/Xboard. @@ -169,7 +169,7 @@ # 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) +# petrifyOnCaptureTypes: defined pieces are turned into wall squares when capturing [PieceSet] (default: -) # petrifyBlastPieces: if petrify and blast combined, should pieces destroyed in the blast be petrified? [bool] (default: false) # doubleStep: enable pawn double step [bool] (default: true) # doubleStepRegionWhite: region where pawn double steps are allowed for white [Bitboard] (default: *2) @@ -1656,7 +1656,7 @@ startFen = lnsgkgsnl/1rci1uab1/p1p1p1p1p/9/9/9/P1P1P1P1P/1BAU1ICR1/LNSGKGSNL[-] pawn = - customPiece1 = p:fmWfceFifmnD pawnTypes = p -petrifyOnCapture = true +petrifyOnCaptureTypes = pnbrq enPassantRegion = - #https://en.wikipedia.org/wiki/Nim @@ -1704,6 +1704,18 @@ flipEnclosedPieces = ataxx pieceDrops = false passOnStalemate = false +#https://www.ludii.games/details.php?keyword=Snort +#also known as Cats & Dogs +[snort:ataxx] +immobile = p +startFen = 8/8/8/8/8/8/8/8 +enclosingDrop = snort +flipEnclosedPieces = none +maxRank = 8 +maxFile = 8 +passOnStalemate = false +stalematePieceCount = false + #https://www.chess.com/variants/blackletter-chess [blackletter:chess] pieceToCharTable = PNBRQ.......E...M...HKpnbrq.......e...m...hk @@ -1727,3 +1739,33 @@ castlingWins = q [opposite-castling:chess] oppositeCastling = true + +# A Kyoto Shogi variant with a left/right theme. +[gethenian] +maxRank = 7 +maxFile = 7 +king = - +customPiece1 = k:K +customPiece2 = q:mW +customPiece3 = b:lfrbB +customPiece4 = i:rflbB +customPiece5 = r:lrR +customPiece6 = n:hlN +customPiece7 = t:hrN +customPiece8 = m:WfF +customPiece9 = s:FfW +startFen = 2ikb2/2mnm2/7/7/7/2MNM2/2B+KI2[] w - - 0 1 +promotionPieceTypes = - +promotedPieceType = k:q b:r i:r n:t m:s +promotionRegionWhite = *1 *2 *3 *4 *5 *6 *7 +promotionRegionBlack = *7 *6 *5 *4 *3 *2 *1 +mandatoryPiecePromotion = true +pieceDemotion = true +pieceDrops = true +capturesToHand = true +dropPromoted = true +immobilityIllegal = false +extinctionValue = loss +extinctionPieceTypes = kq +extinctionPseudoRoyal = true +stalemateValue = loss