From: Ada Joule Date: Sun, 2 Apr 2023 19:25:37 +0000 (+0700) Subject: Add Board.checkedPieces() to ffish.js (#626) X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=80280a0dcb4431065ee5a0fbb4e3a246fe572179;p=fairystockfish.git Add Board.checkedPieces() to ffish.js (#626) --- diff --git a/src/apiutil.h b/src/apiutil.h index 6f36579..988ff5e 100644 --- a/src/apiutil.h +++ b/src/apiutil.h @@ -407,9 +407,9 @@ inline bool has_insufficient_material(Color c, const Position& pos) { return true; } -inline bool is_check(const Position& pos) { - return pos.checkers() - || (pos.extinction_pseudo_royal() && pos.attackers_to_pseudo_royals(~pos.side_to_move())); +inline Bitboard checked(const Position& pos) { + return (pos.checkers() ? square_bb(pos.square(pos.side_to_move())) : Bitboard(0)) + | (pos.extinction_pseudo_royal() ? pos.checked_pseudo_royals(pos.side_to_move()) : Bitboard(0)); } namespace FEN { diff --git a/src/ffishjs.cpp b/src/ffishjs.cpp index c781511..7e4f26d 100644 --- a/src/ffishjs.cpp +++ b/src/ffishjs.cpp @@ -328,8 +328,20 @@ public: return "0-1"; } + std::string checked_pieces() const { + Bitboard checked = Stockfish::checked(pos); + std::string squares; + while (checked) { + Square sr = pop_lsb(checked); + squares += UCI::square(pos, sr); + squares += DELIM; + } + save_pop_back(squares); + return squares; + } + bool is_check() const { - return Stockfish::is_check(pos); + return Stockfish::checked(pos); } bool is_bikjang() const { @@ -706,6 +718,7 @@ EMSCRIPTEN_BINDINGS(ffish_js) { .function("isGameOver", select_overload(&Board::is_game_over)) .function("result", select_overload(&Board::result)) .function("result", select_overload(&Board::result)) + .function("checkedPieces", &Board::checked_pieces) .function("isCheck", &Board::is_check) .function("isBikjang", &Board::is_bikjang) .function("isCapture", &Board::is_capture) diff --git a/src/position.cpp b/src/position.cpp index b1f9082..72afba8 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -983,41 +983,39 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const { return attackers_to(s, occupied, WHITE) | attackers_to(s, occupied, BLACK); } -/// Position::attackers_to_pseudo_royals computes a bitboard of all pieces -/// of a particular color attacking at least one opposing pseudo-royal piece -Bitboard Position::attackers_to_pseudo_royals(Color c) const { +/// Position::checked_pseudo_royals computes a bitboard of +/// all pseudo-royal pieces of a particular color that are in check +Bitboard Position::checked_pseudo_royals(Color c) const { assert(extinction_pseudo_royal()); - Bitboard attackers = 0; - Bitboard pseudoRoyals = st->pseudoRoyals & pieces(~c); - Bitboard pseudoRoyalsTheirs = st->pseudoRoyals & pieces(c); + Bitboard checked = 0; + Bitboard pseudoRoyals = st->pseudoRoyals & pieces(c); + Bitboard pseudoRoyalsTheirs = st->pseudoRoyals & pieces(~c); while (pseudoRoyals) { Square sr = pop_lsb(pseudoRoyals); - if ( blast_on_capture() - && pseudoRoyalsTheirs & attacks_bb(sr)) - // skip if capturing this piece would blast all of the attacker's pseudo-royal pieces - continue; - attackers |= attackers_to(sr, c); + // skip if capturing this piece would blast any of the attacker's pseudo-royal pieces + if (!(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb(sr))) + && attackers_to(sr, ~c)) + checked |= sr; } // Look for duple check if (var->dupleCheck) { - Bitboard b; - Bitboard allAttackers = 0; - Bitboard pseudoRoyalCandidates = st->pseudoRoyalCandidates & pieces(~c); + Bitboard allAttacked = 0; + Bitboard pseudoRoyalCandidates = st->pseudoRoyalCandidates & pieces(c); while (pseudoRoyalCandidates) { Square sr = pop_lsb(pseudoRoyalCandidates); if (!(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb(sr))) - && (b = attackers_to(sr, c))) - allAttackers |= b; + && attackers_to(sr, ~c)) + allAttacked |= sr; else // If at least one isn't attacked, it is not a duple check - return attackers; + return checked; } - attackers |= allAttackers; + checked |= allAttacked; } - return attackers; + return checked; } diff --git a/src/position.h b/src/position.h index 2fe0b7e..7fe9951 100644 --- a/src/position.h +++ b/src/position.h @@ -246,7 +246,7 @@ public: Bitboard blockers_for_king(Color c) const; Bitboard check_squares(PieceType pt) const; Bitboard pinners(Color c) const; - Bitboard attackers_to_pseudo_royals(Color c) const; + Bitboard checked_pseudo_royals(Color c) const; // Attacks to/from a given square Bitboard attackers_to(Square s) const; diff --git a/src/pyffish.cpp b/src/pyffish.cpp index 09a8a54..537148c 100644 --- a/src/pyffish.cpp +++ b/src/pyffish.cpp @@ -259,7 +259,7 @@ extern "C" PyObject* pyffish_givesCheck(PyObject* self, PyObject *args) { StateListPtr states(new std::deque(1)); buildPosition(pos, states, variant, fen, moveList, chess960); - return Py_BuildValue("O", Stockfish::is_check(pos) ? Py_True : Py_False); + return Py_BuildValue("O", Stockfish::checked(pos) ? Py_True : Py_False); } // INPUT variant, fen, move list, move diff --git a/tests/js/ffish.d.ts b/tests/js/ffish.d.ts index 5f62852..f92fff4 100644 --- a/tests/js/ffish.d.ts +++ b/tests/js/ffish.d.ts @@ -59,6 +59,7 @@ export interface Board { isInsufficientMaterial(): boolean; isGameOver(claimDraw?: boolean): boolean; result(claimDraw?: boolean): string; + checkedPieces(): string; isCheck(): boolean; isBikjang(): boolean; isCapture(uciMove: string): boolean; diff --git a/tests/js/test.js b/tests/js/test.js index 0f268c1..88f4c59 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -474,6 +474,51 @@ describe('board.result()', function () { }) }) +describe('board.checkedPieces()', function () { + it("it returns the squares of all checked royal pieces in a concatenated string", () => { + let board = new ffish.Board(); + chai.expect(board.checkedPieces()).to.equal(""); + board.setFen("rnbqkb1r/pppp1Bpp/5n2/4p3/4P3/8/PPPP1PPP/RNBQK1NR b KQkq - 0 3"); + chai.expect(board.checkedPieces()).to.equal("e8"); + board.setFen("r1bqkb1r/pppp1ppp/2n2n2/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR w KQkq - 4 4"); + board.pushSan("Qxf7#"); + chai.expect(board.checkedPieces()).to.equal("e8"); + board.delete(); + + board = new ffish.Board("atomic"); + chai.expect(board.checkedPieces()).to.equal(""); + board.setFen("rnbqkbnr/ppp1pppp/8/1B1p4/4P3/8/PPPP1PPP/RNBQK1NR b KQkq - 3 2"); + chai.expect(board.checkedPieces()).to.equal("e8"); + board.setFen("rnbqkbnr/ppp2ppp/8/8/8/8/PPP2PPP/RNBQKBNR w KQkq - 0 4"); + board.pushSan("Qd7"); + chai.expect(board.checkedPieces()).to.equal("e8"); + board.setFen("8/8/kK6/8/8/8/Q7/8 b - - 0 1") + chai.expect(board.checkedPieces()).to.equal(""); + board.delete(); + + board = new ffish.Board("spartan"); + chai.expect(board.checkedPieces()).to.equal(""); + board.setFen("lgkcckw1/hhhhhhhh/1N3lN1/8/8/8/PPPPPPPP/R1BQKB1R b KQ - 11 6"); + chai.expect(board.checkedPieces().split(' ').sort().join()).to.equal("c8,f8"); + chai.expect(board.isCheck()).to.equal(true); + board.setFen("lgkcckwl/hhhhhhhh/6N1/8/8/8/PPPPPPPP/RNBQKB1R b KQ - 5 3") + chai.expect(board.checkedPieces()).to.equal(""); + board.delete(); + + board = new ffish.Board("shako"); + board.setFen("10/5r4/2p3pBk1/1p6Pr/p3p5/9e/1PP2P4/P2P2PP2/ER3K2R1/8C1 w K - 7 38") + board.pushMoves("f2h2"); + chai.expect(board.checkedPieces()).to.equal("i8"); + board.delete(); + + board = new ffish.Board("janggi"); + board.setFen("4ka3/4a4/9/4R4/2B6/9/9/5K3/4p4/3r5 b - - 0 113") + board.pushMoves("e2f2"); + chai.expect(board.checkedPieces()).to.equal("f3"); + board.delete(); + }) +}) + describe('board.isCheck()', function () { it("it checks if a player is in check", () => { let board = new ffish.Board(); @@ -495,8 +540,29 @@ describe('board.isCheck()', function () { board.setFen("8/8/kK6/8/8/8/Q7/8 b - - 0 1") chai.expect(board.isCheck()).to.equal(false); board.delete(); - }); -}); + + board = new ffish.Board("spartan"); + board.setFen("lgkcckw1/hhhhhhhh/1N3lN1/8/8/8/PPPPPPPP/R1BQKB1R b KQ - 11 6"); + chai.expect(board.isCheck()).to.equal(true); + board.setFen("lgkcckwl/hhhhhhhh/6N1/8/8/8/PPPPPPPP/RNBQKB1R b KQ - 5 3") + chai.expect(board.isCheck()).to.equal(false); + board.setFen("lgkcckwl/hhhhhhhh/8/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1") + chai.expect(board.isCheck()).to.equal(false); + board.delete(); + + board = new ffish.Board("shako"); + board.setFen("10/5r4/2p3pBk1/1p6Pr/p3p5/9e/1PP2P4/P2P2PP2/ER3K2R1/8C1 w K - 7 38") + board.pushMoves("f2h2"); + chai.expect(board.isCheck()).to.equal(true); + board.delete(); + + board = new ffish.Board("janggi"); + board.setFen("4ka3/4a4/9/4R4/2B6/9/9/5K3/4p4/3r5 b - - 0 113") + board.pushMoves("e2f2"); + chai.expect(board.isCheck()).to.equal(true); + board.delete(); + }) +}) describe('board.isBikjang()', function () { it("it checks if a player is in bikjang (only relevant for janggi)", () => {