Support Kakao Janggi repetition rules
authorFabian Fichter <ianfab@users.noreply.github.com>
Tue, 27 Oct 2020 18:29:36 +0000 (19:29 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Tue, 27 Oct 2020 20:34:59 +0000 (21:34 +0100)
Closes #198 and #142.

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

index 51caf97..99e0106 100644 (file)
@@ -285,6 +285,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("nFoldValue", v->nFoldValue);
     parse_attribute("nFoldValueAbsolute", v->nFoldValueAbsolute);
     parse_attribute("perpetualCheckIllegal", v->perpetualCheckIllegal);
+    parse_attribute("moveRepetitionIllegal", v->moveRepetitionIllegal);
     parse_attribute("stalemateValue", v->stalemateValue);
     parse_attribute("stalematePieceCount", v->stalematePieceCount);
     parse_attribute("checkmateValue", v->checkmateValue);
index 5d3b102..7ae6617 100644 (file)
@@ -1198,6 +1198,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
   std::memcpy(static_cast<void*>(&newSt), static_cast<void*>(st), offsetof(StateInfo, key));
   newSt.previous = st;
   st = &newSt;
+  st->move = m;
 
   // Increment ply counters. In particular, rule50 will be reset to zero later on
   // in case of a capture or a pawn move.
@@ -1985,16 +1986,42 @@ bool Position::is_optional_game_end(Value& result, int ply, int countStarted) co
           int cnt = 0;
           bool perpetualThem = st->checkersBB && stp->checkersBB;
           bool perpetualUs = st->previous->checkersBB && stp->previous->checkersBB;
+          int moveRepetition = var->moveRepetitionIllegal
+                               && type_of(st->move) == NORMAL
+                               && (board_bb(~side_to_move(), type_of(piece_on(to_sq(st->move)))) & board_bb(side_to_move(), KING))
+                               ? (stp->move == reverse_move(st->move) ? 2 : is_pass(stp->move) ? 1 : 0) : 0;
 
           for (int i = 4; i <= end; i += 2)
           {
+              // Janggi repetition rule
+              if (moveRepetition > 0)
+              {
+                  if (moveRepetition < 4)
+                  {
+                      if (stp->previous->previous->move == reverse_move((moveRepetition == 1 ? st : stp)->move))
+                          moveRepetition++;
+                      else
+                          moveRepetition = 0;
+                  }
+                  else
+                  {
+                      assert(moveRepetition == 4);
+                      if (!stp->previous->previous->capturedPiece && from_sq(stp->move) == to_sq(stp->previous->previous->move))
+                      {
+                          result = VALUE_MATE;
+                          return true;
+                      }
+                      else
+                          moveRepetition = 0;
+                  }
+              }
               stp = stp->previous->previous;
               perpetualThem &= bool(stp->checkersBB);
 
               // Return a draw score if a position repeats once earlier but strictly
               // after the root, or repeats twice before or at the root.
               if (   stp->key == st->key
-                  && ++cnt + 1 == (ply > i ? 2 : n_fold_rule()))
+                  && ++cnt + 1 == (ply > i && !var->moveRepetitionIllegal ? 2 : n_fold_rule()))
               {
                   result = convert_mate_value(  var->perpetualCheckIllegal && perpetualThem ? VALUE_MATE
                                               : var->perpetualCheckIllegal && perpetualUs ? -VALUE_MATE
@@ -2144,7 +2171,7 @@ bool Position::has_game_cycle(int ply) const {
 
   int end = captures_to_hand() ? st->pliesFromNull : std::min(st->rule50, st->pliesFromNull);
 
-  if (end < 3 || var->nFoldValue != VALUE_DRAW || var->perpetualCheckIllegal || var->materialCounting)
+  if (end < 3 || var->nFoldValue != VALUE_DRAW || var->perpetualCheckIllegal || var->materialCounting || var->moveRepetitionIllegal)
     return false;
 
   Key originalKey = st->key;
index 792daaa..49598a1 100644 (file)
@@ -66,6 +66,7 @@ struct StateInfo {
   bool       shak;
   bool       bikjang;
   bool       pass;
+  Move       move;
   int        repetition;
 
   // Used by NNUE
index 8b9a66d..b9aa5d6 100644 (file)
@@ -943,7 +943,8 @@ namespace {
         Variant* v = janggi_variant();
         v->bikjangRule = false;
         v->materialCounting = JANGGI_MATERIAL;
-        v->nFoldValue = -VALUE_MATE;
+        v->moveRepetitionIllegal = true;
+        v->nFoldRule = 4; // avoid nFold being triggered before move repetition
         return v;
     }
     // Casual rules of Janggi, where bikjang and material counting are not considered
index 5e22704..515a44f 100644 (file)
@@ -102,6 +102,7 @@ struct Variant {
   Value nFoldValue = VALUE_DRAW;
   bool nFoldValueAbsolute = false;
   bool perpetualCheckIllegal = false;
+  bool moveRepetitionIllegal = false;
   Value stalemateValue = VALUE_DRAW;
   bool stalematePieceCount = false; // multiply stalemate value by sign(count(~stm) - count(stm))
   Value checkmateValue = -VALUE_MATE;
index ee7ecd6..103a5bf 100644 (file)
 # 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)
+# moveRepetitionIllegal: prohibit moving back and forth with the same piece nFoldRule-1 times [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)