Add typescript type definition file for ffish.js (#571)
authorAda Joule <ada.fulmina@gmail.com>
Tue, 24 Jan 2023 20:54:19 +0000 (03:54 +0700)
committerGitHub <noreply@github.com>
Tue, 24 Jan 2023 20:54:19 +0000 (21:54 +0100)
src/ffishjs.cpp
src/pyffish.cpp
test.py
tests/js/ffish.d.ts [new file with mode: 0644]
tests/js/package.json
tests/js/test.js

index 0e01ddf..c781511 100644 (file)
@@ -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<std::string()const>(&Board::fen))
+    .function("fen", select_overload<std::string(bool)const>(&Board::fen))
     .function("fen", select_overload<std::string(bool, int)const>(&Board::fen))
     .function("setFen", &Board::set_fen)
     .function("sanMove", select_overload<std::string(std::string)>(&Board::san_move))
@@ -699,6 +708,7 @@ EMSCRIPTEN_BINDINGS(ffish_js) {
     .function("result", select_overload<std::string(bool) const>(&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<void(std::string)>(&Board::push_san_moves))
index fbc0b36..0c186b2 100644 (file)
@@ -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<StateInfo>(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 (file)
--- 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 (file)
index 0000000..5f62852
--- /dev/null
@@ -0,0 +1,102 @@
+export declare function Module(opts?: ModuleOptions): Promise<FairyStockfish>;
+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<T>(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,
+}
index a58e74d..cfbd5cb 100644 (file)
@@ -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"
index 12465c9..2a6d08d 100644 (file)
@@ -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();