From: ianfab Date: Sun, 24 Jun 2018 15:06:49 +0000 (+0200) Subject: Support king of the hill, racing kings, and losers chess X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=dcf2b4018d5c8b10e4ec6c3a26d08c833fbcfc18;p=fairystockfish.git Support king of the hill, racing kings, and losers chess Add options for bare king rule, checkmate/stalemate value, king target squares, mandatory captures, and prohibited checks to support king of the hill, racing kings, and losers, and to complete the implementation of shatranj. --- diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 09e51f4..7d1cd04 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -830,6 +830,7 @@ namespace { Value Evaluation::value() { assert(!pos.checkers()); + assert(!pos.is_variant_end()); // Probe the material hash table me = Material::probe(pos); diff --git a/src/movegen.cpp b/src/movegen.cpp index c68eddf..288c0cc 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -389,16 +389,15 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { - Color us = pos.side_to_move(); - Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us); - Square ksq = pos.square(us); + if (pos.is_variant_end()) + return moveList; + ExtMove* cur = moveList; moveList = pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); while (cur != moveList) - if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT) - && !pos.legal(*cur)) + if (!pos.legal(*cur)) *cur = (--moveList)->move; else ++cur; diff --git a/src/position.cpp b/src/position.cpp index 9ebe3a9..286a989 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -553,6 +553,31 @@ bool Position::legal(Move m) const { assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); + // illegal checks + if (!checking_permitted() && gives_check(m)) + return false; + + // illegal quiet moves + if (must_capture() && !capture(m)) + { + if (checkers()) + { + for (const auto& mevasion : MoveList(*this)) + if (capture(mevasion) && legal(mevasion)) + return false; + } + else + { + for (const auto& mcap : MoveList(*this)) + if (capture(mcap) && legal(mcap)) + return false; + } + } + + // game end + if (is_variant_end()) + 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. diff --git a/src/position.h b/src/position.h index 64b4bb5..d978a97 100644 --- a/src/position.h +++ b/src/position.h @@ -90,6 +90,17 @@ public: std::vector promotion_piece_types() const; bool double_step_enabled() const; bool castling_enabled() const; + bool checking_permitted() const; + bool must_capture() const; + // winning conditions + Value stalemate_value(int ply = 0) const; + Value checkmate_value(int ply = 0) const; + Value bare_king_value(int ply = 0) const; + bool bare_king_move() const; + Bitboard capture_the_flag(Color c) const; + bool flag_move() const; + bool is_variant_end() const; + bool is_variant_end(Value& result, int ply = 0) const; // Position representation Bitboard pieces() const; @@ -236,6 +247,90 @@ inline bool Position::castling_enabled() const { return var->castling; } +inline bool Position::checking_permitted() const { + assert(var != nullptr); + return var->checking; +} + +inline bool Position::must_capture() const { + assert(var != nullptr); + return var->mustCapture; +} + +inline Value Position::stalemate_value(int ply) const { + assert(var != nullptr); + Value v = var->stalemateValue; + return v == VALUE_MATE ? mate_in(ply) + : v == -VALUE_MATE ? mated_in(ply) + : v; +} + +inline Value Position::checkmate_value(int ply) const { + assert(var != nullptr); + Value v = var->checkmateValue; + return v == VALUE_MATE ? mate_in(ply) + : v == -VALUE_MATE ? mated_in(ply) + : v; +} + +inline Value Position::bare_king_value(int ply) const { + assert(var != nullptr); + Value v = var->bareKingValue; + return v == VALUE_MATE ? mate_in(ply) + : v == -VALUE_MATE ? mated_in(ply) + : v; +} + +inline bool Position::bare_king_move() const { + assert(var != nullptr); + return var->bareKingMove; +} + +inline Bitboard Position::capture_the_flag(Color c) const { + assert(var != nullptr); + return c == WHITE ? var->whiteFlag : var->blackFlag; +} + +inline bool Position::flag_move() const { + assert(var != nullptr); + return var->flagMove; +} + +inline bool Position::is_variant_end() const { + Value result; + return is_variant_end(result); +} + +inline bool Position::is_variant_end(Value& result, int ply) const { + // bare king rule + if ( bare_king_value() != VALUE_NONE + && !bare_king_move() + && !(count(sideToMove) - count(sideToMove))) + { + result = bare_king_value(ply); + return true; + } + if ( bare_king_value() != VALUE_NONE + && bare_king_move() + && !(count(~sideToMove) - count(~sideToMove))) + { + result = -bare_king_value(ply); + return true; + } + // capture the flag + if (!flag_move() && (capture_the_flag(~sideToMove) & square(~sideToMove))) + { + result = mated_in(ply); + return true; + } + if (flag_move() && (capture_the_flag(sideToMove) & square(sideToMove))) + { + result = mate_in(ply); + return true; + } + return false; +} + inline Color Position::side_to_move() const { return sideToMove; } diff --git a/src/search.cpp b/src/search.cpp index 8fb4ae4..8c0a1ef 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -202,8 +202,10 @@ void MainThread::search() { if (rootMoves.empty()) { rootMoves.emplace_back(MOVE_NONE); + Value variantResult; sync_cout << "info depth 0 score " - << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) + << UCI::value( rootPos.is_variant_end(variantResult) ? variantResult + : rootPos.checkers() ? rootPos.checkmate_value() : rootPos.stalemate_value()) << sync_endl; } else @@ -565,6 +567,10 @@ namespace { if (!rootNode) { + Value variantResult; + if (pos.is_variant_end(variantResult, ss->ply)) + return variantResult; + // Step 2. Check for aborted search and immediate draw if ( Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) @@ -1148,7 +1154,7 @@ moves_loop: // When in check, search starts from here if (!moveCount) bestValue = excludedMove ? alpha - : inCheck ? mated_in(ss->ply) : VALUE_DRAW; + : inCheck ? pos.checkmate_value(ss->ply) : pos.stalemate_value(ss->ply); else if (bestMove) { // Quiet best move: update move sorting heuristics @@ -1217,6 +1223,10 @@ moves_loop: // When in check, search starts from here inCheck = pos.checkers(); moveCount = 0; + Value variantResult; + if (pos.is_variant_end(variantResult, ss->ply)) + return variantResult; + // Check for an immediate draw or maximum ply reached if ( pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) diff --git a/src/uci.cpp b/src/uci.cpp index 39e539f..cff5242 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -256,7 +256,7 @@ string UCI::value(Value v) { if (abs(v) < VALUE_MATE - MAX_PLY) ss << "cp " << v * 100 / PawnValueEg; else - ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; + ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v - 1) / 2; return ss.str(); } diff --git a/src/variant.cpp b/src/variant.cpp index b0467f4..ca6aa68 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -91,7 +91,9 @@ void VariantMap::init() { v->promotionPieceTypes = {FERS}; v->doubleStep = false; v->castling = false; - // TODO: bare king, stalemate + v->bareKingValue = -VALUE_MATE; + v->bareKingMove = true; + v->stalemateValue = -VALUE_MATE; return v; } (); const Variant* amazon = [&]{ @@ -111,6 +113,32 @@ void VariantMap::init() { v->promotionPieceTypes = {QUEEN, ROOK, BISKNI, KNIBIS}; return v; } (); + const Variant* kingofthehill = [&]{ + Variant* v = new Variant(); + v->whiteFlag = make_bitboard(SQ_D4, SQ_E4, SQ_D5, SQ_E5); + v->whiteFlag = make_bitboard(SQ_D4, SQ_E4, SQ_D5, SQ_E5); + v->flagMove = false; + return v; + } (); + const Variant* racingkings = [&]{ + Variant* v = new Variant(); + v->startFen = "8/8/8/8/8/8/krbnNBRK/qrbnNBRQ w - - 0 1"; + v->whiteFlag = Rank8BB; + v->whiteFlag = Rank8BB; + v->flagMove = true; + v->castling = false; + v->checking = false; + return v; + } (); + const Variant* losers = [&]{ + Variant* v = new Variant(); + v->checkmateValue = VALUE_MATE; + v->stalemateValue = VALUE_MATE; + v->bareKingValue = VALUE_MATE; + v->bareKingMove = false; + v->mustCapture = true; + return v; + } (); insert(std::pair(std::string("chess"), chess)); insert(std::pair(std::string("makruk"), makruk)); insert(std::pair(std::string("asean"), asean)); @@ -118,6 +146,9 @@ void VariantMap::init() { insert(std::pair(std::string("shatranj"), shatranj)); insert(std::pair(std::string("amazon"), amazon)); insert(std::pair(std::string("hoppelpoppel"), hoppelpoppel)); + insert(std::pair(std::string("kingofthehill"), kingofthehill)); + insert(std::pair(std::string("racingkings"), racingkings)); + insert(std::pair(std::string("losers"), losers)); } void VariantMap::clear_all() { diff --git a/src/variant.h b/src/variant.h index 9b2d925..0ed3cb5 100644 --- a/src/variant.h +++ b/src/variant.h @@ -26,6 +26,7 @@ #include #include "types.h" +#include "bitboard.h" /// Variant struct stores information needed to determine the rules of a variant. @@ -38,6 +39,16 @@ struct Variant { std::vector promotionPieceTypes = {QUEEN, ROOK, BISHOP, KNIGHT}; bool doubleStep = true; bool castling = true; + bool checking = true; + bool mustCapture = false; + // game end + Value stalemateValue = VALUE_DRAW; + Value checkmateValue = -VALUE_MATE; + Value bareKingValue = VALUE_NONE; + bool bareKingMove = false; + Bitboard whiteFlag = 0; + Bitboard blackFlag = 0; + bool flagMove = false; void set_piece(PieceType pt, char c) { pieceToChar[make_piece(WHITE, pt)] = toupper(c);