Use tandem pawn notation for double tandems
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 6 Nov 2021 10:14:01 +0000 (11:14 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 6 Nov 2021 16:05:27 +0000 (17:05 +0100)
In WXF notation +/- disambiguation can be ambiguous
when there is more than one tandem of pieces/pawns.

Closes #395.

setup.py
src/apiutil.h
src/pyffish.cpp
test.py

index a6937d1..a243af9 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.65",
+setup(name="pyffish", version="0.0.66",
       description="Fairy-Stockfish Python wrapper",
       long_description=long_description,
       long_description_content_type="text/markdown",
index c1e717d..0cd7bc7 100644 (file)
@@ -77,6 +77,15 @@ inline bool is_shogi(Notation n) {
     return n == NOTATION_SHOGI_HOSKING || n == NOTATION_SHOGI_HODGES || n == NOTATION_SHOGI_HODGES_NUMBER;
 }
 
+// is there more than one file with a pair of pieces?
+inline bool multi_tandem(Bitboard b) {
+    int tandems = 0;
+    for (File f = FILE_A; f <= FILE_MAX; ++f)
+        if (more_than_one(b & file_bb(f)))
+            tandems++;
+    return tandems >= 2;
+}
+
 inline std::string piece(const Position& pos, Move m, Notation n) {
     Color us = pos.side_to_move();
     Square from = from_sq(m);
@@ -86,7 +95,7 @@ inline std::string piece(const Position& pos, Move m, Notation n) {
     if ((n == NOTATION_SAN || n == NOTATION_LAN) && type_of(pc) == PAWN && type_of(m) != DROP)
         return "";
     // Tandem pawns
-    else if (n == NOTATION_XIANGQI_WXF && popcount(pos.pieces(us, pt) & file_bb(from)) > 2)
+    else if (n == NOTATION_XIANGQI_WXF && popcount(pos.pieces(us, pt) & file_bb(from)) >= 3 - multi_tandem(pos.pieces(us, pt)))
         return std::to_string(popcount(forward_file_bb(us, from) & pos.pieces(us, pt)) + 1);
     // Moves of promoted pieces
     else if (is_shogi(n) && type_of(m) != DROP && pos.unpromoted_piece_on(from))
@@ -129,6 +138,7 @@ inline std::string rank(const Position& pos, Square s, Notation n) {
     case NOTATION_XIANGQI_WXF:
     {
         if (pos.empty(s))
+            // Handle piece drops
             return std::to_string(relative_rank(pos.side_to_move(), s, pos.max_rank()) + 1);
         else if (pos.pieces(pos.side_to_move(), type_of(pos.piece_on(s))) & forward_file_bb(pos.side_to_move(), s))
             return "-";
@@ -169,7 +179,7 @@ inline Disambiguation disambiguation_level(const Position& pos, Move m, Notation
     if (n == NOTATION_XIANGQI_WXF)
     {
         // Disambiguate by rank (+/-) if target square of other piece is valid
-        if (popcount(pos.pieces(us, pt) & file_bb(from)) == 2)
+        if (popcount(pos.pieces(us, pt) & file_bb(from)) == 2 && !multi_tandem(pos.pieces(us, pt)))
         {
             Square otherFrom = lsb((pos.pieces(us, pt) & file_bb(from)) ^ from);
             Square otherTo = otherFrom + Direction(to) - Direction(from);
index ff1ced6..a80b216 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, 65);
+    return Py_BuildValue("(iii)", 0, 0, 66);
 }
 
 extern "C" PyObject* pyffish_info(PyObject* self) {
diff --git a/test.py b/test.py
index ff195a0..1f9e1b4 100644 (file)
--- a/test.py
+++ b/test.py
@@ -549,18 +549,33 @@ class TestPyffish(unittest.TestCase):
         self.assertEqual(result, "R1+1")
 
         # skip disambiguation for elephants and advisors, but not for pieces that require it
-        fen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/1NB6/P1P1P1P1P/1C1A3C1/9/RNBAK21R w - - 0 1"
+        fen = "rnbakabnr/9/1c5c1/p1p1p1p1p/4P4/1NB6/P1P1P3P/1C1A3C1/9/RNBAK4 w - - 0 1"
         result = sf.get_san("xiangqi", fen, "c5e3", False, sf.NOTATION_XIANGQI_WXF)
         self.assertEqual(result, "E7-5")
         result = sf.get_san("xiangqi", fen, "d1e2", False, sf.NOTATION_XIANGQI_WXF)
         self.assertEqual(result, "A6+5")
         result = sf.get_san("xiangqi", fen, "b5c7", False, sf.NOTATION_XIANGQI_WXF)
         self.assertEqual(result, "H++7")
+        result = sf.get_san("xiangqi", fen, "e6e7", False, sf.NOTATION_XIANGQI_WXF)
+        self.assertEqual(result, "P++1")
+        result = sf.get_san("xiangqi", fen, "e4e5", False, sf.NOTATION_XIANGQI_WXF)
+        self.assertEqual(result, "P-+1")
 
         # Tandem pawns
         fen = "rnbakabnr/9/1c5c1/p1p1P1p1p/4P4/9/P3P3P/1C5C1/9/RNBAKABNR w - - 0 1"
         result = sf.get_san("xiangqi", fen, "e7d7", False, sf.NOTATION_XIANGQI_WXF)
         self.assertEqual(result, "15=6")
+        result = sf.get_san("xiangqi", fen, "e6d6", False, sf.NOTATION_XIANGQI_WXF)
+        self.assertEqual(result, "25=6")
+        result = sf.get_san("xiangqi", fen, "e4e5", False, sf.NOTATION_XIANGQI_WXF)
+        self.assertEqual(result, "35+1")
+
+        # use tandem pawn notation for pair of tandem pawns
+        fen = "5k3/9/3P5/3P1P1P1/5P3/9/9/9/9/4K4 w - - 0 1"
+        result = sf.get_san("xiangqi", fen, "d7e7", False, sf.NOTATION_XIANGQI_WXF)
+        self.assertEqual(result, "26=5")
+        result = sf.get_san("xiangqi", fen, "f6e6", False, sf.NOTATION_XIANGQI_WXF)
+        self.assertEqual(result, "24=5")
 
         fen = "1rb1ka2r/4a4/2ncb1nc1/p1p1p1p1p/9/2P6/P3PNP1P/2N1C2C1/9/R1BAKAB1R w - - 1 7"
         result = sf.get_san("xiangqi", fen, "c3e2")