diff --git a/src/Makefile b/src/Makefile index 1e5a16580b1..c74b43327e5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -207,6 +207,7 @@ ifneq ($(comp),mingw) endif ### 3.4 Debugging +CXXFLAGS += -DKOTH ifeq ($(debug),no) CXXFLAGS += -DNDEBUG else diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 2f943c4e725..181684081f4 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -133,7 +133,11 @@ void benchmark(const Position& current, istream& is) { for (size_t i = 0; i < fens.size(); ++i) { +#ifdef KOTH + Position pos(fens[i], Options["UCI_Chess960"], Options["UCI_VariantKOTH"], Threads.main()); +#else Position pos(fens[i], Options["UCI_Chess960"], Threads.main()); +#endif cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; diff --git a/src/endgame.cpp b/src/endgame.cpp index 311443ec939..a74aeb6f618 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -96,7 +96,11 @@ namespace { string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/" + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10"; +#ifdef KOTH + return Position(fen, NULL).material_key(); +#else return Position(fen, false, NULL).material_key(); +#endif } template diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9a55fbd8878..2b3d2e18207 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -153,6 +153,15 @@ namespace { // Hanging contains a bonus for each enemy hanging piece const Score Hanging = S(23, 20); +#ifdef KOTH + const Score KOTHDistanceBonus[4] = { + S(9*PawnValueMg + PawnValueMg/2, 9*PawnValueEg), + S(4*PawnValueMg + PawnValueMg/2, 4*PawnValueEg), + S(2*PawnValueMg + PawnValueMg/2, 2*PawnValueEg), + S(0, 0) + }; +#endif + #undef S const Score RookOnPawn = make_score(10, 28); @@ -394,6 +403,15 @@ namespace { // King shelter and enemy pawns storm Score score = ei.pi->king_safety(pos, ksq); +#ifdef KOTH + if (pos.is_koth()) + { + // Initial attempt to adjust score based on KOTH distance + score += KOTHDistanceBonus[pos.koth_distance(Us)]; + score -= KOTHDistanceBonus[pos.koth_distance(Them)]; + } +#endif + // Main king safety evaluation if (ei.kingAttackersCount[Them]) { @@ -680,6 +698,17 @@ namespace { // Score is computed from the point of view of white. score = pos.psq_score(); +#ifdef KOTH + if (pos.is_koth()) + { + // TODO: Do we need or want both conditions here? + if (pos.is_koth_win()) + return VALUE_MATE; + if (pos.is_koth_loss()) + return -VALUE_MATE; + } +#endif + // Probe the material hash table ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); score += ei.mi->material_value(); diff --git a/src/movepick.cpp b/src/movepick.cpp index 95172b944c1..bdff449e460 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -302,6 +302,14 @@ void MovePicker::generate_next_stage() { template<> Move MovePicker::next_move() { +#ifdef KOTH + assert(!pos.is_koth_win()); + + // Best move can only be MOVE_NONE when searching on a lost KOTH position + if (pos.is_koth_loss()) + return MOVE_NONE; +#endif + Move move; while (true) diff --git a/src/position.cpp b/src/position.cpp index bdbfea80175..bd73ed6ca76 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -191,7 +191,11 @@ void Position::clear() { /// This function is not very robust - make sure that input FENs are correct, /// this is assumed to be the responsibility of the GUI. +#ifdef KOTH +void Position::set(const string& fenStr, bool isChess960, bool isKOTH, Thread* th) { +#else void Position::set(const string& fenStr, bool isChess960, Thread* th) { +#endif /* A FEN string defines a particular position using only the ASCII character set. @@ -300,6 +304,9 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) { gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); chess960 = isChess960; +#ifdef KOTH + koth = isKOTH; +#endif thisThread = th; set_state(st); @@ -712,6 +719,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI assert(is_ok(m)); assert(&newSt != st); +#ifdef KOTH + assert(!is_koth_win()); + assert(!is_koth_loss()); +#endif ++nodes; Key k = st->key; @@ -1178,7 +1189,11 @@ void Position::flip() { std::getline(ss, token); // Half and full moves f += token; +#ifdef KOTH + set(f, is_chess960(), is_koth(), this_thread()); +#else set(f, is_chess960(), this_thread()); +#endif assert(pos_is_ok()); } diff --git a/src/position.h b/src/position.h index d950411f6fe..65b33267f02 100644 --- a/src/position.h +++ b/src/position.h @@ -76,12 +76,21 @@ class Position { public: Position() {} Position(const Position& pos, Thread* t) { *this = pos; thisThread = t; } +#ifdef KOTH + Position(const std::string& f, Thread* t) { set(f, false, false, t); } + Position(const std::string& f, bool c960, bool isKOTH, Thread* t) { set(f, c960, isKOTH, t); } +#else Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } +#endif Position& operator=(const Position&); static void init(); // Text input/output +#ifdef KOTH + void set(const std::string& fenStr, bool isChess960, bool isKOTH, Thread* th); +#else void set(const std::string& fenStr, bool isChess960, Thread* th); +#endif const std::string fen() const; const std::string pretty(Move m = MOVE_NONE) const; @@ -159,6 +168,12 @@ class Position { Phase game_phase() const; int game_ply() const; bool is_chess960() const; +#ifdef KOTH + bool is_koth() const; + bool is_koth_win() const; + bool is_koth_loss() const; + int koth_distance(Color c) const; +#endif Thread* this_thread() const; uint64_t nodes_searched() const; void set_nodes_searched(uint64_t n); @@ -201,6 +216,9 @@ class Position { Thread* thisThread; StateInfo* st; bool chess960; +#ifdef KOTH + bool koth; +#endif }; inline uint64_t Position::nodes_searched() const { @@ -366,6 +384,32 @@ inline bool Position::pawn_on_7th(Color c) const { return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7)); } +#ifdef KOTH +inline bool Position::is_koth() const { + return koth; +} + +// Win if king is in the center (KOTH) +inline bool Position::is_koth_win() const { + return koth_distance(side_to_move()) == 0; +} + +// Loss if king is in the center (KOTH) +inline bool Position::is_koth_loss() const { + return koth_distance(~side_to_move()) == 0; +} + +inline int Position::koth_distance(Color c) const { + Square ksq = king_square(c); + return std::min( + std::min(square_distance(ksq, SQ_D4), + square_distance(ksq, SQ_E4)), + std::min(square_distance(ksq, SQ_D5), + square_distance(ksq, SQ_E5)) + ); +} +#endif + inline bool Position::is_chess960() const { return chess960; } diff --git a/src/search.cpp b/src/search.cpp index 3b7fab5f31f..4aa34b34c56 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -250,6 +250,12 @@ void Search::think() { RootPos.this_thread()->wait_for(Signals.stop); } +#ifdef KOTH + // Best move can only be MOVE_NONE when searching on a lost KOTH position + if (RootPos.is_koth() && RootPos.is_koth_loss()) + sync_cout << "bestmove " << "(none) ponder (none)" << sync_endl; + else +#endif // Best move could be MOVE_NONE when searching on a stalemate position sync_cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], RootPos.is_chess960()) << " ponder " << move_to_uci(RootMoves[0].pv[1], RootPos.is_chess960()) diff --git a/src/uci.cpp b/src/uci.cpp index b82eeff556b..c75f4334176 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -68,7 +68,11 @@ namespace { else return; +#ifdef KOTH + pos.set(fen, Options["UCI_Chess960"], Options["UCI_VariantKOTH"], Threads.main()); +#else pos.set(fen, Options["UCI_Chess960"], Threads.main()); +#endif SetupStates = Search::StateStackPtr(new std::stack()); // Parse move list (if any) @@ -145,7 +149,11 @@ namespace { void UCI::loop(int argc, char* argv[]) { +#ifdef KOTH + Position pos(StartFEN, Threads.main()); // The root position +#else Position pos(StartFEN, false, Threads.main()); // The root position +#endif string token, cmd; for (int i = 1; i < argc; ++i) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 63d47f30edf..30e2c9a8a6f 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -71,6 +71,9 @@ void init(OptionsMap& o) { o["Minimum Thinking Time"] << Option(20, 0, 5000); o["Slow Mover"] << Option(80, 10, 1000); o["UCI_Chess960"] << Option(false); +#ifdef KOTH + o["UCI_VariantKOTH"] << Option(false); +#endif }