Skip to content

Commit

Permalink
Implement fog_fen() (#840)
Browse files Browse the repository at this point in the history
  • Loading branch information
gbtami authored Dec 29, 2024
1 parent 60c2975 commit c94d110
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 6 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -690,15 +690,15 @@ 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)
ss << emptyCnt;

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)))
Expand Down
17 changes: 16 additions & 1 deletion src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<LEGAL>(*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);
Expand Down
19 changes: 18 additions & 1 deletion src/pyffish.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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<StateInfo>(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."},
Expand All @@ -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
};

Expand Down
17 changes: 17 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

0 comments on commit c94d110

Please sign in to comment.