/// 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;
// 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;
/// 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()))
}
}
+
// 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;
// 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;
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;
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
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);
}
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);
}
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")