diff --git a/setup.py b/setup.py index bcbd9e481..d8aa3ea9c 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ sources=sources, extra_compile_args=args) -setup(name="pyffish", version="0.0.84", +setup(name="pyffish", version="0.0.85", description="Fairy-Stockfish Python wrapper", long_description=long_description, long_description_content_type="text/markdown", diff --git a/src/position.cpp b/src/position.cpp index 81fa3c774..bd4adc828 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -681,7 +681,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) { /// Position::fen() returns a FEN representation of the position. In case of /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. -string Position::fen(bool sfen, bool showPromoted, int countStarted, std::string holdings) const { +string Position::fen(bool sfen, bool showPromoted, int countStarted, std::string holdings, Bitboard fogArea) const { int emptyCnt; std::ostringstream ss; @@ -690,7 +690,7 @@ string Position::fen(bool sfen, bool showPromoted, int countStarted, std::string { for (File f = FILE_A; f <= max_file(); ++f) { - for (emptyCnt = 0; f <= max_file() && !(pieces() & make_square(f, r)); ++f) + for (emptyCnt = 0; f <= max_file() && !(pieces() & make_square(f, r)) && !(fogArea & make_square(f, r)); ++f) ++emptyCnt; if (emptyCnt) @@ -698,7 +698,7 @@ string Position::fen(bool sfen, bool showPromoted, int countStarted, std::string if (f <= max_file()) { - if (empty(make_square(f, r))) + if (empty(make_square(f, r)) || fogArea & make_square(f, r)) // Wall square ss << "*"; else if (unpromoted_piece_on(make_square(f, r))) diff --git a/src/position.h b/src/position.h index 8868d7646..9818ede2b 100644 --- a/src/position.h +++ b/src/position.h @@ -114,7 +114,7 @@ class Position { // FEN string input/output Position& set(const Variant* v, const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th, bool sfen = false); Position& set(const std::string& code, Color c, StateInfo* si); - std::string fen(bool sfen = false, bool showPromoted = false, int countStarted = 0, std::string holdings = "-") const; + std::string fen(bool sfen = false, bool showPromoted = false, int countStarted = 0, std::string holdings = "-", Bitboard fogArea = 0) const; // Variant rule properties const Variant* variant() const; @@ -328,6 +328,7 @@ class Position { Score psq_score() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; + Bitboard fog_area() const; // Position consistency check, for debugging bool pos_is_ok() const; @@ -1407,6 +1408,20 @@ inline Piece Position::captured_piece() const { return st->capturedPiece; } +inline Bitboard Position::fog_area() const { + Bitboard b = board_bb(); + // Our own pieces are visible + Bitboard visible = pieces(sideToMove); + // Squares where we can move to are visible as well + for (const auto& m : MoveList(*this)) + { + Square to = to_sq(m); + visible |= to; + } + // Everything else is invisible + return ~visible & b; +} + inline const std::string Position::piece_to_partner() const { if (!st->capturedPiece) return std::string(); Color color = color_of(st->capturedPiece); diff --git a/src/pyffish.cpp b/src/pyffish.cpp index ed1f487a3..0c297190e 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, 84); + return Py_BuildValue("(iii)", 0, 0, 85); } extern "C" PyObject* pyffish_info(PyObject* self) { @@ -383,6 +383,22 @@ extern "C" PyObject* pyffish_validateFen(PyObject* self, PyObject *args) { return Py_BuildValue("i", FEN::validate_fen(std::string(fen), variants.find(std::string(variant))->second, chess960)); } +// INPUT variant, fen +extern "C" PyObject* pyffish_getFogFEN(PyObject* self, PyObject *args) { + PyObject* moveList = PyList_New(0); + Position pos; + const char *fen, *variant; + + int chess960 = false, sfen = false, showPromoted = false, countStarted = 0; + if (!PyArg_ParseTuple(args, "ss|p", &fen, &variant, &chess960)) { + return NULL; + } + StateListPtr states(new std::deque(1)); + buildPosition(pos, states, variant, fen, moveList, chess960); + + Py_XDECREF(moveList); + return Py_BuildValue("s", pos.fen(sfen, showPromoted, countStarted, "-", pos.fog_area()).c_str()); +} static PyMethodDef PyFFishMethods[] = { {"version", (PyCFunction)pyffish_version, METH_NOARGS, "Get package version."}, @@ -405,6 +421,7 @@ static PyMethodDef PyFFishMethods[] = { {"is_optional_game_end", (PyCFunction)pyffish_isOptionalGameEnd, METH_VARARGS, "Get result from given FEN it rules enable game end by player."}, {"has_insufficient_material", (PyCFunction)pyffish_hasInsufficientMaterial, METH_VARARGS, "Checks for insufficient material."}, {"validate_fen", (PyCFunction)pyffish_validateFen, METH_VARARGS, "Validate an input FEN."}, + {"get_fog_fen", (PyCFunction)pyffish_getFogFEN, METH_VARARGS, "Get Fog of War FEN from given FEN."}, {NULL, NULL, 0, NULL}, // sentinel }; diff --git a/test.py b/test.py index 03f656b64..0999589d7 100644 --- a/test.py +++ b/test.py @@ -113,6 +113,13 @@ customPiece5 = f:mBpBmWpR2 promotedPieceType = u:w a:w c:f i:f startFen = lnsgkgsnl/1rci1uab1/p1p1p1p1p/9/9/9/P1P1P1P1P/1BAU1ICR1/LNSGKGSNL[-] w 0 1 + +[fogofwar:chess] +king = - +commoner = k +castlingKingPiece = k +extinctionValue = loss +extinctionPieceTypes = k """ sf.load_variant_config(ini_text) @@ -1156,5 +1163,15 @@ def test_validate_fen(self): fen = sf.start_fen(variant) self.assertEqual(sf.validate_fen(fen, variant), sf.FEN_OK) + def test_get_fog_fen(self): + fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" # startpos + result = sf.get_fog_fen(fen, "fogofwar") + self.assertEqual(result, "********/********/********/********/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") + + fen = "rnbqkbnr/p1p2ppp/8/Pp1pp3/4P3/8/1PPP1PPP/RNBQKBNR w KQkq b6 0 1" + result = sf.get_fog_fen(fen, "fogofwar") + self.assertEqual(result, "********/********/2******/Pp*p***1/4P3/4*3/1PPP1PPP/RNBQKBNR w KQkq b6 0 1") + + if __name__ == '__main__': unittest.main(verbosity=2)