From: Fabian Fichter Date: Sun, 4 Nov 2018 11:32:22 +0000 (+0100) Subject: Support placement chess (close #10) X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=99e11ae96b3307744de31f1bca5395d945f4403c;p=fairystockfish.git Support placement chess (close #10) In order to support placement chess, new options are added: - mandatory piece drops - limiting drops to certain squares - castling with dropped pieces - bishop drops on opposite colors Furthermore, support slash as separator for pieces in hand in FENs of drop games for compatibility with FENs from pychess and lichess. --- diff --git a/src/movegen.cpp b/src/movegen.cpp index 6bbdb66..aa1ec14 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -291,8 +291,8 @@ namespace { moveList = generate_moves(pos, moveList, Us, pt, target); // generate drops if (pos.piece_drops() && Type != CAPTURES && pos.count_in_hand(Us, ALL_PIECES)) - for (PieceType pt = PAWN; pt < KING; ++pt) - moveList = generate_drops(pos, moveList, pt, target & ~pos.pieces(~Us)); + for (PieceType pt = PAWN; pt <= KING; ++pt) + moveList = generate_drops(pos, moveList, pt, target & ~pos.pieces(~Us) & pos.drop_region(Us)); if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count(Us)) { diff --git a/src/position.cpp b/src/position.cpp index 7c992f7..e54a9f1 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -298,7 +298,11 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, } else if (token == '/') + { sq += 2 * SOUTH + (FILE_MAX - max_file()) * EAST; + if (!is_ok(sq)) + break; + } else if ((idx = piece_to_char().find(token)) != string::npos) { @@ -602,7 +606,7 @@ const string Position::fen() const { { ss << '['; for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PieceType(KING - 1); pt >= PAWN; --pt) + for (PieceType pt = KING; pt >= PAWN; --pt) ss << std::string(pieceCountInHand[c][pt], piece_to_char()[make_piece(c, pt)]); ss << ']'; } @@ -724,14 +728,44 @@ bool Position::legal(Move m) const { } } + // illegal non-drop moves + if (must_drop() && type_of(m) != DROP && count_in_hand(us, ALL_PIECES)) + { + if (checkers()) + { + for (const auto& mevasion : MoveList(*this)) + if (type_of(mevasion) == DROP && legal(mevasion)) + return false; + } + else + { + for (const auto& mquiet : MoveList(*this)) + if (type_of(mquiet) == DROP && legal(mquiet)) + return false; + } + } + + // illegal drop move + if (drop_opposite_colored_bishop() && type_of(m) == DROP) + { + if (type_of(moved_piece(m)) != BISHOP) + { + Bitboard remaining = drop_region(us) & ~pieces() & ~SquareBB[to]; + // Are enough squares available to drop bishops on opposite colors? + if ( (!( DarkSquares & pieces(us, BISHOP)) && ( DarkSquares & remaining)) + + (!(~DarkSquares & pieces(us, BISHOP)) && (~DarkSquares & remaining)) < count_in_hand(us, BISHOP)) + return false; + } + else + // Drop resulting in same-colored bishops + if ((DarkSquares & to ? DarkSquares : ~DarkSquares) & pieces(us, BISHOP)) + return false; + } + // no legal moves from target square if (immobility_illegal() && (type_of(m) == DROP || type_of(m) == NORMAL) && !(moves_bb(us, type_of(moved_piece(m)), to, 0) & board_bb())) return false; - // illegal drops - if (piece_drops() && type_of(m) == DROP) - return pieceCountInHand[us][type_of(moved_piece(m))] && empty(to_sq(m)); - // game end if (is_variant_end()) return false; @@ -1041,6 +1075,24 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->materialKey ^= Zobrist::psq[pc][pieceCount[pc]-1]; if (type_of(pc) != PAWN) st->nonPawnMaterial[us] += PieceValue[MG][pc]; + // Set castling rights for dropped king or rook + if (castling_dropped_piece() && relative_rank(us, to, max_rank()) == RANK_1) + { + if (type_of(pc) == KING && file_of(to) == FILE_E) + { + Bitboard castling_rooks = pieces(us, ROOK) + & rank_bb(relative_rank(us, RANK_1, max_rank())) + & (file_bb(FILE_A) | file_bb(max_file())); + while (castling_rooks) + set_castling_right(us, pop_lsb(&castling_rooks)); + } + else if (type_of(pc) == ROOK) + { + if ( (file_of(to) == FILE_A || file_of(to) == max_file()) + && piece_on(make_square(FILE_E, relative_rank(us, RANK_1, max_rank()))) == make_piece(us, KING)) + set_castling_right(us, to); + } + } } else if (type_of(m) != CASTLING) move_piece(pc, from, to); diff --git a/src/position.h b/src/position.h index ea06a38..7ecd822 100644 --- a/src/position.h +++ b/src/position.h @@ -103,15 +103,19 @@ public: bool double_step_enabled() const; bool first_rank_double_steps() const; bool castling_enabled() const; + bool castling_dropped_piece() const; File castling_kingside_file() const; File castling_queenside_file() const; bool checking_permitted() const; bool must_capture() const; + bool must_drop() const; bool piece_drops() const; bool drop_loop() const; bool captures_to_hand() const; bool first_rank_drops() const; bool drop_on_top() const; + Bitboard drop_region(Color c) const; + bool drop_opposite_colored_bishop() const; bool immobility_illegal() const; // winning conditions Value stalemate_value(int ply = 0) const; @@ -329,6 +333,11 @@ inline bool Position::castling_enabled() const { return var->castling; } +inline bool Position::castling_dropped_piece() const { + assert(var != nullptr); + return var->castlingDroppedPiece; +} + inline File Position::castling_kingside_file() const { assert(var != nullptr); return var->castlingKingsideFile; @@ -349,6 +358,11 @@ inline bool Position::must_capture() const { return var->mustCapture; } +inline bool Position::must_drop() const { + assert(var != nullptr); + return var->mustDrop; +} + inline bool Position::piece_drops() const { assert(var != nullptr); return var->pieceDrops; @@ -374,6 +388,16 @@ inline bool Position::drop_on_top() const { return var->dropOnTop; } +inline Bitboard Position::drop_region(Color c) const { + assert(var != nullptr); + return c == WHITE ? var->whiteDropRegion : var->blackDropRegion; +} + +inline bool Position::drop_opposite_colored_bishop() const { + assert(var != nullptr); + return var->dropOppositeColoredBishop; +} + inline bool Position::immobility_illegal() const { assert(var != nullptr); return var->immobilityIllegal; diff --git a/src/variant.cpp b/src/variant.cpp index 88cdf4d..aeb0432 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -210,6 +210,18 @@ VariantMap variants; // Global object v->capturesToHand = false; return v; } + Variant* placement_variant() { + Variant* v = chess_variant(); + v->startFen = "8/pppppppp/8/8/8/8/PPPPPPPP/8[KQRRBBNNkqrrbbnn] w - - 0 1"; + v->mustDrop = true; + v->pieceDrops = true; + v->capturesToHand = false; + v->whiteDropRegion = Rank1BB; + v->blackDropRegion = Rank8BB; + v->dropOppositeColoredBishop = true; + v->castlingDroppedPiece = true; + return v; + } Variant* euroshogi_variant() { Variant* v = fairy_variant_base(); v->reset_pieces(); @@ -528,6 +540,7 @@ void VariantMap::init() { add("loop", loop_variant()); add("chessgi", chessgi_variant()); add("pocketknight", pocketknight_variant()); + add("placement", placement_variant()); add("euroshogi", euroshogi_variant()); add("judkinshogi", judkinsshogi_variant()); add("minishogi", minishogi_variant()); diff --git a/src/variant.h b/src/variant.h index eda559c..f197460 100644 --- a/src/variant.h +++ b/src/variant.h @@ -46,15 +46,20 @@ struct Variant { bool doubleStep = true; bool firstRankDoubleSteps = false; bool castling = true; + bool castlingDroppedPiece = false; File castlingKingsideFile = FILE_G; File castlingQueensideFile = FILE_C; bool checking = true; bool mustCapture = false; + bool mustDrop = false; bool pieceDrops = false; bool dropLoop = false; bool capturesToHand = false; bool firstRankDrops = false; bool dropOnTop = false; + Bitboard whiteDropRegion = AllSquares; + Bitboard blackDropRegion = AllSquares; + bool dropOppositeColoredBishop = false; bool immobilityIllegal = false; // game end Value stalemateValue = VALUE_DRAW; diff --git a/tests/perft.sh b/tests/perft.sh index 981f2cc..e84f28c 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -47,6 +47,7 @@ expect perft.exp ai-wok startpos 5 13275068 > /dev/null expect perft.exp euroshogi startpos 5 9451149 > /dev/null expect perft.exp minishogi startpos 5 533203 > /dev/null expect perft.exp horde startpos 6 5396554 > /dev/null +expect perft.exp placement startpos 4 1597696 > /dev/null rm perft.exp