Support king of the hill, racing kings, and losers chess
authorianfab <ianfab@users.noreply.github.com>
Sun, 24 Jun 2018 15:06:49 +0000 (17:06 +0200)
committerianfab <ianfab@users.noreply.github.com>
Sun, 24 Jun 2018 15:06:49 +0000 (17:06 +0200)
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.

src/evaluate.cpp
src/movegen.cpp
src/position.cpp
src/position.h
src/search.cpp
src/uci.cpp
src/variant.cpp
src/variant.h

index 09e51f4..7d1cd04 100644 (file)
@@ -830,6 +830,7 @@ namespace {
   Value Evaluation<T>::value() {
 
     assert(!pos.checkers());
+    assert(!pos.is_variant_end());
 
     // Probe the material hash table
     me = Material::probe(pos);
index c68eddf..288c0cc 100644 (file)
@@ -389,16 +389,15 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
 template<>
 ExtMove* generate<LEGAL>(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<KING>(us);
+  if (pos.is_variant_end())
+      return moveList;
+
   ExtMove* cur = moveList;
 
   moveList = pos.checkers() ? generate<EVASIONS    >(pos, moveList)
                             : generate<NON_EVASIONS>(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;
index 9ebe3a9..286a989 100644 (file)
@@ -553,6 +553,31 @@ bool Position::legal(Move m) const {
   assert(color_of(moved_piece(m)) == us);
   assert(piece_on(square<KING>(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<EVASIONS>(*this))
+              if (capture(mevasion) && legal(mevasion))
+                  return false;
+      }
+      else
+      {
+          for (const auto& mcap : MoveList<CAPTURES>(*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.
index 64b4bb5..d978a97 100644 (file)
@@ -90,6 +90,17 @@ public:
   std::vector<PieceType> 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<ALL_PIECES>(sideToMove) - count<KING>(sideToMove)))
+  {
+      result = bare_king_value(ply);
+      return true;
+  }
+  if (    bare_king_value() != VALUE_NONE
+      &&  bare_king_move()
+      && !(count<ALL_PIECES>(~sideToMove) - count<KING>(~sideToMove)))
+  {
+      result = -bare_king_value(ply);
+      return true;
+  }
+  // capture the flag
+  if (!flag_move() && (capture_the_flag(~sideToMove) & square<KING>(~sideToMove)))
+  {
+      result = mated_in(ply);
+      return true;
+  }
+  if (flag_move() && (capture_the_flag(sideToMove) & square<KING>(sideToMove)))
+  {
+      result = mate_in(ply);
+      return true;
+  }
+  return false;
+}
+
 inline Color Position::side_to_move() const {
   return sideToMove;
 }
index 8fb4ae4..8c0a1ef 100644 (file)
@@ -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)
index 39e539f..cff5242 100644 (file)
@@ -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();
 }
index b0467f4..ca6aa68 100644 (file)
@@ -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, const Variant*>(std::string("chess"), chess));
     insert(std::pair<std::string, const Variant*>(std::string("makruk"), makruk));
     insert(std::pair<std::string, const Variant*>(std::string("asean"), asean));
@@ -118,6 +146,9 @@ void VariantMap::init() {
     insert(std::pair<std::string, const Variant*>(std::string("shatranj"), shatranj));
     insert(std::pair<std::string, const Variant*>(std::string("amazon"), amazon));
     insert(std::pair<std::string, const Variant*>(std::string("hoppelpoppel"), hoppelpoppel));
+    insert(std::pair<std::string, const Variant*>(std::string("kingofthehill"), kingofthehill));
+    insert(std::pair<std::string, const Variant*>(std::string("racingkings"), racingkings));
+    insert(std::pair<std::string, const Variant*>(std::string("losers"), losers));
 }
 
 void VariantMap::clear_all() {
index 9b2d925..0ed3cb5 100644 (file)
@@ -26,6 +26,7 @@
 #include <string>
 
 #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<PieceType> 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);