Support Koedem
authorFabian Fichter <ianfab@users.noreply.github.com>
Thu, 20 Feb 2020 20:56:43 +0000 (21:56 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Thu, 20 Feb 2020 20:56:43 +0000 (21:56 +0100)
http://schachclub-oetigheim.de/wp-content/uploads/2016/04/Koedem-rules.pdf

Closes #78.

src/evaluate.cpp
src/parser.cpp
src/position.cpp
src/position.h
src/variant.cpp
src/variant.h
src/variants.ini

index f5e88d1..b3382c5 100644 (file)
@@ -911,7 +911,11 @@ namespace {
     {
         for (PieceType pt : pos.extinction_piece_types())
             if (pt != ALL_PIECES)
-                score += make_score(1100, 1100) / pos.count(Us, pt) * (pos.extinction_value() / VALUE_MATE);
+            {
+                int denom = std::max(pos.count(Us, pt) - pos.extinction_piece_count(), 1);
+                if (pos.count(Them, pt) >= pos.extinction_opponent_piece_count() || pos.two_boards())
+                    score += make_score(1100, 1100) / denom * (pos.extinction_value() / VALUE_MATE);
+            }
             else if (pos.extinction_value() == VALUE_MATE && !pos.count<KING>(Us))
                 score += make_score(5000, pos.non_pawn_material(Us)) / pos.count<ALL_PIECES>(Us);
     }
index 8798b17..50e192c 100644 (file)
@@ -224,6 +224,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("dropChecks", v->dropChecks);
     parse_attribute("mustCapture", v->mustCapture);
     parse_attribute("mustDrop", v->mustDrop);
+    parse_attribute("mustDropType", v->mustDropType, v->pieceToChar);
     parse_attribute("pieceDrops", v->pieceDrops);
     parse_attribute("dropLoop", v->dropLoop);
     parse_attribute("capturesToHand", v->capturesToHand);
@@ -269,6 +270,8 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
         if (DoCheck && idx == std::string::npos)
             std::cerr << "extinctionPieceTypes - Invalid piece type: " << token << std::endl;
     }
+    parse_attribute("extinctionPieceCount", v->extinctionPieceCount);
+    parse_attribute("extinctionOpponentPieceCount", v->extinctionOpponentPieceCount);
     parse_attribute("flagPiece", v->flagPiece, v->pieceToChar);
     parse_attribute("whiteFlag", v->whiteFlag);
     parse_attribute("blackFlag", v->blackFlag);
index 4c5f7d4..0596378 100644 (file)
@@ -819,7 +819,7 @@ bool Position::legal(Move m) const {
   }
 
   // Illegal non-drop moves
-  if (must_drop() && type_of(m) != DROP && count_in_hand(us, ALL_PIECES))
+  if (must_drop() && type_of(m) != DROP && count_in_hand(us, var->mustDropType))
   {
       if (checkers())
       {
@@ -1615,9 +1615,9 @@ bool Position::see_ge(Move m, Value threshold) const {
   if (   extinction_value() != VALUE_NONE
       && piece_on(to)
       && (   (   extinction_piece_types().find(type_of(piece_on(to))) != extinction_piece_types().end()
-              && pieceCount[piece_on(to)] == 1)
+              && pieceCount[piece_on(to)] == extinction_piece_count() + 1)
           || (   extinction_piece_types().find(ALL_PIECES) != extinction_piece_types().end()
-              && count<ALL_PIECES>(~sideToMove) == 1)))
+              && count<ALL_PIECES>(~sideToMove) == extinction_piece_count() + 1)))
       return extinction_value() < VALUE_ZERO;
 
   int swap = PieceValue[MG][piece_on(to)] - threshold;
