diff --git a/setup.py b/setup.py index 050ae4359..9a5474675 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 5278c0157..a8594f8ad 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -825,6 +825,43 @@ string Position::fen(bool sfen, bool showPromoted, int countStarted, std::string } +/// Position::fog_fen() returns a Fog of War (dark chess) FEN representation +/// of the position. Squares where current player can't move (from or to) are +/// filled with NO_PIECE (wall squares) + +string Position::fog_fen(bool sfen, bool showPromoted, int countStarted, std::string holdings) { + Color us = sideToMove; + Color them = ~us; + Bitboard fog; + + // Our own pieces are visible + Bitboard visible = pieces(us); + + // 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 + fog = ~visible & board_bb(); + + // Fill in invisible squares with walls + Bitboard occupied = pieces(them); + while (fog) + { + Square sq = pop_lsb(fog); + if (piece_on(sq)) remove_piece(sq); + + st->wallSquares |= sq; + byTypeBB[ALL_PIECES] |= sq; + } + + return fen(sfen, showPromoted, countStarted, holdings); +} + + /// Position::slider_blockers() returns a bitboard of all the pieces (both colors) /// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a /// slider if removing that piece from the board would result in a position where diff --git a/src/position.h b/src/position.h index 8868d7646..039cbc3de 100644 --- a/src/position.h +++ b/src/position.h @@ -115,6 +115,7 @@ class Position { 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 fog_fen(bool sfen = false, bool showPromoted = false, int countStarted = 0, std::string holdings = "-"); // Variant rule properties const Variant* variant() const; diff --git a/src/pyffish.cpp b/src/pyffish.cpp index ed1f487a3..cc6b0b373 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; + 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.fog_fen().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 bef4e5f6e..79ff6f670 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) @@ -1151,5 +1158,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)