From 8517100f15a0f499da3237615eb5b86090c889b8 Mon Sep 17 00:00:00 2001 From: Fabian Fichter Date: Sat, 6 Nov 2021 11:14:01 +0100 Subject: [PATCH] Use tandem pawn notation for double tandems In WXF notation +/- disambiguation can be ambiguous when there is more than one tandem of pieces/pawns. Closes #395. --- setup.py | 2 +- src/apiutil.h | 14 ++++++++++++-- src/pyffish.cpp | 2 +- test.py | 17 ++++++++++++++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index a6937d1..a243af9 100644 --- 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", diff --git a/src/apiutil.h b/src/apiutil.h index c1e717d..0cd7bc7 100644 --- a/src/apiutil.h +++ b/src/apiutil.h @@ -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); diff --git a/src/pyffish.cpp b/src/pyffish.cpp index ff1ced6..a80b216 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, 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 --- 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") -- 1.7.0.4