Skip to content

Commit

Permalink
Implement fog_fen()
Browse files Browse the repository at this point in the history
  • Loading branch information
gbtami committed Nov 20, 2024
1 parent fbde758 commit 29b41d7
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 2 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
37 changes: 37 additions & 0 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<LEGAL>(*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
Expand Down
1 change: 1 addition & 0 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
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;
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.fog_fen().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 @@ -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)

0 comments on commit 29b41d7

Please sign in to comment.