@@ -1832,12 +1832,14 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
   // extinction
   if (extinction_value() != VALUE_NONE)
   {
-      for (PieceType pt : extinction_piece_types())
-          if (!count(WHITE, pt) || !count(BLACK, pt))
-          {
-              result = !count(sideToMove, pt) ? extinction_value(ply) : -extinction_value(ply);
-              return true;
-          }
+      for (Color c : { WHITE, BLACK })
+          for (PieceType pt : extinction_piece_types())
+              if (   count_with_hand( c, pt) <= var->extinctionPieceCount
+                  && count_with_hand(~c, pt) >= var->extinctionOpponentPieceCount)
+              {
+                  result = c == sideToMove ? extinction_value(ply) : -extinction_value(ply);
+                  return true;
+              }
   }
   // capture the flag
   if (   capture_the_flag_piece()
index b6b8a24..6d9c59f 100644 (file)
@@ -150,6 +150,8 @@ public:
   Value extinction_value(int ply = 0) const;
   bool bare_king_move() const;
   const std::set<PieceType>& extinction_piece_types() const;
+  int extinction_piece_count() const;
+  int extinction_opponent_piece_count() const;
   PieceType capture_the_flag_piece() const;
   Bitboard capture_the_flag(Color c) const;
   bool flag_move() const;
@@ -160,6 +162,7 @@ public:
 
   // Variant-specific properties
   int count_in_hand(Color c, PieceType pt) const;
+  int count_with_hand(Color c, PieceType pt) const;
 
   // Position representation
   Bitboard pieces() const;
@@ -647,6 +650,16 @@ inline const std::set<PieceType>& Position::extinction_piece_types() const {
   return var->extinctionPieceTypes;
 }
 
+inline int Position::extinction_piece_count() const {
+  assert(var != nullptr);
+  return var->extinctionPieceCount;
+}
+
+inline int Position::extinction_opponent_piece_count() const {
+  assert(var != nullptr);
+  return var->extinctionOpponentPieceCount;
+}
+
 inline PieceType Position::capture_the_flag_piece() const {
   assert(var != nullptr);
   return var->flagPiece;
@@ -975,6 +988,10 @@ inline int Position::count_in_hand(Color c, PieceType pt) const {
   return pieceCountInHand[c][pt];
 }
 
+inline int Position::count_with_hand(Color c, PieceType pt) const {
+  return pieceCount[make_piece(c, pt)] + pieceCountInHand[c][pt];
+}
+
 inline void Position::add_to_hand(Piece pc) {
   pieceCountInHand[color_of(pc)][type_of(pc)]++;
   pieceCountInHand[color_of(pc)][ALL_PIECES]++;
index 70a41d4..9307ae5 100644 (file)
@@ -287,6 +287,20 @@ namespace {
         v->variantTemplate = "bughouse";
         v->twoBoards = true;
         v->capturesToHand = false;
+        v->stalemateValue = -VALUE_MATE;
+        return v;
+    }
+    // Koedem (Bughouse variant)
+    // http://schachclub-oetigheim.de/wp-content/uploads/2016/04/Koedem-rules.pdf
+    Variant* koedem_variant() {
+        Variant* v = bughouse_variant();
+        v->remove_piece(KING);
+        v->add_piece(COMMONER, 'k');
+        v->mustDrop = true;
+        v->mustDropType = COMMONER;
+        v->extinctionValue = -VALUE_MATE;
+        v->extinctionPieceTypes = {COMMONER};
+        v->extinctionOpponentPieceCount = 2; // own all kings/commoners
         return v;
     }
     Variant* pocketknight_variant() {
@@ -865,6 +879,7 @@ void VariantMap::init() {
     add("loop", loop_variant());
     add("chessgi", chessgi_variant());
     add("bughouse", bughouse_variant());
+    add("koedem", koedem_variant());
     add("pocketknight", pocketknight_variant());
     add("placement", placement_variant());
     add("sittuyin", sittuyin_variant());
index ec7712a..0ee06d3 100644 (file)
@@ -69,6 +69,7 @@ struct Variant {
   bool dropChecks = true;
   bool mustCapture = false;
   bool mustDrop = false;
+  PieceType mustDropType = ALL_PIECES;
   bool pieceDrops = false;
   bool dropLoop = false;
   bool capturesToHand = false;
@@ -103,6 +104,8 @@ struct Variant {
   Value extinctionValue = VALUE_NONE;
   bool bareKingMove = false;
   std::set<PieceType> extinctionPieceTypes = {};
+  int extinctionPieceCount = 0;
+  int extinctionOpponentPieceCount = 0;
   PieceType flagPiece = NO_PIECE_TYPE;
   Bitboard whiteFlag = 0;
   Bitboard blackFlag = 0;
index 89fe1ef..399f0ed 100644 (file)
 # dropChecks: allow checks by piece drops [bool] (default: true)
 # mustCapture: captures are mandatory (check evasion still takes precedence) [bool] (default: false)
 # mustDrop: drops are mandatory (e.g., for Sittuyin setup phase) [bool] (default: false)
+# mustDropType: piece type for which piece drops are mandatory [PieceType] (default: *)
 # pieceDrops: enable piece drops [bool] (default: false)
 # dropLoop: captures promoted pieces are not demoted [bool] (default: false)
 # capturesToHand: captured pieces are go to opponent's hand [bool] (default: false)
 # extinctionValue: result when one of extinctionPieceTypes is extinct [Value] (default: <none>)
 # bareKingMove: allow additional move by opponent after lone/bare king position [bool] (default: false)
 # extinctionPieceTypes: list of piece types for extinction rules, e.g., pnbrq (* means all) (default: <none>)
+# extinctionPieceCount: piece count at which the game is decided by extinction rule (default: 0)
+# extinctionOpponentPieceCount: opponent piece count required to adjudicate by extinction rule (default: 0)
 # flagPiece: piece type for capture the flag win rule [PieceType] (default: <none>)
 # whiteFlag: white's target region for capture the flag win rule [Bitboard] (default: <none>)
 # blackFlag: black's target region for capture the flag win rule [Bitboard] (default: <none>)