Adjudicate when board is full (#750)
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 23 Dec 2023 14:02:28 +0000 (15:02 +0100)
committerGitHub <noreply@github.com>
Sat, 23 Dec 2023 14:02:28 +0000 (15:02 +0100)
Closes #749.

setup.py
src/parser.cpp
src/position.cpp
src/pyffish.cpp
src/variant.cpp
src/variant.h
src/variants.ini
test.py

index a03b832..ab253c9 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,7 @@ pyffish_module = Extension(
     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",
index 7535e68..48aa687 100644 (file)
@@ -537,6 +537,7 @@ Variant* VariantParser<DoCheck>::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);
     
index a7ada05..bf6e60f 100644 (file)
@@ -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<KING>(~sideToMove) && count<KING>(sideToMove) && !checkers())
   {
       result = mate_in(ply);
       return true;
   }
+
   // Failing to checkmate with virtual pieces is a loss
   if (two_boards() && !checkers())
   {
index 537148c..d321a57 100644 (file)
@@ -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) {
index cd565ba..386c21d 100644 (file)
@@ -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
index b9174ad..ec19918 100644 (file)
@@ -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;
 
index 6b3ea74..abc2c70 100644 (file)
 # 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 b2d953a..9587501 100644 (file)
--- a/test.py
+++ b/test.py
@@ -977,6 +977,13 @@ class TestPyffish(unittest.TestCase):
         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 @@ class TestPyffish(unittest.TestCase):
         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])