From aaf49a3c81cc668bea31d15e0410804a119d720d Mon Sep 17 00:00:00 2001 From: Fabian Fichter Date: Sat, 23 Dec 2023 12:15:04 +0100 Subject: [PATCH] Adjudicate when board is full Closes #749. --- setup.py | 2 +- src/parser.cpp | 1 + src/position.cpp | 7 +++++-- src/pyffish.cpp | 2 +- src/variant.cpp | 2 ++ src/variant.h | 1 + src/variants.ini | 1 + test.py | 12 ++++++++++++ 8 files changed, 24 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index a03b8328e..ab253c9ac 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ sources=sources, extra_compile_args=args) -setup(name="pyffish", version="0.0.78", +setup(name="pyffish", version="0.0.80", description="Fairy-Stockfish Python wrapper", long_description=long_description, long_description_content_type="text/markdown", diff --git a/src/parser.cpp b/src/parser.cpp index 7535e686e..48aa6870f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -537,6 +537,7 @@ Variant* VariantParser::parse(Variant* v) { parse_attribute("connectNxN", v->connectNxN); parse_attribute("connectValue", v->connectValue); parse_attribute("materialCounting", v->materialCounting); + parse_attribute("adjudicateFullBoard", v->adjudicateFullBoard); parse_attribute("countingRule", v->countingRule); parse_attribute("castlingWins", v->castlingWins); diff --git a/src/position.cpp b/src/position.cpp index a7ada05c6..bf6e60f28 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -2845,18 +2845,21 @@ bool Position::is_immediate_game_end(Value& result, int ply) const { } } - // Check for bikjang rule (Janggi) and double passing - if (st->pliesFromNull > 0 && ((st->bikjang && st->previous->bikjang) || (st->pass && st->previous->pass))) + // Check for bikjang rule (Janggi), double passing, or board running full + if ( (st->pliesFromNull > 0 && ((st->bikjang && st->previous->bikjang) || (st->pass && st->previous->pass))) + || (var->adjudicateFullBoard && !(~pieces() & board_bb()))) { result = var->materialCounting ? convert_mate_value(material_counting_result(), ply) : VALUE_DRAW; return true; } + // Tsume mode: Assume that side with king wins when not in check if (tsumeMode && !count(~sideToMove) && count(sideToMove) && !checkers()) { result = mate_in(ply); return true; } + // Failing to checkmate with virtual pieces is a loss if (two_boards() && !checkers()) { diff --git a/src/pyffish.cpp b/src/pyffish.cpp index 537148c4d..d321a57cc 100644 --- a/src/pyffish.cpp +++ b/src/pyffish.cpp @@ -54,7 +54,7 @@ void buildPosition(Position& pos, StateListPtr& states, const char *variant, con } extern "C" PyObject* pyffish_version(PyObject* self) { - return Py_BuildValue("(iii)", 0, 0, 78); + return Py_BuildValue("(iii)", 0, 0, 80); } extern "C" PyObject* pyffish_info(PyObject* self) { diff --git a/src/variant.cpp b/src/variant.cpp index cd565baf2..386c21d5d 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -1141,6 +1141,7 @@ namespace { v->enclosingDrop = ATAXX; v->flipEnclosedPieces = ATAXX; v->materialCounting = UNWEIGHTED_MATERIAL; + v->adjudicateFullBoard = true; v->nMoveRule = 0; v->freeDrops = true; return v; @@ -1167,6 +1168,7 @@ namespace { v->enclosingDropStart = make_bitboard(SQ_D4, SQ_E4, SQ_D5, SQ_E5); v->flipEnclosedPieces = REVERSI; v->materialCounting = UNWEIGHTED_MATERIAL; + v->adjudicateFullBoard = true; return v; } // Flipello diff --git a/src/variant.h b/src/variant.h index b9174ad07..ec1991859 100644 --- a/src/variant.h +++ b/src/variant.h @@ -157,6 +157,7 @@ struct Variant { int connectNxN = 0; Value connectValue = VALUE_MATE; MaterialCounting materialCounting = NO_MATERIAL_COUNTING; + bool adjudicateFullBoard = false; CountingRule countingRule = NO_COUNTING; CastlingRights castlingWins = NO_CASTLING; diff --git a/src/variants.ini b/src/variants.ini index 6b3ea7407..abc2c7099 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -290,6 +290,7 @@ # connectNxN: connect a tight NxN square for win [int] (default: 0) # connectValue: result in case of connect [Value] (default: win) # materialCounting: enable material counting rules [MaterialCounting] (default: none) +# adjudicateFullBoard: apply material counting immediately when board is full [bool] (default: false) # countingRule: enable counting rules [CountingRule] (default: none) # castlingWins: Specified castling moves are win conditions. Losing these rights is losing. [CastlingRights] (default: -) diff --git a/test.py b/test.py index b2d953a5c..9587501da 100644 --- a/test.py +++ b/test.py @@ -977,6 +977,13 @@ def test_game_result(self): result = sf.game_result("royalduck", "rnbqk1nr/pppp1ppp/4p3/8/7P/5Pb1/PPPPP*P1/RNBQKBNR w KQkq - 1 4", []) self.assertEqual(result, sf.VALUE_MATE) + def _check_immediate_game_end(self, variant, fen, moves, game_end, game_result=None): + with self.subTest(variant=variant, fen=fen, game_end=game_end, game_result=game_result): + result = sf.is_immediate_game_end(variant, fen, moves) + self.assertEqual(result[0], game_end) + if game_result is not None: + self.assertEqual(result[1], game_result) + def test_is_immediate_game_end(self): result = sf.is_immediate_game_end("capablanca", CAPA, []) self.assertFalse(result[0]) @@ -991,6 +998,11 @@ def test_is_immediate_game_end(self): self.assertTrue(result[0]) self.assertEqual(result[1], -sf.VALUE_MATE) + # full board adjudication + self._check_immediate_game_end("flipello", "pppppppp/pppppppp/pppPpppp/pPpPpppp/pppppppp/pPpPPPPP/ppPpPPpp/pppppppp[PPpp] b - - 63 32", [], True, sf.VALUE_MATE) + self._check_immediate_game_end("ataxx", "PPPpppp/pppPPPp/pPPPPPP/PPPPPPp/ppPPPpp/pPPPPpP/pPPPPPP b - - 99 50", [], True, -sf.VALUE_MATE) + self._check_immediate_game_end("ataxx", "PPPpppp/pppPPPp/pPP*PPP/PP*P*Pp/ppP*Ppp/pPPPPpP/pPPPPPP b - - 99 50", [], True, -sf.VALUE_MATE) + def test_is_optional_game_end(self): result = sf.is_optional_game_end("capablanca", CAPA, []) self.assertFalse(result[0])