From: Fabian Fichter Date: Thu, 19 Nov 2020 18:32:49 +0000 (+0100) Subject: Support Semi-Torpedo chess (#209) X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=14f6c9d9871bcfdb60c247dea2090c7805762881;p=fairystockfish.git Support Semi-Torpedo chess (#209) --- diff --git a/src/bitboard.h b/src/bitboard.h index 2a0542f..21a77e6 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -321,11 +321,11 @@ constexpr Bitboard forward_ranks_bb(Color c, Rank r) { } -/// promotion_zone_bb() returns a bitboard representing the squares on all the ranks +/// zone_bb() returns a bitboard representing the squares on all the ranks /// in front of and on the given relative rank, from the point of view of the given color. -/// For instance, promotion_zone_bb(BLACK, RANK_7) will return the 16 squares on ranks 1 and 2. +/// For instance, zone_bb(BLACK, RANK_7) will return the 16 squares on ranks 1 and 2. -inline Bitboard promotion_zone_bb(Color c, Rank r, Rank maxRank) { +inline Bitboard zone_bb(Color c, Rank r, Rank maxRank) { return forward_ranks_bb(c, relative_rank(c, r, maxRank)) | rank_bb(relative_rank(c, r, maxRank)); } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f62fcc1..0efda47 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -493,7 +493,7 @@ namespace { // Piece promotion bonus if (pos.promoted_piece_type(Pt) != NO_PIECE_TYPE) { - Bitboard zone = promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank()); + Bitboard zone = zone_bb(Us, pos.promotion_rank(), pos.max_rank()); if (zone & (b | s)) score += make_score(PieceValue[MG][pos.promoted_piece_type(Pt)] - PieceValue[MG][Pt], PieceValue[EG][pos.promoted_piece_type(Pt)] - PieceValue[EG][Pt]) / (zone & s && b ? 6 : 12); diff --git a/src/movegen.cpp b/src/movegen.cpp index 7d44606..3971fc5 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -107,12 +107,11 @@ namespace { const Square ksq = pos.count(Them) ? pos.square(Them) : SQ_NONE; Bitboard TRank8BB = pos.mandatory_pawn_promotion() ? rank_bb(relative_rank(Us, pos.promotion_rank(), pos.max_rank())) - : promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank()); + : zone_bb(Us, pos.promotion_rank(), pos.max_rank()); Bitboard TRank7BB = shift(TRank8BB); // Define squares a pawn can pass during a double step - Bitboard TRank3BB = rank_bb(relative_rank(Us, Rank(pos.double_step_rank() + 1), pos.max_rank())); - if (pos.first_rank_double_steps()) - TRank3BB |= rank_bb(relative_rank(Us, RANK_2, pos.max_rank())); + Bitboard TRank3BB = forward_ranks_bb(Us, relative_rank(Us, pos.double_step_rank_min(), pos.max_rank())) + & ~shift(forward_ranks_bb(Us, relative_rank(Us, pos.double_step_rank_max(), pos.max_rank()))); Bitboard emptySquares; @@ -242,7 +241,7 @@ namespace { if (pos.ep_square() != SQ_NONE) { - assert(rank_of(pos.ep_square()) == relative_rank(Them, Rank(pos.double_step_rank() + 1), pos.max_rank())); + assert(relative_rank(Them, rank_of(pos.ep_square()), pos.max_rank()) <= Rank(pos.double_step_rank_max() + 1)); // An en passant capture can be an evasion only if the checking piece // is the double pushed pawn and so is in the target. Otherwise this @@ -294,7 +293,7 @@ namespace { // Restrict target squares considering promotion zone if (b2 | b3) { - Bitboard promotion_zone = promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank()); + Bitboard promotion_zone = zone_bb(Us, pos.promotion_rank(), pos.max_rank()); if (pos.mandatory_piece_promotion()) b1 &= (promotion_zone & from ? Bitboard(0) : ~promotion_zone) | (pos.piece_promotion_on_capture() ? ~pos.pieces() : Bitboard(0)); // Exclude quiet promotions/demotions diff --git a/src/parser.cpp b/src/parser.cpp index 99e0106..9da29fd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -240,7 +240,8 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("endgameEval", v->endgameEval); parse_attribute("doubleStep", v->doubleStep); parse_attribute("doubleStepRank", v->doubleStepRank); - parse_attribute("firstRankDoubleSteps", v->firstRankDoubleSteps); + parse_attribute("doubleStepRankMin", v->doubleStepRankMin); + parse_attribute("enPassantRegion", v->enPassantRegion); parse_attribute("castling", v->castling); parse_attribute("castlingDroppedPiece", v->castlingDroppedPiece); parse_attribute("castlingKingsideFile", v->castlingKingsideFile); diff --git a/src/position.cpp b/src/position.cpp index c5bd02d..a39fc60 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1016,7 +1016,7 @@ bool Position::pseudo_legal(const Move m) const { // Handle the case where a mandatory piece promotion/demotion is not taken if ( mandatory_piece_promotion() && (is_promoted(from) ? piece_demotion() : promoted_piece_type(type_of(pc)) != NO_PIECE_TYPE) - && (promotion_zone_bb(us, promotion_rank(), max_rank()) & (SquareBB[from] | to)) + && (zone_bb(us, promotion_rank(), max_rank()) & (SquareBB[from] | to)) && (!piece_promotion_on_capture() || capture(m))) return false; @@ -1044,8 +1044,8 @@ bool Position::pseudo_legal(const Move m) const { if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture && !((from + pawn_push(us) == to) && empty(to)) // Not a single push && !( (from + 2 * pawn_push(us) == to) // Not a double push - && (rank_of(from) == relative_rank(us, double_step_rank(), max_rank()) - || (first_rank_double_steps() && rank_of(from) == relative_rank(us, RANK_1, max_rank()))) + && ( relative_rank(us, from, max_rank()) <= double_step_rank_max() + && relative_rank(us, from, max_rank()) >= double_step_rank_min()) && empty(to) && empty(to - pawn_push(us)) && double_step_enabled())) @@ -1271,7 +1271,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(pc == make_piece(us, PAWN)); assert(to == st->epSquare); - assert(relative_rank(~us, to, max_rank()) == Rank(double_step_rank() + 1)); + assert((var->enPassantRegion & to) + && relative_rank(~us, to, max_rank()) <= Rank(double_step_rank_max() + 1) + && relative_rank(~us, to, max_rank()) > double_step_rank_min()); assert(piece_on(to) == NO_PIECE); assert(piece_on(capsq) == make_piece(them, PAWN)); } @@ -1432,7 +1434,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { { // Set en-passant square if the moved pawn can be captured if ( std::abs(int(to) - int(from)) == 2 * NORTH - && relative_rank(us, rank_of(from), max_rank()) == double_step_rank() + && (var->enPassantRegion & (to - pawn_push(us))) && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))) { st->epSquare = to - pawn_push(us); @@ -1683,7 +1685,7 @@ void Position::undo_move(Move m) { assert(type_of(pc) == PAWN); assert(to == st->previous->epSquare); - assert(relative_rank(~us, to, max_rank()) == Rank(double_step_rank() + 1)); + assert(relative_rank(~us, to, max_rank()) <= Rank(double_step_rank_max() + 1)); assert(piece_on(capsq) == NO_PIECE); assert(st->capturedPiece == make_piece(~us, PAWN)); } @@ -2320,7 +2322,7 @@ bool Position::pos_is_ok() const { || (count(WHITE) && piece_on(square(WHITE)) != make_piece(WHITE, KING)) || (count(BLACK) && piece_on(square(BLACK)) != make_piece(BLACK, KING)) || ( ep_square() != SQ_NONE - && relative_rank(~sideToMove, ep_square(), max_rank()) != Rank(double_step_rank() + 1))) + && relative_rank(~sideToMove, ep_square(), max_rank()) > Rank(double_step_rank_max() + 1))) assert(0 && "pos_is_ok: Default"); if (Fast) diff --git a/src/position.h b/src/position.h index 49598a1..6e4dc48 100644 --- a/src/position.h +++ b/src/position.h @@ -122,8 +122,8 @@ public: bool piece_demotion() const; bool endgame_eval() const; bool double_step_enabled() const; - Rank double_step_rank() const; - bool first_rank_double_steps() const; + Rank double_step_rank_max() const; + Rank double_step_rank_min() const; bool castling_enabled() const; bool castling_dropped_piece() const; File castling_kingside_file() const; @@ -432,14 +432,14 @@ inline bool Position::double_step_enabled() const { return var->doubleStep; } -inline Rank Position::double_step_rank() const { +inline Rank Position::double_step_rank_max() const { assert(var != nullptr); return var->doubleStepRank; } -inline bool Position::first_rank_double_steps() const { +inline Rank Position::double_step_rank_min() const { assert(var != nullptr); - return var->firstRankDoubleSteps; + return var->doubleStepRankMin; } inline bool Position::castling_enabled() const { @@ -542,7 +542,7 @@ inline Bitboard Position::drop_region(Color c, PieceType pt) const { if (pt == PAWN) { if (!var->promotionZonePawnDrops) - b &= ~promotion_zone_bb(c, promotion_rank(), max_rank()); + b &= ~zone_bb(c, promotion_rank(), max_rank()); if (!first_rank_pawn_drops()) b &= ~rank_bb(relative_rank(c, RANK_1, max_rank())); } @@ -655,7 +655,7 @@ inline bool Position::pass_on_stalemate() const { inline Bitboard Position::promoted_soldiers(Color c) const { assert(var != nullptr); - return pieces(c, SOLDIER) & promotion_zone_bb(c, var->soldierPromotionRank, max_rank()); + return pieces(c, SOLDIER) & zone_bb(c, var->soldierPromotionRank, max_rank()); } inline bool Position::makpong() const { @@ -1005,8 +1005,9 @@ inline bool Position::pawn_passed(Color c, Square s) const { } inline bool Position::advanced_pawn_push(Move m) const { - return type_of(moved_piece(m)) == PAWN - && relative_rank(sideToMove, to_sq(m), max_rank()) > (max_rank() + 1) / 2; + return ( type_of(moved_piece(m)) == PAWN + && relative_rank(sideToMove, to_sq(m), max_rank()) > (max_rank() + 1) / 2) + || type_of(m) == ENPASSANT; } inline int Position::pawns_on_same_color_squares(Color c, Square s) const { diff --git a/src/variant.cpp b/src/variant.cpp index 6932d3e..3aed47c 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -278,10 +278,13 @@ namespace { v->extinctionPieceCount = 2; return v; } + // Horde chess + // https://en.wikipedia.org/wiki/Dunsany%27s_chess#Horde_chess Variant* horde_variant() { Variant* v = fairy_variant_base(); v->startFen = "rnbqkbnr/pppppppp/8/1PP2PP1/PPPPPPPP/PPPPPPPP/PPPPPPPP/PPPPPPPP w kq - 0 1"; - v->firstRankDoubleSteps = true; + v->doubleStepRankMin = RANK_1; + v->enPassantRegion = Rank3BB | Rank6BB; // exclude en passant on second rank v->extinctionValue = -VALUE_MATE; v->extinctionPieceTypes = {ALL_PIECES}; return v; @@ -608,7 +611,6 @@ namespace { v->add_piece(BREAKTHROUGH_PIECE, 'p'); v->startFen = "pppppppp/pppppppp/8/8/8/8/PPPPPPPP/PPPPPPPP w 0 1"; v->promotionPieceTypes = {}; - v->firstRankDoubleSteps = false; v->doubleStep = false; v->castling = false; v->stalemateValue = -VALUE_MATE; @@ -821,6 +823,7 @@ namespace { v->mandatoryPawnPromotion = false; v->immobilityIllegal = true; v->doubleStepRank = RANK_3; + v->doubleStepRankMin = RANK_3; v->castling = false; return v; } @@ -838,6 +841,7 @@ namespace { v->castlingQueensideFile = FILE_D; v->castlingRank = RANK_2; v->doubleStepRank = RANK_3; + v->doubleStepRankMin = RANK_3; return v; } Variant* clobber10_variant() { diff --git a/src/variant.h b/src/variant.h index 515a44f..078063b 100644 --- a/src/variant.h +++ b/src/variant.h @@ -57,7 +57,8 @@ struct Variant { bool endgameEval = false; bool doubleStep = true; Rank doubleStepRank = RANK_2; - bool firstRankDoubleSteps = false; + Rank doubleStepRankMin = RANK_2; + Bitboard enPassantRegion = AllSquares; bool castling = true; bool castlingDroppedPiece = false; File castlingKingsideFile = FILE_G; diff --git a/src/variants.ini b/src/variants.ini index 2292130..5fd8c11 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -122,7 +122,8 @@ # endgameEval: enable special endgame evaluation (for very chess-like variants only) [bool] (default: false) # doubleStep: enable pawn double step [bool] (default: true) # doubleStepRank: relative rank from where pawn double steps are allowed [Rank] (default: 2) -# firstRankDoubleSteps: enable pawn double steps from first rank [bool] (default: false) +# doubleStepRankMin: earlist relative rank from where pawn double steps are allowed [Rank] (default: 2) +# enPassantRegion: define region (target squares) where en passant is allowed after double steps [Bitboard] # castling: enable castling [bool] (default: true) # castlingDroppedPiece: enable castling with dropped rooks/kings [bool] (default: false) # castlingKingsideFile: destination file of king after kingside castling [File] (default: g) @@ -294,6 +295,9 @@ startFen = 1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1 [weak:chess] startFen = nnnnknnn/pppppppp/2p2p2/1pppppp1/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1 +[semitorpedo:chess] +doubleStepRank = 3 + # This variant is similar to Capablanca Chess, but with two archbishops and no chancellor piece. [gemini:janus] startFen = rnbaqkabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBAQKABNR w KQkq - 0 1 diff --git a/tests/perft.sh b/tests/perft.sh index faf594f..261e565 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -54,7 +54,8 @@ if [[ $1 == "" || $1 == "variant" ]]; then expect perft.exp euroshogi startpos 5 9451149 > /dev/null expect perft.exp minishogi startpos 5 533203 > /dev/null expect perft.exp kyotoshogi startpos 5 225903 > /dev/null - expect perft.exp horde startpos 6 5396554 > /dev/null + expect perft.exp horde startpos 5 265223 > /dev/null + expect perft.exp horde "fen 4k3/7r/8/P7/2p1n2P/3p2P1/1P3P2/PPP1PPP1 w - - 0 1" 4 128809 > /dev/null expect perft.exp placement startpos 4 1597696 > /dev/null expect perft.exp sittuyin startpos 3 580096 > /dev/null expect perft.exp sittuyin "fen 8/8/6R1/s3r3/P5R1/1KP3p1/1F2kr2/8[] b - - 0 72" 4 652686 > /dev/null