template <> bool set(const std::string& value, CountingRule& target) {
target = value == "makruk" ? MAKRUK_COUNTING
+ : value == "cambodian" ? CAMBODIAN_COUNTING
: value == "asean" ? ASEAN_COUNTING
: NO_COUNTING;
return value == "makruk" || value == "asean" || value == "none";
// Counting limit or ep-square
if (st->countingLimit)
- ss << " " << st->countingLimit << " ";
+ ss << " " << counting_limit(countStarted) << " ";
else
ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(*this, ep_square()) + " ");
sideToMove = ~sideToMove;
- if ( counting_rule()
- && (!st->countingLimit || (captured && count<ALL_PIECES>(sideToMove) == 1))
- && counting_limit())
+ if (counting_rule())
{
- st->countingLimit = 2 * counting_limit();
- st->countingPly = counting_rule() == MAKRUK_COUNTING && count<ALL_PIECES>(sideToMove) == 1 ? 2 * count<ALL_PIECES>() : 0;
+ if (counting_rule() != ASEAN_COUNTING && type_of(captured) == PAWN && count<ALL_PIECES>(~sideToMove) == 1 && !count<PAWN>() && count_limit(~sideToMove))
+ {
+ st->countingLimit = 2 * count_limit(~sideToMove);
+ st->countingPly = 2 * count<ALL_PIECES>() - 1;
+ }
+
+ if ((!st->countingLimit || ((captured || type_of(m) == PROMOTION) && count<ALL_PIECES>(sideToMove) == 1)) && count_limit(sideToMove))
+ {
+ st->countingLimit = 2 * count_limit(sideToMove);
+ st->countingPly = counting_rule() == ASEAN_COUNTING || count<ALL_PIECES>(sideToMove) > 1 ? 0 : 2 * count<ALL_PIECES>();
+ }
}
// Update king attacks used for fast check detection
// counting rules
if ( counting_rule()
&& st->countingLimit
- && counting_ply(countStarted) > st->countingLimit
+ && counting_ply(countStarted) > counting_limit(countStarted)
&& (!checkers() || MoveList<LEGAL>(*this).size()))
{
result = VALUE_DRAW;
}
-/// Position::counting_limit() returns the counting limit in full moves.
+/// Position::count_limit() returns the counting limit in full moves.
-int Position::counting_limit() const {
+int Position::count_limit(Color sideToCount) const {
assert(counting_rule());
{
case MAKRUK_COUNTING:
// No counting for side to move
- if (count<PAWN>() || count<ALL_PIECES>(~sideToMove) == 1)
+ if (count<PAWN>() || count<ALL_PIECES>(~sideToCount) == 1)
return 0;
// Board's honor rule
- if (count<ALL_PIECES>(sideToMove) > 1)
+ if (count<ALL_PIECES>(sideToCount) > 1)
return 64;
// Pieces' honor rule
- if (count<ROOK>(~sideToMove) > 1)
+ if (count<ROOK>(~sideToCount) > 1)
return 8;
- if (count<ROOK>(~sideToMove) == 1)
+ if (count<ROOK>(~sideToCount) == 1)
return 16;
- if (count<KHON>(~sideToMove) > 1)
+ if (count<KHON>(~sideToCount) > 1)
return 22;
- if (count<KNIGHT>(~sideToMove) > 1)
+ if (count<KNIGHT>(~sideToCount) > 1)
return 32;
- if (count<KHON>(~sideToMove) == 1)
+ if (count<KHON>(~sideToCount) == 1)
return 44;
return 64;
+ case CAMBODIAN_COUNTING:
+ // No counting for side to move
+ if (count<ALL_PIECES>(sideToCount) > 3 || count<ALL_PIECES>(~sideToCount) == 1)
+ return 0;
+ // Board's honor rule
+ if (count<ALL_PIECES>(sideToCount) > 1)
+ return 63;
+ // Pieces' honor rule
+ if (count<PAWN>())
+ return 0;
+ if (count<ROOK>(~sideToCount) > 1)
+ return 7;
+ if (count<ROOK>(~sideToCount) == 1)
+ return 15;
+ if (count<KHON>(~sideToCount) > 1)
+ return 21;
+ if (count<KNIGHT>(~sideToCount) > 1)
+ return 31;
+ if (count<KHON>(~sideToCount) == 1)
+ return 43;
+
+ return 63;
+
case ASEAN_COUNTING:
- if (count<PAWN>() || count<ALL_PIECES>(sideToMove) > 1)
+ if (count<PAWN>() || count<ALL_PIECES>(sideToCount) > 1)
return 0;
- if (count<ROOK>(~sideToMove))
+ if (count<ROOK>(~sideToCount))
return 16;
- if (count<KHON>(~sideToMove))
+ if (count<KHON>(~sideToCount))
return 44;
- if (count<KNIGHT>(~sideToMove))
+ if (count<KNIGHT>(~sideToCount))
return 64;
return 0;
bool has_game_cycle(int ply) const;
bool has_repeated() const;
Bitboard chased() const;
- int counting_limit() const;
+ int count_limit(Color sideToCount) const;
+ int board_honor_counting_ply(int countStarted) const;
+ bool board_honor_counting_shorter(int countStarted) const;
+ int counting_limit(int countStarted) const;
int counting_ply(int countStarted) const;
int rule50_count() const;
Score psq_score() const;
return gamePly;
}
+inline int Position::board_honor_counting_ply(int countStarted) const {
+ return countStarted == 0 ?
+ st->countingPly :
+ countStarted < 0 ? 0 : std::max(1 + gamePly - countStarted, 0);
+}
+
+inline bool Position::board_honor_counting_shorter(int countStarted) const {
+ return counting_rule() == CAMBODIAN_COUNTING && 126 - board_honor_counting_ply(countStarted) < st->countingLimit - st->countingPly;
+}
+
+inline int Position::counting_limit(int countStarted) const {
+ return board_honor_counting_shorter(countStarted) ? 126 : st->countingLimit;
+}
+
inline int Position::counting_ply(int countStarted) const {
- return countStarted == 0 || (count<ALL_PIECES>(WHITE) <= 1 || count<ALL_PIECES>(BLACK) <= 1) ? st->countingPly : countStarted < 0 ? 0 : std::min(st->countingPly, std::max(1 + gamePly - countStarted, 0));
+ return !count<PAWN>() && (count<ALL_PIECES>(WHITE) <= 1 || count<ALL_PIECES>(BLACK) <= 1) && !board_honor_counting_shorter(countStarted) ?
+ st->countingPly :
+ board_honor_counting_ply(countStarted);
}
inline int Position::rule50_count() const {
};
enum CountingRule {
- NO_COUNTING, MAKRUK_COUNTING, ASEAN_COUNTING
+ NO_COUNTING, MAKRUK_COUNTING, CAMBODIAN_COUNTING, ASEAN_COUNTING
};
enum ChasingRule {
v->startFen = "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w DEde - 0 1";
v->gating = true;
v->cambodianMoves = true;
+ v->countingRule = CAMBODIAN_COUNTING;
v->nnueAlias = "makruk";
return v;
}
# [Bitboard]: list of squares [e.g., d4 e4 d5 e5]. * can be used as wildcard for files (e.g., *1 is the first rank)
# [Value]: game result for the side to move [win, loss, draw]
# [MaterialCounting]: material couting rules for adjudication [janggi, unweighted, whitedrawodds, blackdrawodds, none]
-# [CountingRule]: makruk or ASEAN counting rules [makruk, asean, none]
+# [CountingRule]: makruk, cambodian, or ASEAN counting rules [makruk, cambodian, asean, none]
# [ChasingRule]: xiangqi chasing rules [axf, none]
# [EnclosingRule]: reversi or ataxx enclosing rules [reversi, ataxx, none]
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")
+ # ouk piece honor counting
+ fen = "8/3k4/8/2K1S1P1/8/8/8/8 w - - 0 1"
+ moves = ["g5g6m"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True)
+ self.assertEqual(result, "8/3k4/6M~1/2K1S3/8/8/8/8 b - 86 8 1")
+
+ fen = "8/2K3k1/5m2/4S1S1/8/8/8/8 w - 128 97 1"
+ moves = ["e5f6"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True)
+ self.assertEqual(result, "8/2K3k1/5S2/6S1/8/8/8/8 b - 42 8 1")
+
+ # adjust to board honor counting if it's faster
+ fen = "8/3k4/8/2K1S1P1/8/8/8/8 w - - 0 1"
+ moves = ["g5g6m"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True, -1)
+ self.assertEqual(result, "8/3k4/6M~1/2K1S3/8/8/8/8 b - 86 8 1")
+
+ fen = "8/2K3k1/5m2/4S1S1/8/8/8/8 w - 126 101 80"
+ moves = ["e5f6"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True, 58)
+ self.assertEqual(result, "8/2K3k1/5S2/6S1/8/8/8/8 b - 126 102 80")
+
+ # pawn promotion triggers piece honor counting
+ fen = "8/8/4k3/5P2/8/2RMK3/8/8 w - 126 41 50"
+ moves = ["f5f6m"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True, 58)
+ self.assertEqual(result, "8/8/4kM~2/8/8/2RMK3/8/8 b - 30 10 50")
+
+ # king capturing the last unpromoted pawn triggers piece honor counting
+ fen = "8/8/4k3/5P2/8/2RMK3/8/8 b - 126 42 50"
+ moves = ["e6f5"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True, 58)
+ self.assertEqual(result, "8/8/8/5k2/8/2RMK3/8/8 w - 30 7 51")
+
+ # ouk board honor counting
+ fen = "3k4/2m5/8/4MP2/3KS3/8/8/8 w - - 0 1"
+ moves = ["f5f6m"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True)
+ self.assertEqual(result, "3k4/2m5/5M~2/4M3/3KS3/8/8/8 b - 126 0 1")
+
+ fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 126 0 33"
+ moves = ["d4d5"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True)
+ self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 126 1 33")
+
+ fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 126 36 1"
+ moves = ["d4d5"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True)
+ self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 126 37 1")
+
+ fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 126 0 33"
+ moves = ["d4d5"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True, -1)
+ self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 126 0 33")
+
+ fen = "3k4/2m5/5M~2/4M3/3KS3/8/8/8 w - 126 7 33"
+ moves = ["d4d5"]
+ result = sf.get_fen("cambodian", fen, moves, False, False, True, 58)
+ self.assertEqual(result, "3k4/2m5/5M~2/3KM3/4S3/8/8/8 b - 126 8 33")
+
# asean counting
fen = "4k3/3r4/2K5/8/3R4/8/8/8 w - - 0 1"
moves = ["d4d7"]