Support specifying the start of counting phase
authorAda Joule <ada.fulmina@gmail.com>
Fri, 24 Apr 2020 20:14:07 +0000 (03:14 +0700)
committerFabian Fichter <ianfab@users.noreply.github.com>
Thu, 30 Apr 2020 16:16:27 +0000 (18:16 +0200)
Required by gbtami/pychess-variants#172

No functional changes

Closes #76

src/position.cpp
src/position.h
src/pyffish.cpp
src/xboard.cpp
test.py

index 32e1934..d8fd66b 100644 (file)
@@ -581,7 +581,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) {
 /// Position::fen() returns a FEN representation of the position. In case of
 /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
 
-const string Position::fen(bool sfen, bool showPromoted, std::string holdings) const {
+const string Position::fen(bool sfen, bool showPromoted, unsigned int countStarted, std::string holdings) const {
 
   int emptyCnt;
   std::ostringstream ss;
@@ -686,7 +686,10 @@ const string Position::fen(bool sfen, bool showPromoted, std::string holdings) c
 
   // Counting ply or 50-move rule counter
   if (st->countingLimit)
-      ss << st->countingPly;
+      if (countStarted == 0)
+          ss << st->countingPly;
+      else
+          ss << std::min(st->countingPly, countStarted > (unsigned int)gamePly ? 0 : (int)(1 + gamePly - countStarted));
   else
       ss << st->rule50;
 
@@ -1806,7 +1809,7 @@ bool Position::see_ge(Move m, Value threshold) const {
 /// Position::is_optinal_game_end() tests whether the position may end the game by
 /// 50-move rule, by repetition, or a variant rule that allows a player to claim a game result.
 
-bool Position::is_optional_game_end(Value& result, int ply) const {
+bool Position::is_optional_game_end(Value& result, int ply, unsigned int countStarted) const {
 
   // n-move rule
   if (n_move_rule() && st->rule50 > (2 * n_move_rule() - 1) && (!checkers() || MoveList<LEGAL>(*this).size()))
@@ -1850,10 +1853,11 @@ bool Position::is_optional_game_end(Value& result, int ply) const {
       }
   }
 
+      
   // counting rules
   if (   counting_rule()
       && st->countingLimit
-      && st->countingPly > st->countingLimit
+      && std::min(st->countingPly, countStarted > (unsigned int)gamePly ? 0 : countStarted == 0 ? st->countingPly : (int)(1 + gamePly - countStarted)) > st->countingLimit
       && (!checkers() || MoveList<LEGAL>(*this).size()))
   {
       result = VALUE_DRAW;
index b77f286..813f054 100644 (file)
@@ -91,7 +91,7 @@ public:
   // FEN string input/output
   Position& set(const Variant* v, const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th, bool sfen = false);
   Position& set(const std::string& code, Color c, StateInfo* si);
-  const std::string fen(bool sfen = false, bool showPromoted = false, std::string holdings = "-") const;
+  const std::string fen(bool sfen = false, bool showPromoted = false, unsigned int countStarted = 0, std::string holdings = "-") const;
 
   // Variant rule properties
   const Variant* variant() const;
@@ -254,7 +254,7 @@ public:
   Thread* this_thread() const;
   bool is_immediate_game_end() const;
   bool is_game_end(Value& result, int ply = 0) const;
-  bool is_optional_game_end(Value& result, int ply = 0) const;
+  bool is_optional_game_end(Value& result, int ply = 0, unsigned int countStarted = 0) const;
   bool is_immediate_game_end(Value& result, int ply = 0) const;
   bool has_game_cycle(int ply) const;
   bool has_repeated() const;
index d324c89..609da4d 100644 (file)
@@ -479,14 +479,14 @@ extern "C" PyObject* pyffish_getFEN(PyObject* self, PyObject *args) {
     Position pos;
     const char *fen, *variant;
 
-    int chess960 = false, sfen = false, showPromoted = false;
-    if (!PyArg_ParseTuple(args, "ssO!|ppp", &variant, &fen, &PyList_Type, &moveList, &chess960, &sfen, &showPromoted)) {
+    int chess960 = false, sfen = false, showPromoted = false, countStarted = 0;
+    if (!PyArg_ParseTuple(args, "ssO!|pppi", &variant, &fen, &PyList_Type, &moveList, &chess960, &sfen, &showPromoted, &countStarted)) {
         return NULL;
     }
 
     StateListPtr states(new std::deque<StateInfo>(1));
     buildPosition(pos, states, variant, fen, moveList, chess960);
-    return Py_BuildValue("s", pos.fen(sfen, showPromoted).c_str());
+    return Py_BuildValue("s", pos.fen(sfen, showPromoted, (unsigned int)countStarted).c_str());
 }
 
 // INPUT variant, fen, move list
@@ -552,14 +552,14 @@ extern "C" PyObject* pyffish_isOptionalGameEnd(PyObject* self, PyObject *args) {
     const char *fen, *variant;
     bool gameEnd;
     Value result;
-    int chess960 = false;
-    if (!PyArg_ParseTuple(args, "ssO!|p", &variant, &fen, &PyList_Type, &moveList, &chess960)) {
+    int chess960 = false, countStarted = 0;
+    if (!PyArg_ParseTuple(args, "ssO!|pi", &variant, &fen, &PyList_Type, &moveList, &chess960, &countStarted)) {
         return NULL;
     }
 
     StateListPtr states(new std::deque<StateInfo>(1));
     buildPosition(pos, states, variant, fen, moveList, chess960);
-    gameEnd = pos.is_optional_game_end(result);
+    gameEnd = pos.is_optional_game_end(result, 0, (unsigned int)countStarted);
     return Py_BuildValue("(Oi)", gameEnd ? Py_True : Py_False, result);
 }
 
index bcf4c00..243bd51 100644 (file)
@@ -274,7 +274,7 @@ void StateMachine::process_command(Position& pos, std::string token, std::istrin
           else
           {
               std::transform(black_holdings.begin(), black_holdings.end(), black_holdings.begin(), ::tolower);
-              fen = pos.fen(false, false, white_holdings + black_holdings);
+              fen = pos.fen(false, false, 0, white_holdings + black_holdings);
           }
           setboard(pos, states, fen);
       }
diff --git a/test.py b/test.py
index 5680980..87906c0 100644 (file)
--- a/test.py
+++ b/test.py
@@ -216,6 +216,43 @@ class TestPyffish(unittest.TestCase):
         result = sf.get_fen("makruk", fen, [], False, False, True)
         self.assertEqual(result, fen)
 
+        # makruk piece honor counting
+        fen = "8/3k4/8/2K1S1P1/8/8/8/8 w - - 0 1"
+        moves = ["g5g6m"]
+        result = sf.get_fen("makruk", fen, moves, False, False, True)
+        self.assertEqual(result, "8/3k4/6M~1/2K1S3/8/8/8/8 b - 88 8 1")
+
+        fen = "8/2K3k1/5m2/4S1S1/8/8/8/8 w - 128 97 1"
+        moves = ["e5f6"]
+        result = sf.get_fen("makruk", fen, moves, False, False, True)
+        self.assertEqual(result, "8/2K3k1/5S2/6S1/8/8/8/8 b - 44 8 1")
+
+        # makruk board honor counting
+        fen = "3k4/2m5/8/4MP2/3KS3/8/8/8 w - - 0 1"
+        moves = ["f5f6m"]
+        result = sf.get_fen("makruk", fen, moves, False, False, True)
+        self.assertEqual(result, "3k4/2m5/5M~2/4M3/3KS3/8/8/8 b - 128 0 1")
+
+        fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 128 0 33"
+        moves = ["d4d5"]
+        result = sf.get_fen("makruk", fen, moves, False, False, True)
+        self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 128 1 33")
+
+        fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 128 36 1"
+        moves = ["d4d5"]
+        result = sf.get_fen("makruk", fen, moves, False, False, True)
+        self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 128 37 1")
+
+        fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 128 0 33"
+        moves = ["d4d5"]
+        result = sf.get_fen("makruk", fen, moves, False, False, True, -1)
+        self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 128 0 33")
+
+        fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 128 7 33"
+        moves = ["d4d5"]
+        result = sf.get_fen("makruk", fen, moves, False, False, True, 58)
+        self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 128 8 33")
+
     def test_get_san(self):
         fen = "4k3/8/3R4/8/1R3R2/8/3R4/4K3 w - - 0 1"
         result = sf.get_san("chess", fen, "b4d4")