diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 7ab1809c..6bfabf55 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -45,7 +45,7 @@ jobs: - name: Download required packages run: | sudo apt update - sudo apt install expect valgrind g++-multilib + sudo apt install expect valgrind g++-multilib llvm - name: Download the used network from the fishtest framework run: | @@ -73,6 +73,12 @@ jobs: make -j2 ARCH=x86-32 optimize=no debug=yes build ../tests/signature.sh $benchref + - name: Test debug x86-32 profile build (Optimization on, All features) + run: | + export CXXFLAGS="" + make clean + make -j2 ARCH=x86-32 optimize=yes all=yes largeboards=yes debug=yes profile-build + - name: Test x86-32 build run: | make clean @@ -106,6 +112,12 @@ jobs: make -j2 ARCH=x86-64-modern optimize=no debug=yes build ../tests/signature.sh $benchref + - name: Test debug x86-64-modern profile build (Optimization on, All features) + run: | + export CXXFLAGS="" + make clean + make -j2 ARCH=x86-64-modern optimize=yes all=yes largeboards=yes debug=yes profile-build + - name: Test x86-64-modern build run: | make clean diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index ed14ee97..1704e23e 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -14,7 +14,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, windows-2019, macos-11] + os: [ubuntu-20.04, windows-2019, macos-12] steps: - uses: actions/checkout@v3 diff --git a/setup.py b/setup.py index fa72818d..050ae435 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ sources=sources, extra_compile_args=args) -setup(name="pyffish", version="0.0.82", +setup(name="pyffish", version="0.0.84", description="Fairy-Stockfish Python wrapper", long_description=long_description, long_description_content_type="text/markdown", diff --git a/src/Makefile b/src/Makefile index 689be029..8fc1b168 100644 --- a/src/Makefile +++ b/src/Makefile @@ -542,7 +542,7 @@ endif ### 3.3 Optimization ifeq ($(optimize),yes) - CXXFLAGS += -O3 + CXXFLAGS += -O3 -fno-strict-aliasing ifeq ($(comp),gcc) ifeq ($(OS), Android) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 39c153ca..b2bbc148 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1297,6 +1297,15 @@ namespace { // Connect-n if (pos.connect_n() > 0) { + //Calculate eligible pieces for connection once. + //Still consider all opponent pieces as blocking. + Bitboard connectPiecesUs = 0; + for (PieceSet ps = pos.connect_piece_types(); ps;){ + PieceType pt = pop_lsb(ps); + connectPiecesUs |= pos.pieces(pt); + }; + connectPiecesUs &= pos.pieces(Us); + for (const Direction& d : pos.getConnectDirections()) { @@ -1310,7 +1319,7 @@ namespace { Square s = pop_lsb(b); int c = 0; for (int j = 0; j < pos.connect_n(); j++) - if (pos.pieces(Us) & (s - j * d)) + if (connectPiecesUs & (s - j * d)) c++; score += make_score(200, 200) * c / (pos.connect_n() - c) / (pos.connect_n() - c); } diff --git a/src/position.cpp b/src/position.cpp index 061385de..285b8422 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -2893,7 +2893,7 @@ bool Position::is_immediate_game_end(Value& result, int ply) const { } // Check for bikjang rule (Janggi), double passing, or board running full - if ( (st->pliesFromNull > 0 && ((st->bikjang && st->previous->bikjang) || (st->pass && st->previous->pass))) + if ( (st->pliesFromNull > 0 && ((st->bikjang && st->previous->bikjang) || ((st->pass && st->previous->pass)&&!var->wallOrMove))) || (var->adjudicateFullBoard && !(~pieces() & board_bb()))) { result = var->materialCounting ? convert_mate_value(material_counting_result(), ply) : VALUE_DRAW; diff --git a/src/position.h b/src/position.h index e3aa715a..65dfee1a 100644 --- a/src/position.h +++ b/src/position.h @@ -282,6 +282,7 @@ class Position { bool gives_check(Move m) const; Piece moved_piece(Move m) const; Piece captured_piece() const; + const std::string piece_to_partner() const; // Piece specific bool pawn_passed(Color c, Square s) const; @@ -1064,7 +1065,7 @@ inline int Position::connect_n() const { inline PieceSet Position::connect_piece_types() const { assert(var != nullptr); - return var->connectPieceTypes; + return var->connectPieceTypesTrimmed; } inline bool Position::connect_horizontal() const { @@ -1435,6 +1436,15 @@ inline Piece Position::captured_piece() const { return st->capturedPiece; } +inline const std::string Position::piece_to_partner() const { + if (!st->capturedPiece) return std::string(); + Color color = color_of(st->capturedPiece); + Piece piece = st->capturedpromoted ? + (st->unpromotedCapturedPiece ? st->unpromotedCapturedPiece : make_piece(color, promotion_pawn_type(color))) : + st->capturedPiece; + return std::string(1, piece_to_char()[piece]); +} + inline Thread* Position::this_thread() const { return thisThread; } diff --git a/src/pyffish.cpp b/src/pyffish.cpp index fb8094f2..ed1f487a 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, 82); + return Py_BuildValue("(iii)", 0, 0, 84); } extern "C" PyObject* pyffish_info(PyObject* self) { @@ -279,6 +279,21 @@ extern "C" PyObject* pyffish_isCapture(PyObject* self, PyObject *args) { return Py_BuildValue("O", pos.capture(UCI::to_move(pos, moveStr)) ? Py_True : Py_False); } +// INPUT variant, fen, move list +extern "C" PyObject* pyffish_pieceToPartner(PyObject* self, PyObject *args) { + PyObject *moveList; + Position pos; + const char *fen, *variant; + int chess960 = false; + if (!PyArg_ParseTuple(args, "ssO!|p", &variant, &fen, &PyList_Type, &moveList, &chess960)) { + return NULL; + } + + StateListPtr states(new std::deque(1)); + buildPosition(pos, states, variant, fen, moveList, chess960); + return Py_BuildValue("s", pos.piece_to_partner().c_str()); +} + // INPUT variant, fen, move list // should only be called when the move list is empty extern "C" PyObject* pyffish_gameResult(PyObject* self, PyObject *args) { @@ -384,6 +399,7 @@ static PyMethodDef PyFFishMethods[] = { {"get_fen", (PyCFunction)pyffish_getFEN, METH_VARARGS, "Get resulting FEN from given FEN and movelist."}, {"gives_check", (PyCFunction)pyffish_givesCheck, METH_VARARGS, "Get check status from given FEN and movelist."}, {"is_capture", (PyCFunction)pyffish_isCapture, METH_VARARGS, "Get whether given move is a capture from given FEN and movelist."}, + {"piece_to_partner", (PyCFunction)pyffish_pieceToPartner, METH_VARARGS, "Get unpromoted captured piece from given FEN and movelist."}, {"game_result", (PyCFunction)pyffish_gameResult, METH_VARARGS, "Get result from given FEN, considering variant end, checkmate, and stalemate."}, {"is_immediate_game_end", (PyCFunction)pyffish_isImmediateGameEnd, METH_VARARGS, "Get result from given FEN if variant rules ends the game."}, {"is_optional_game_end", (PyCFunction)pyffish_isOptionalGameEnd, METH_VARARGS, "Get result from given FEN it rules enable game end by player."}, diff --git a/src/variant.cpp b/src/variant.cpp index 59b0ec59..b9b451d0 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -2067,15 +2067,16 @@ Variant* Variant::conclude() { connect_directions.push_back(SOUTH_EAST); } - // If not a connect variant, set connectPieceTypes to no pieces. + // If not a connect variant, set connectPieceTypesTrimmed to no pieces. + // connectPieceTypesTrimmed is separated so that connectPieceTypes is left unchanged for inheritance. if ( !(connectRegion1[WHITE] || connectRegion1[BLACK] || connectN || connectNxN || collinearN) ) { - connectPieceTypes = NO_PIECE_SET; + connectPieceTypesTrimmed = NO_PIECE_SET; } //Otherwise optimize to pieces actually in the game. else { - connectPieceTypes = connectPieceTypes & pieceTypes; + connectPieceTypesTrimmed = connectPieceTypes & pieceTypes; }; return this; diff --git a/src/variant.h b/src/variant.h index f5602a6d..f444325f 100644 --- a/src/variant.h +++ b/src/variant.h @@ -180,7 +180,7 @@ struct Variant { bool endgameEval = false; bool shogiStylePromotions = false; std::vector connect_directions; - + PieceSet connectPieceTypesTrimmed = ~NO_PIECE_SET; void add_piece(PieceType pt, char c, std::string betza = "", char c2 = ' ') { // Avoid ambiguous definition by removing existing piece with same letter size_t idx; diff --git a/src/variants.ini b/src/variants.ini index 6cfa80ef..4e0bd836 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -1823,8 +1823,10 @@ pieceToCharTable = PNBRQ.............MKpnbrq.............mk customPiece1 = r:RmF customPiece2 = n:NmK customPiece3 = b:BmW -customPiece1 = m:KAD +customPiece4 = m:KAD promotionPieceTypes = mqnbr +castling = true +castlingRookPieces = r startFen = rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[MMmm] w KQkq - 0 1 pieceDrops = true whiteDropRegion = *1 @@ -1973,3 +1975,44 @@ flagPiece = k flagRegionWhite = *8 flagRegionBlack = *1 startFen = lhatkahl/ssssssss/8/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1 + +[muje] +variantTemplate = shogi +pieceToCharTable = PNBR.F.IS..+Kpnbr.f.is..+k +maxFile = 9 +maxRank = 9 +pocketSize = 7 +startFen = rnbfkfbnr/1si3is1/p1p1p1p1p/9/9/9/P1P1P1P1P/1SI3IS1/RNBFKFBNR[] w - - 0 1 +pieceDrops = true +capturesToHand = true +soldier = p +knight = n +bishop = b +rook = r +fers = f +wazir = w +king = k +customPiece1 = i:pB +customPiece2 = s:pR +promotionRegionWhite = *7 *8 *9 +promotionRegionBlack = *3 *2 *1 +promotedPieceType = p:w +doubleStep = false +castling = false +stalemateValue = loss +nMoveRule = 0 +nFoldValue = loss +perpetualCheckIllegal = true +mobilityRegionWhiteKing = *3 *2 *1 +mobilityRegionBlackKing = *7 *8 *9 +mandatoryPiecePromotion = true + +#https://www.chessvariants.com/diffobjective.dir/giveaway.html +#When stalemate, the stalemated player does not move but the opponent can if he wish to play for win go on moving and do as many moves he wants to do. +#If the stalemate then disappear, both players move again as usual. So, if white for example has a pawn on h2 and nothing more and black a pawn on h3, pawn on a7 and rooks on a8 and h8 +# black can win by moving : 1.-,Rh4 2.-, a5. 3.-, a4 4.- Ra5 5.-,a3 6.-,a2 7.-,a1=R 8.-,Rg1 9.-,Rg3 10.hxg3,h2 11.gxh4,Rg5 12.hxg5,h1=Q 13.g6,Qh7 14.gxh7 and black has won. +[andersgiveaway:giveaway] +passOnStalemate = true + +[andersanti:antichess] +passOnStalemate = true diff --git a/test.py b/test.py index 7a5aa456..bef4e5f6 100644 --- a/test.py +++ b/test.py @@ -974,6 +974,27 @@ def test_is_capture(self): result = sf.is_capture("sittuyin", "8/2k5/8/4P3/4P1N1/5K2/8/8[] w - - 0 1", [], "e5e5f") self.assertFalse(result) + def test_piece_to_partner(self): + # take the rook and promote to queen + result = sf.piece_to_partner("bughouse", "r2qkbnr/1Ppppppp/2n5/8/8/8/1PPPPPPP/RNBQKBNR[] w KQkq - 0 1", ["b7a8q"]) + self.assertEqual(result, "r") + + # take back the queen (promoted pawn) + result = sf.piece_to_partner("bughouse", "r2qkbnr/1Ppppppp/2n5/8/8/8/1PPPPPPP/RNBQKBNR[] w KQkq - 0 1", ["b7a8q", "d8a8"]) + self.assertEqual(result, "P") + + # just a simple move (no take) + result = sf.piece_to_partner("bughouse", "r2qkbnr/1Ppppppp/2n5/8/8/8/1PPPPPPP/RNBQKBNR[] w KQkq - 0 1", ["b7a8q", "d8b8"]) + self.assertEqual(result, "") + + # silver takes the pawn and promotes to gold + result = sf.piece_to_partner("shogi", "lnsgkgsnl/1r5b1/ppppppppp/S8/9/9/PPPPPPPPP/1B5R1/LNSGKG1NL[] w 0 1", ["a6a7+"]) + self.assertEqual(result, "p") + + # take back the gold (promoted silver) + result = sf.piece_to_partner("shogi", "lnsgkgsnl/1r5b1/ppppppppp/S8/9/9/PPPPPPPPP/1B5R1/LNSGKG1NL[] w 0 1", ["a6a7+", "a9a7"]) + self.assertEqual(result, "S") + def test_game_result(self): result = sf.game_result("chess", CHESS, ["f2f3", "e7e5", "g2g4", "d8h4"]) self.assertEqual(result, -sf.VALUE_MATE)