From 88d692e010f26f78903ef02a86ca1d90498b353a Mon Sep 17 00:00:00 2001 From: Ada Joule Date: Wed, 25 Jan 2023 03:54:19 +0700 Subject: [PATCH] Add typescript type definition file for ffish.js (#571) --- src/ffishjs.cpp | 10 +++++ src/pyffish.cpp | 18 +++++++++ test.py | 22 ++++++++++ tests/js/ffish.d.ts | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/js/package.json | 3 +- tests/js/test.js | 31 +++++++++++++++ 6 files changed, 185 insertions(+), 1 deletions(-) create mode 100644 tests/js/ffish.d.ts diff --git a/src/ffishjs.cpp b/src/ffishjs.cpp index 0e01ddf..c781511 100644 --- a/src/ffishjs.cpp +++ b/src/ffishjs.cpp @@ -175,6 +175,10 @@ public: return this->pos.fen(); } + std::string fen(bool showPromoted) const { + return this->pos.fen(false, showPromoted); + } + std::string fen(bool showPromoted, int countStarted) const { return this->pos.fen(false, showPromoted, countStarted); } @@ -332,6 +336,10 @@ public: return pos.bikjang(); } + bool is_capture(std::string uciMove) const { + return pos.capture(UCI::to_move(pos, uciMove)); + } + std::string move_stack() const { std::string moves; for(auto it = std::begin(moveStack); it != std::end(moveStack); ++it) { @@ -680,6 +688,7 @@ EMSCRIPTEN_BINDINGS(ffish_js) { .function("reset", &Board::reset) .function("is960", &Board::is_960) .function("fen", select_overload(&Board::fen)) + .function("fen", select_overload(&Board::fen)) .function("fen", select_overload(&Board::fen)) .function("setFen", &Board::set_fen) .function("sanMove", select_overload(&Board::san_move)) @@ -699,6 +708,7 @@ EMSCRIPTEN_BINDINGS(ffish_js) { .function("result", select_overload(&Board::result)) .function("isCheck", &Board::is_check) .function("isBikjang", &Board::is_bikjang) + .function("isCapture", &Board::is_capture) .function("moveStack", &Board::move_stack) .function("pushMoves", &Board::push_moves) .function("pushSanMoves", select_overload(&Board::push_san_moves)) diff --git a/src/pyffish.cpp b/src/pyffish.cpp index fbc0b36..0c186b2 100644 --- a/src/pyffish.cpp +++ b/src/pyffish.cpp @@ -262,6 +262,23 @@ extern "C" PyObject* pyffish_givesCheck(PyObject* self, PyObject *args) { return Py_BuildValue("O", Stockfish::is_check(pos) ? Py_True : Py_False); } +// INPUT variant, fen, move list, move +extern "C" PyObject* pyffish_isCapture(PyObject* self, PyObject *args) { + Position pos; + PyObject *moveList; + const char *variant, *fen, *move; + int chess960 = false; + if (!PyArg_ParseTuple(args, "ssO!s|p", &variant, &fen, &PyList_Type, &moveList, &move, &chess960)) { + return NULL; + } + + StateListPtr states(new std::deque(1)); + buildPosition(pos, states, variant, fen, moveList, chess960); + std::string moveStr = move; + + return Py_BuildValue("O", pos.capture(UCI::to_move(pos, moveStr)) ? Py_True : Py_False); +} + // INPUT variant, fen, move list // should only be called when the move list is empty extern "C" PyObject* pyffish_gameResult(PyObject* self, PyObject *args) { @@ -366,6 +383,7 @@ static PyMethodDef PyFFishMethods[] = { {"legal_moves", (PyCFunction)pyffish_legalMoves, METH_VARARGS, "Get legal moves from given FEN and movelist."}, {"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."}, {"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/test.py b/test.py index add64bc..c8101dc 100644 --- a/test.py +++ b/test.py @@ -863,6 +863,28 @@ class TestPyffish(unittest.TestCase): result = sf.gives_check("janggi", "4ka3/4a4/9/4R4/2B6/9/9/5K3/4p4/3r5 b - - 0 113", ["e2f2"]) self.assertTrue(result) + def test_is_capture(self): + result = sf.is_capture("chess", CHESS, [], "e2e4") + self.assertFalse(result) + + result = sf.is_capture("chess", CHESS, ["e2e4", "e7e5", "g1f3", "b8c6", "f1c4", "f8c5"], "e1g1") + self.assertFalse(result) + + result = sf.is_capture("chess", CHESS, ["e2e4", "g8f6", "e4e5", "d7d5"], "e5f6") + self.assertTrue(result) + + # en passant + result = sf.is_capture("chess", CHESS, ["e2e4", "g8f6", "e4e5", "d7d5"], "e5d6") + self.assertTrue(result) + + # 960 castling + result = sf.is_capture("chess", "bqrbkrnn/pppppppp/8/8/8/8/PPPPPPPP/BQRBKRNN w CFcf - 0 1", ["g1f3", "h8g6"], "e1f1", True) + self.assertFalse(result) + + # Sittuyin in-place promotion + result = sf.is_capture("sittuyin", "8/2k5/8/4P3/4P1N1/5K2/8/8[] w - - 0 1", [], "e5e5f") + self.assertFalse(result) + def test_game_result(self): result = sf.game_result("chess", CHESS, ["f2f3", "e7e5", "g2g4", "d8h4"]) self.assertEqual(result, -sf.VALUE_MATE) diff --git a/tests/js/ffish.d.ts b/tests/js/ffish.d.ts new file mode 100644 index 0000000..5f62852 --- /dev/null +++ b/tests/js/ffish.d.ts @@ -0,0 +1,102 @@ +export declare function Module(opts?: ModuleOptions): Promise; +export default Module; + +export interface ModuleOptions { + arguments?: string[]; + buffer?: ArrayBuffer | SharedArrayBuffer; + wasmMemory?: WebAssembly.Memory; + locateFile?: (file: string, prefix: string) => string; + logReadFiles?: boolean; + printWithColors?: boolean; + onAbort?: (status: string | number) => void; + onRuntimeInitialized?: (loadedModule: FairyStockfish) => void; + noExitRuntime?: boolean; + noInitialRun?: boolean; + preInit?: () => void | (() => void)[]; + preRun?: () => void | (() => void)[]; + print?: (text: string) => void; + printErr?: (text: string) => void; + mainScriptUrlOrBlob?: string; +} + +export interface FairyStockfish { + Board: Board; + Game: Game; + Notation: typeof Notation; + Termination: typeof Termination; + info(): string; + setOption(name: string, value: T): void; + setOptionInt(name: string, value: number): void; + setOptionBool(name: string, value: boolean): void; + readGamePGN(pgn: string): Game; + variants(): string; + loadVariantConfig(variantInitContent: string): void; + capturesToHand(uciVariant: string): boolean; + startingFen(uciVariant: string): string; + validateFen(fen: string, uciVariant?: string, chess960?: boolean): number; +} + +export interface Board { + new(uciVariant?: string, fen?: string, is960?: boolean): Board; + delete(): void; + legalMoves(): string; + legalMovesSan(): string; + numberLegalMoves(): number; + push(uciMove: string): boolean; + pushSan(sanMove: string, notation?: Notation): boolean; + pop(): void; + reset(): void; + is960(): boolean; + fen(showPromoted?: boolean, countStarted?: number): string; + setFen(fen: string): void; + sanMove(uciMove: string, notation?: Notation): string; + variationSan(uciMoves: string, notation?: Notation, moveNumbers?: boolean): string; + turn(): boolean; + fullmoveNumber(): number; + halfmoveClock(): number; + gamePly(): number; + hasInsufficientMaterial(turn: boolean): boolean; + isInsufficientMaterial(): boolean; + isGameOver(claimDraw?: boolean): boolean; + result(claimDraw?: boolean): string; + isCheck(): boolean; + isBikjang(): boolean; + isCapture(uciMove: string): boolean; + moveStack(): string; + pushMoves(uciMoves: string): void; + pushSanMoves(sanMoves: string, notation?: Notation): void; + pocket(color: boolean): string; + toString(): string; + toVerboseString(): string; + variant(): string; +} + +export interface Game { + delete(): void; + headerKeys(): string; + headers(item: string): string; + mainlineMoves(): string; +} + +export declare enum Notation { + DEFAULT, + SAN, + LAN, + SHOGI_HOSKING, + SHOGI_HODGES, + SHOGI_HODGES_NUMBER, + JANGGI, + XIANGQI_WXF, + THAI_SAN, + THAI_LAN, +} + +export declare enum Termination { + ONGOING, + CHECKMATE, + STALEMATE, + INSUFFICIENT_MATERIAL, + N_MOVE_RULE, + N_FOLD_REPETITION, + VARIANT_END, +} diff --git a/tests/js/package.json b/tests/js/package.json index a58e74d..cfbd5cb 100644 --- a/tests/js/package.json +++ b/tests/js/package.json @@ -1,8 +1,9 @@ { "name": "ffish", - "version": "0.7.2", + "version": "0.7.3", "description": "A high performance WebAssembly chess variant library based on Fairy-Stockfish", "main": "ffish.js", + "types": "ffish.d.ts", "scripts": { "test": "mocha --timeout 80000", "dev": "node index" diff --git a/tests/js/test.js b/tests/js/test.js index 12465c9..2a6d08d 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -200,6 +200,14 @@ describe('board.fen()', function () { }); }); +describe('board.fen(showPromoted)', function () { + it("it returns the current position in fen format. showPromoted makes promoted pieces always followed by the symbol ~ regardless of variant.", () => { + let board = new ffish.Board("makruk", "8/6ks/3M~2r1/2K1M3/8/3R4/8/8 w - 128 18 50"); + chai.expect(board.fen(true)).to.equal("8/6ks/3M~2r1/2K1M3/8/3R4/8/8 w - 128 18 50"); + chai.expect(board.fen(false)).to.equal("8/6ks/3M2r1/2K1M3/8/3R4/8/8 w - 128 18 50"); + }); +}); + describe('board.fen(showPromoted, countStarted)', function () { it("it returns the current position in fen format. showPromoted makes promoted pieces always followed by the symbol ~ regardless of variant. countStarted overwrites the start of makruk's board honor counting.", () => { let board = new ffish.Board("makruk", "8/6ks/3M~2r1/2K1M3/8/3R4/8/8 w - 128 18 50"); @@ -500,6 +508,29 @@ describe('board.isBikjang()', function () { }); }); +describe('board.isCapture(move)', function() { + it("it checks if a move is a capture", () => { + let board = new ffish.Board(); + chai.expect(board.isCapture("e2e4")).to.equal(false); + board.pushMoves("e2e4 e7e5 g1f3 b8c6 f1c4 f8c5"); + chai.expect(board.isCapture("e1g1")).to.equal(false); + board.reset(); + board.pushMoves("e2e4 g8f6 e4e5 d7d5"); + chai.expect(board.isCapture("e5f6")).to.equal(true); + chai.expect(board.isCapture("e5d6")).to.equal(true); + board.delete(); + + board = new ffish.Board("chess", "bqrbkrnn/pppppppp/8/8/8/8/PPPPPPPP/BQRBKRNN w CFcf - 0 1", true); + board.pushMoves("g1f3 h8g6"); + chai.expect(board.isCapture("e1f1")).to.equal(false); + board.delete(); + + board = new ffish.Board("sittuyin", "8/2k5/8/4P3/4P1N1/5K2/8/8[] w - - 0 1"); + chai.expect(board.isCapture("e5e5f")).to.equal(false); + board.delete(); + }); +}); + describe('board.moveStack()', function () { it("it returns the move stack in UCI notation", () => { let board = new ffish.Board(); -- 1.7.0.4