From: Fabian Fichter Date: Fri, 3 Jul 2020 10:58:13 +0000 (+0200) Subject: Support Othello/Reversi X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=79993e4f8159a22a89e9d34c7dab9cdd664490d4;p=fairystockfish.git Support Othello/Reversi --- diff --git a/src/movegen.cpp b/src/movegen.cpp index 129d596..a271a57 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -342,7 +342,7 @@ namespace { moveList = make_move_and_gating(pos, moveList, Us, ksq, pop_lsb(&b)); // Passing move by king - if (pos.king_pass()) + if (pos.pass()) *moveList++ = make(ksq, ksq); if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO))) @@ -354,6 +354,9 @@ namespace { moveList = make_move_and_gating(pos, moveList, Us, ksq, pos.castling_rook_square(OOO)); } } + // Workaround for passing: Execute a non-move with any piece + else if (pos.pass() && !pos.count(Us) && pos.pieces(Us)) + *moveList++ = make(lsb(pos.pieces(Us)), lsb(pos.pieces(Us))); // Castling with non-king piece if (!pos.count(Us) && Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO))) @@ -468,7 +471,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { Bitboard sliders = pos.checkers(); // Passing move by king in bikjang - if (pos.bikjang() && pos.king_pass()) + if (pos.bikjang() && pos.pass()) *moveList++ = make(ksq, ksq); // Consider all evasion moves for special pieces diff --git a/src/parser.cpp b/src/parser.cpp index d89f25c..dca83f8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -252,6 +252,8 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("firstRankPawnDrops", v->firstRankPawnDrops); parse_attribute("promotionZonePawnDrops", v->promotionZonePawnDrops); parse_attribute("dropOnTop", v->dropOnTop); + parse_attribute("enclosingDrop", v->enclosingDrop); + parse_attribute("enclosingDropStart", v->enclosingDropStart); parse_attribute("whiteDropRegion", v->whiteDropRegion); parse_attribute("blackDropRegion", v->blackDropRegion); parse_attribute("sittuyinRookDrop", v->sittuyinRookDrop); @@ -263,11 +265,12 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("seirawanGating", v->seirawanGating); parse_attribute("cambodianMoves", v->cambodianMoves); parse_attribute("diagonalLines", v->diagonalLines); - parse_attribute("kingPass", v->kingPass); - parse_attribute("kingPassOnStalemate", v->kingPassOnStalemate); + parse_attribute("pass", v->pass); + parse_attribute("passOnStalemate", v->passOnStalemate); parse_attribute("makpongRule", v->makpongRule); parse_attribute("flyingGeneral", v->flyingGeneral); parse_attribute("soldierPromotionRank", v->soldierPromotionRank); + parse_attribute("flipEnclosedPieces", v->flipEnclosedPieces); // game end parse_attribute("nMoveRule", v->nMoveRule); parse_attribute("nFoldRule", v->nFoldRule); @@ -275,6 +278,7 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("nFoldValueAbsolute", v->nFoldValueAbsolute); parse_attribute("perpetualCheckIllegal", v->perpetualCheckIllegal); parse_attribute("stalemateValue", v->stalemateValue); + parse_attribute("stalematePieceCount", v->stalematePieceCount); parse_attribute("checkmateValue", v->checkmateValue); parse_attribute("shogiPawnDropMateIllegal", v->shogiPawnDropMateIllegal); parse_attribute("shatarMateRule", v->shatarMateRule); diff --git a/src/position.cpp b/src/position.cpp index d6a2daf..db17d95 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -893,7 +893,7 @@ bool Position::legal(Move m) const { return false; // Illegal king passing move - if (king_pass_on_stalemate() && is_pass(m) && !checkers()) + if (pass_on_stalemate() && is_pass(m) && !checkers()) { for (const auto& move : MoveList(*this)) if (!is_pass(move) && legal(move)) @@ -1213,7 +1213,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); if (to == from) { - assert((type_of(m) == PROMOTION && sittuyin_promotion()) || (is_pass(m) && king_pass())); + assert((type_of(m) == PROMOTION && sittuyin_promotion()) || (is_pass(m) && pass())); captured = NO_PIECE; } st->capturedpromoted = is_promoted(to); @@ -1339,6 +1339,35 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { set_castling_right(us, to); } } + // Flip enclosed pieces + if (flip_enclosed_pieces()) + { + st->flippedPieces = 0; + // Find end of rows to be flipped + Bitboard b = attacks_bb(us, QUEEN, to, board_bb() & ~pieces(~us)) & ~PseudoAttacks[us][KING][to] & pieces(us); + while(b) + st->flippedPieces |= between_bb(to, pop_lsb(&b)); + // Flip pieces + Bitboard to_flip = st->flippedPieces; + while(to_flip) + { + Square s = pop_lsb(&to_flip); + Piece flipped = piece_on(s); + Piece resulting = ~flipped; + + // remove opponent's piece + remove_piece(s); + k ^= Zobrist::psq[flipped][s]; + st->materialKey ^= Zobrist::psq[flipped][pieceCount[flipped]]; + st->nonPawnMaterial[them] -= PieceValue[MG][flipped]; + + // add our piece + put_piece(resulting, s); + k ^= Zobrist::psq[resulting][s]; + st->materialKey ^= Zobrist::psq[resulting][pieceCount[resulting]-1]; + st->nonPawnMaterial[us] += PieceValue[MG][resulting]; + } + } } else if (type_of(m) != CASTLING) move_piece(from, to); @@ -1498,7 +1527,7 @@ void Position::undo_move(Move m) { assert(type_of(m) == DROP || empty(from) || type_of(m) == CASTLING || is_gating(m) || (type_of(m) == PROMOTION && sittuyin_promotion()) - || (is_pass(m) && king_pass())); + || (is_pass(m) && pass())); assert(type_of(st->capturedPiece) != KING); // Remove gated piece @@ -1543,7 +1572,21 @@ void Position::undo_move(Move m) { else { if (type_of(m) == DROP) + { + if (flip_enclosed_pieces()) + { + // Flip pieces + Bitboard to_flip = st->flippedPieces; + while(to_flip) + { + Square s = pop_lsb(&to_flip); + Piece resulting = ~piece_on(s); + remove_piece(s); + put_piece(resulting, s); + } + } undrop_piece(make_piece(us, in_hand_piece_type(m)), to); // Remove the dropped piece + } else move_piece(to, from); // Put the piece back at the source square diff --git a/src/position.h b/src/position.h index a0ca7cb..4446b08 100644 --- a/src/position.h +++ b/src/position.h @@ -60,6 +60,7 @@ struct StateInfo { Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; + Bitboard flippedPieces; bool capturedpromoted; bool shak; bool bikjang; @@ -132,6 +133,7 @@ public: bool captures_to_hand() const; bool first_rank_pawn_drops() const; bool drop_on_top() const; + bool enclosing_drop() const; Bitboard drop_region(Color c) const; Bitboard drop_region(Color c, PieceType pt) const; bool sittuyin_rook_drop() const; @@ -143,10 +145,11 @@ public: bool seirawan_gating() const; bool cambodian_moves() const; Bitboard diagonal_lines() const; - bool king_pass() const; - bool king_pass_on_stalemate() const; + bool pass() const; + bool pass_on_stalemate() const; Bitboard promoted_soldiers(Color c) const; bool makpong() const; + bool flip_enclosed_pieces() const; // winning conditions int n_move_rule() const; int n_fold_rule() const; @@ -508,6 +511,11 @@ inline bool Position::drop_on_top() const { return var->dropOnTop; } +inline bool Position::enclosing_drop() const { + assert(var != nullptr); + return var->enclosingDrop; +} + inline Bitboard Position::drop_region(Color c) const { assert(var != nullptr); return c == WHITE ? var->whiteDropRegion : var->blackDropRegion; @@ -536,6 +544,29 @@ inline Bitboard Position::drop_region(Color c, PieceType pt) const { if (pt == ROOK && sittuyin_rook_drop()) b &= rank_bb(relative_rank(c, RANK_1, max_rank())); + // Filter out squares where the drop does not enclose at least one opponent's piece + if (enclosing_drop()) + { + // Reversi start + if (var->enclosingDropStart & ~pieces()) + b &= var->enclosingDropStart; + else + { + Bitboard theirs = pieces(~c); + b &= shift(theirs) | shift(theirs) + | shift(theirs) | shift(theirs) + | shift(theirs) | shift(theirs) + | shift(theirs) | shift(theirs); + Bitboard b2 = b; + while (b2) + { + Square s = pop_lsb(&b2); + if (!(attacks_bb(c, QUEEN, s, board_bb() & ~pieces(~c)) & ~PseudoAttacks[c][KING][s] & pieces(c))) + b ^= s; + } + } + } + return b; } @@ -584,14 +615,14 @@ inline Bitboard Position::diagonal_lines() const { return var->diagonalLines; } -inline bool Position::king_pass() const { +inline bool Position::pass() const { assert(var != nullptr); - return var->kingPass || var->kingPassOnStalemate; + return var->pass || var->passOnStalemate; } -inline bool Position::king_pass_on_stalemate() const { +inline bool Position::pass_on_stalemate() const { assert(var != nullptr); - return var->kingPassOnStalemate; + return var->passOnStalemate; } inline Bitboard Position::promoted_soldiers(Color c) const { @@ -614,6 +645,11 @@ inline int Position::n_fold_rule() const { return var->nFoldRule; } +inline bool Position::flip_enclosed_pieces() const { + assert(var != nullptr); + return var->flipEnclosedPieces; +} + inline Value Position::stalemate_value(int ply) const { assert(var != nullptr); if (var->stalematePieceCount) diff --git a/src/variant.cpp b/src/variant.cpp index 7aa1535..77365f5 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -876,7 +876,7 @@ namespace { v->materialCounting = JANGGI_MATERIAL; v->diagonalLines = make_bitboard(SQ_D1, SQ_F1, SQ_E2, SQ_D3, SQ_F3, SQ_D8, SQ_F8, SQ_E9, SQ_D10, SQ_F10); - v->kingPass = true; + v->pass = true; v->nFoldValue = VALUE_DRAW; v->perpetualCheckIllegal = true; return v; diff --git a/src/variant.h b/src/variant.h index 4551498..27065dc 100644 --- a/src/variant.h +++ b/src/variant.h @@ -76,6 +76,8 @@ struct Variant { bool firstRankPawnDrops = false; bool promotionZonePawnDrops = false; bool dropOnTop = false; + bool enclosingDrop = false; + Bitboard enclosingDropStart = 0; Bitboard whiteDropRegion = AllSquares; Bitboard blackDropRegion = AllSquares; bool sittuyinRookDrop = false; @@ -87,11 +89,12 @@ struct Variant { bool seirawanGating = false; bool cambodianMoves = false; Bitboard diagonalLines = 0; - bool kingPass = false; - bool kingPassOnStalemate = false; + bool pass = false; + bool passOnStalemate = false; bool makpongRule = false; bool flyingGeneral = false; Rank soldierPromotionRank = RANK_1; + bool flipEnclosedPieces = false; // game end int nMoveRule = 50; int nFoldRule = 3; diff --git a/src/variants.ini b/src/variants.ini index 6a22b38..1dcfc0d 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -138,6 +138,8 @@ # firstRankPawnDrops: allow pawn drops to first rank [bool] (default: false) # promotionZonePawnDrops: allow pawn drops in promotion zone [bool] (default: false) # dropOnTop: piece drops need to be on top of pieces on board (e.g., for connect4) [bool] (default: false) +# enclosingDrop: require piece drop to enclose opponent's pieces (e.g., for othello) [bool] (default: false) +# enclosingDropStart: drop region for starting phase disregarding enclosingDrop (e.g., for reversi) [Bitboard] # whiteDropRegion: restrict region for piece drops of all white pieces [Bitboard] # blackDropRegion: restrict region for piece drops of all black pieces [Bitboard] # sittuyinRookDrop: restrict region of rook drops to first rank [bool] (default: false) @@ -149,17 +151,19 @@ # 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] -# kingPass: allow passing by king [bool] (default: false) -# kingPassOnStalemate: allow passing by king in case of stalemate [bool] (default: false) +# pass: allow passing [bool] (default: false) +# passOnStalemate: allow passing in case of stalemate [bool] (default: false) # makpongRule: the king may not move away from check [bool] (default: false) # flyingGeneral: disallow general face-off like in xiangqi [bool] (default: false) # soldierPromotionRank: restrict soldier to shogi pawn movements until reaching n-th rank [bool] (default: 1) +# flipEnclosedPieces: change color of pieces that are enclosed by a drop (e.g., for othello) [bool] (default: false) # nMoveRule: move count for 50/n-move rule [int] (default: 50) # nFoldRule: move count for 3/n-fold repetition rule [int] (default: 3) # nFoldValue: result in case of 3/n-fold repetition [Value] (default: draw) # nFoldValueAbsolute: result in case of 3/n-fold repetition is from white's point of view [bool] (default: false) # perpetualCheckIllegal: prohibit perpetual checks [bool] (default: false) # stalemateValue: result in case of stalemate [Value] (default: draw) +# stalematePieceCount: count material in case of stalemate [bool] (default: false) # checkmateValue: result in case of checkmate [Value] (default: loss) # shogiPawnDropMateIllegal: prohibit checkmate via shogi pawn drops [bool] (default: false) # shatarMateRule: enable shatar mating rules [bool] (default: false) @@ -300,6 +304,26 @@ immobilityIllegal = false connectN = 4 nMoveRule = 0 +[reversi] +immobile = p +startFen = 8/8/8/8/8/8/8/8[PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPpppppppppppppppppppppppppppppppp] w 0 1 +pieceDrops = true +promotionPieceTypes = - +doubleStep = false +castling = false +stalemateValue = loss +stalematePieceCount = true +materialCounting = unweighted +enclosingDrop = true +enclosingDropStart = d4 e4 d5 e5 +immobilityIllegal = false +flipEnclosedPieces = true +passOnStalemate = false + +[othello:reversi] +startFen = 8/8/8/3pP3/3Pp3/8/8/8[PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPpppppppppppppppppppppppppppppppp] w 0 1 +passOnStalemate = true + [grandhouse:grand] startFen = r8r/1nbqkcabn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKCABN1/R8R[] w - - 0 1 pieceDrops = true diff --git a/src/xboard.cpp b/src/xboard.cpp index e8adfb7..e199d56 100644 --- a/src/xboard.cpp +++ b/src/xboard.cpp @@ -238,7 +238,7 @@ void StateMachine::process_command(Position& pos, std::string token, std::istrin std::getline(is >> std::ws, fen); // Check if setboard actually indicates a passing move // to avoid unnecessarily clearing the move history - if (pos.king_pass()) + if (pos.pass()) { StateInfo st; Position p;