From: Fabian Fichter Date: Sun, 27 Jan 2019 16:46:48 +0000 (+0100) Subject: Support counting rules X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=19a744a3c64a1af1a046f36e3c6417b24b7fcac9;p=fairystockfish.git Support counting rules Counting rules are used for south-east asian chess variants: - Makruk - ASEAN - Sittuyin Standard FENs and (cutechess-style) FENs including counting rules are both supported. --- diff --git a/src/position.cpp b/src/position.cpp index 29ce04d..2b1863b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -376,9 +376,13 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, set_castling_right(c, rsq); } + // counting limit + if (counting_rule() && isdigit(ss.peek())) + ss >> st->countingLimit; + // 4. En passant square. Ignore if no pawn capture is possible - if ( ((ss >> col) && (col >= 'a' && col <= 'a' + max_file())) - && ((ss >> row) && (row >= '1' && row <= '1' + max_rank()))) + else if ( ((ss >> col) && (col >= 'a' && col <= 'a' + max_file())) + && ((ss >> row) && (row >= '1' && row <= '1' + max_rank()))) { st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); @@ -424,6 +428,13 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); } + // counting rules + if (st->countingLimit && st->rule50) + { + st->countingPly = st->rule50; + st->rule50 = 0; + } + chess960 = isChess960; thisThread = th; set_state(st); @@ -633,8 +644,14 @@ const string Position::fen() const { if (max_check_count()) ss << " " << (max_check_count() - st->checksGiven[WHITE]) << "+" << (max_check_count() - st->checksGiven[BLACK]); - ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(*this, ep_square()) + " ") - << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; + // Counting limit and counting ply, or ep-square and 50-move rule counter + if (st->countingLimit) + ss << " " << st->countingLimit << " " << st->countingPly; + else + ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(*this, ep_square()) + " ") + << st->rule50; + + ss << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; return ss.str(); } @@ -970,6 +987,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++gamePly; ++st->rule50; ++st->pliesFromNull; + if (st->countingLimit) + ++st->countingPly; Color us = sideToMove; Color them = ~us; @@ -1232,6 +1251,14 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { sideToMove = ~sideToMove; + if ( counting_rule() + && ( ((!st->countingLimit || captured) && count(sideToMove) == 1) + || (!st->countingLimit && !count()))) + { + st->countingLimit = 2 * counting_limit(); + st->countingPly = st->countingLimit && count(sideToMove) == 1 ? 2 * count() : 0; + } + // Update king attacks used for fast check detection set_check_info(st); @@ -1581,6 +1608,16 @@ bool Position::is_optional_game_end(Value& result, int ply) const { } } + // counting rules + if ( counting_rule() + && st->countingLimit + && st->countingPly >= st->countingLimit + && (!checkers() || MoveList(*this).size())) + { + result = VALUE_DRAW; + return true; + } + return false; } @@ -1739,6 +1776,57 @@ bool Position::has_game_cycle(int ply) const { } +/// Position::counting_limit() returns the counting limit in full moves. + +int Position::counting_limit() const { + + assert(counting_rule()); + + // No counting yet + if (count() && count(sideToMove) > 1) + return 0; + + switch (counting_rule()) + { + case MAKRUK_COUNTING: + // Board's honor rule + if (count(sideToMove) > 1) + return 64; + + // Pieces' honor rule + if (count(~sideToMove) > 1) + return 8; + if (count(~sideToMove) == 1) + return 16; + if (count(~sideToMove) > 1) + return 22; + if (count(~sideToMove) > 1) + return 32; + if (count(~sideToMove) == 1) + return 44; + + return 64; + + case ASEAN_COUNTING: + if (count(sideToMove) > 1) + return 0; + if (count(~sideToMove)) + return 16; + if (count(~sideToMove) && count(~sideToMove)) + return 44; + if (count(~sideToMove) && count(~sideToMove)) + return 64; + + return 0; + + default: + assert(false); + return 0; + } + +} + + /// Position::flip() flips position with the white and black sides reversed. This /// is only useful for debugging e.g. for finding evaluation symmetry bugs. diff --git a/src/position.h b/src/position.h index d05ffc0..df16fcf 100644 --- a/src/position.h +++ b/src/position.h @@ -45,6 +45,8 @@ struct StateInfo { int castlingRights; int rule50; int pliesFromNull; + int countingPly; + int countingLimit; CheckCount checksGiven[COLOR_NB]; Score psq; Square epSquare; @@ -138,6 +140,7 @@ public: CheckCount max_check_count() const; int connect_n() const; CheckCount checks_given(Color c) const; + CountingRule counting_rule() const; // Variant-specific properties int count_in_hand(Color c, PieceType pt) const; @@ -223,6 +226,7 @@ public: bool is_immediate_game_end(Value& result, int ply = 0) const; bool has_game_cycle(int ply) const; bool has_repeated() const; + int counting_limit() const; int rule50_count() const; Score psq_score() const; Value non_pawn_material(Color c) const; @@ -565,6 +569,11 @@ inline CheckCount Position::checks_given(Color c) const { return st->checksGiven[c]; } +inline CountingRule Position::counting_rule() const { + assert(var != nullptr); + return var->countingRule; +} + inline bool Position::is_immediate_game_end() const { Value result; return is_immediate_game_end(result); diff --git a/src/types.h b/src/types.h index 2a63e68..29757fc 100644 --- a/src/types.h +++ b/src/types.h @@ -170,6 +170,10 @@ enum CheckCount : int { CHECKS_0 = 0, CHECKS_NB = 11 }; +enum CountingRule { + NO_COUNTING, MAKRUK_COUNTING, ASEAN_COUNTING +}; + enum Phase { PHASE_ENDGAME, PHASE_MIDGAME = 128, diff --git a/src/variant.cpp b/src/variant.cpp index 75d6027..91c520b 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -47,6 +47,7 @@ VariantMap variants; // Global object v->doubleStep = false; v->castling = false; v->nMoveRule = 0; + v->countingRule = MAKRUK_COUNTING; return v; } Variant* asean_variant() { @@ -59,6 +60,7 @@ VariantMap variants; // Global object v->promotionPieceTypes = {ROOK, KNIGHT, KHON, MET}; v->doubleStep = false; v->castling = false; + v->countingRule = ASEAN_COUNTING; return v; } Variant* aiwok_variant() { @@ -238,6 +240,7 @@ VariantMap variants; // Global object v->promotionRank = RANK_1; // no regular promotions v->sittuyinPromotion = true; v->immobilityIllegal = false; + v->countingRule = ASEAN_COUNTING; return v; } Variant* minishogi_variant_base() { diff --git a/src/variant.h b/src/variant.h index 9fa1c0f..e285731 100644 --- a/src/variant.h +++ b/src/variant.h @@ -88,6 +88,7 @@ struct Variant { bool flagMove = false; CheckCount maxCheckCount = CheckCount(0); int connectN = 0; + CountingRule countingRule = NO_COUNTING; void add_piece(PieceType pt, char c) { pieceToChar[make_piece(WHITE, pt)] = toupper(c);