From 619d9eb12351f4cc655700c6cfd73c8790f1e5d8 Mon Sep 17 00:00:00 2001 From: Ada Joule Date: Sat, 25 Apr 2020 03:14:07 +0700 Subject: [PATCH] Support specifying the start of counting phase Required by gbtami/pychess-variants#172 No functional changes Closes #76 --- src/position.cpp | 12 ++++++++---- src/position.h | 4 ++-- src/pyffish.cpp | 12 ++++++------ src/xboard.cpp | 2 +- test.py | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 32e1934..d8fd66b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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(*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(*this).size())) { result = VALUE_DRAW; diff --git a/src/position.h b/src/position.h index b77f286..813f054 100644 --- a/src/position.h +++ b/src/position.h @@ -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; diff --git a/src/pyffish.cpp b/src/pyffish.cpp index d324c89..609da4d 100644 --- a/src/pyffish.cpp +++ b/src/pyffish.cpp @@ -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(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(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); } diff --git a/src/xboard.cpp b/src/xboard.cpp index bcf4c00..243bd51 100644 --- a/src/xboard.cpp +++ b/src/xboard.cpp @@ -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 --- 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") -- 1.7.0.4