hyperchess-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +83 -0
  3. package/dist/board/create.d.ts +13 -0
  4. package/dist/board/create.d.ts.map +1 -0
  5. package/dist/board/create.js +21 -0
  6. package/dist/board/create.js.map +1 -0
  7. package/dist/board/hfen.d.ts +31 -0
  8. package/dist/board/hfen.d.ts.map +1 -0
  9. package/dist/board/hfen.js +181 -0
  10. package/dist/board/hfen.js.map +1 -0
  11. package/dist/board/index.d.ts +6 -0
  12. package/dist/board/index.d.ts.map +1 -0
  13. package/dist/board/index.js +11 -0
  14. package/dist/board/index.js.map +1 -0
  15. package/dist/board/move.d.ts +16 -0
  16. package/dist/board/move.d.ts.map +1 -0
  17. package/dist/board/move.js +43 -0
  18. package/dist/board/move.js.map +1 -0
  19. package/dist/board/undo.d.ts +4 -0
  20. package/dist/board/undo.d.ts.map +1 -0
  21. package/dist/board/undo.js +22 -0
  22. package/dist/board/undo.js.map +1 -0
  23. package/dist/board/validate.d.ts +7 -0
  24. package/dist/board/validate.d.ts.map +1 -0
  25. package/dist/board/validate.js +14 -0
  26. package/dist/board/validate.js.map +1 -0
  27. package/dist/game/add-move.d.ts +7 -0
  28. package/dist/game/add-move.d.ts.map +1 -0
  29. package/dist/game/add-move.js +78 -0
  30. package/dist/game/add-move.js.map +1 -0
  31. package/dist/game/index.d.ts +21 -0
  32. package/dist/game/index.d.ts.map +1 -0
  33. package/dist/game/index.js +58 -0
  34. package/dist/game/index.js.map +1 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +19 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/io/hsan-exporter.d.ts +10 -0
  40. package/dist/io/hsan-exporter.d.ts.map +1 -0
  41. package/dist/io/hsan-exporter.js +70 -0
  42. package/dist/io/hsan-exporter.js.map +1 -0
  43. package/dist/io/hsan-parser.d.ts +12 -0
  44. package/dist/io/hsan-parser.d.ts.map +1 -0
  45. package/dist/io/hsan-parser.js +118 -0
  46. package/dist/io/hsan-parser.js.map +1 -0
  47. package/dist/io/index.d.ts +3 -0
  48. package/dist/io/index.d.ts.map +1 -0
  49. package/dist/io/index.js +8 -0
  50. package/dist/io/index.js.map +1 -0
  51. package/dist/moves/index.d.ts +22 -0
  52. package/dist/moves/index.d.ts.map +1 -0
  53. package/dist/moves/index.js +39 -0
  54. package/dist/moves/index.js.map +1 -0
  55. package/dist/standalone.d.ts +64 -0
  56. package/dist/standalone.d.ts.map +1 -0
  57. package/dist/standalone.js +323 -0
  58. package/dist/standalone.js.map +1 -0
  59. package/dist/types/board.d.ts +38 -0
  60. package/dist/types/board.d.ts.map +1 -0
  61. package/dist/types/board.js +7 -0
  62. package/dist/types/board.js.map +1 -0
  63. package/dist/types/game.d.ts +45 -0
  64. package/dist/types/game.d.ts.map +1 -0
  65. package/dist/types/game.js +7 -0
  66. package/dist/types/game.js.map +1 -0
  67. package/dist/types/move.d.ts +24 -0
  68. package/dist/types/move.d.ts.map +1 -0
  69. package/dist/types/move.js +7 -0
  70. package/dist/types/move.js.map +1 -0
  71. package/dist/types/piece.d.ts +50 -0
  72. package/dist/types/piece.d.ts.map +1 -0
  73. package/dist/types/piece.js +29 -0
  74. package/dist/types/piece.js.map +1 -0
  75. package/dist/utils/square-notation.d.ts +14 -0
  76. package/dist/utils/square-notation.d.ts.map +1 -0
  77. package/dist/utils/square-notation.js +27 -0
  78. package/dist/utils/square-notation.js.map +1 -0
  79. package/dist/wasm/engine.d.ts +23 -0
  80. package/dist/wasm/engine.d.ts.map +1 -0
  81. package/dist/wasm/engine.js +37 -0
  82. package/dist/wasm/engine.js.map +1 -0
  83. package/dist/wasm/snapshot.d.ts +47 -0
  84. package/dist/wasm/snapshot.d.ts.map +1 -0
  85. package/dist/wasm/snapshot.js +125 -0
  86. package/dist/wasm/snapshot.js.map +1 -0
  87. package/package.json +74 -0
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # hyperchess-core
2
+
3
+ Pure HyperChess game logic with zero dependencies. Works on all platforms.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install hyperchess-core
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { createBoard, getBoardHfen } from 'hyperchess-core';
15
+
16
+ // Create a board from starting position
17
+ const board = createBoard();
18
+
19
+ // Export to HFEN-I notation
20
+ const hfen = getBoardHfen(board);
21
+ console.log(hfen); // 12/abcdefghijkl/...
22
+
23
+ // Or create from custom HFEN
24
+ const customBoard = createBoard(hfen);
25
+ ```
26
+
27
+ ## API
28
+
29
+ ### Board Operations
30
+ - `createBoard(hfen?: string): Board` - Create board from HFEN or default
31
+ - `applyMove(board: Board, move: Move): Board` - Apply move (returns new board)
32
+ - `isLegalMove(board: Board, move: Move): boolean` - Check move validity
33
+ - `undoMove(board: Board): Board` - Undo last move
34
+ - `getBoardHfen(board: Board): string` - Export to HFEN-I notation
35
+
36
+ ### Move Generation
37
+ - `generateLegalMoves(board: Board): Move[]` - All legal moves
38
+ - `isInCheck(board: Board): boolean` - Is current player in check?
39
+ - `isCheckmate(board: Board): boolean` - Is checkmate?
40
+
41
+ ### Game State
42
+ - `createGame(hfen?: string): Game` - Start new game
43
+ - `addMove(game: Game, move: string): Game | null` - Add move to game
44
+ - `removeLastMove(game: Game): Game` - Undo move
45
+ - `getGameStatus(game: Game): GameStatus` - Current status
46
+
47
+ ## Architecture
48
+
49
+ ```
50
+ src/
51
+ ├── types/ # Type definitions (no logic)
52
+ │ ├── board.ts # Board state
53
+ │ ├── move.ts # Move types
54
+ │ ├── piece.ts # Piece types
55
+ │ └── game.ts # Game state
56
+ ├── board/ # Board operations
57
+ │ ├── create.ts # HFEN parsing
58
+ │ ├── move.ts # Move application
59
+ │ ├── validate.ts # Legality checking
60
+ │ ├── undo.ts # Undo logic
61
+ │ └── hfen.ts # HFEN export
62
+ ├── moves/ # Move generation
63
+ │ └── index.ts # generateLegalMoves, checks
64
+ ├── game/ # Game state machine
65
+ │ └── index.ts # createGame, addMove, status
66
+ └── index.ts # Public API exports
67
+ ```
68
+
69
+ ## Tree-Shaking
70
+
71
+ All exports are separate files, allowing bundlers to eliminate unused code:
72
+
73
+ ```typescript
74
+ // Only import what you need
75
+ import { createBoard } from 'hyperchess-core/board';
76
+ import { generateLegalMoves } from 'hyperchess-core/moves';
77
+
78
+ // Unused piece types are not bundled
79
+ ```
80
+
81
+ ## License
82
+
83
+ MIT
@@ -0,0 +1,13 @@
1
+ import { Board } from '../types/board';
2
+ /**
3
+ * Create a board from HFEN-I notation (or the default starting position if
4
+ * `hfen` is omitted).
5
+ *
6
+ * Delegates parsing entirely to the Rust engine via WASM — this used to be
7
+ * a hand-written parser here that never implemented HFEN-I identity decoding
8
+ * and mis-parsed multi-digit empty-square runs, so `createBoard()` with no
9
+ * arguments (the SDK's own default starting position!) threw on every call.
10
+ * See docs/sdk-plan/WASM-MIGRATION-PLAN.md for the full history.
11
+ */
12
+ export declare function createBoard(hfen?: string): Board;
13
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/board/create.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAGhD"}
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-core
3
+ // File: packages/core/src/board/create.ts
4
+ // Version: 1.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ import { loadWasmBoard, snapshotFromWasmBoard } from '../wasm/engine';
7
+ /**
8
+ * Create a board from HFEN-I notation (or the default starting position if
9
+ * `hfen` is omitted).
10
+ *
11
+ * Delegates parsing entirely to the Rust engine via WASM — this used to be
12
+ * a hand-written parser here that never implemented HFEN-I identity decoding
13
+ * and mis-parsed multi-digit empty-square runs, so `createBoard()` with no
14
+ * arguments (the SDK's own default starting position!) threw on every call.
15
+ * See docs/sdk-plan/WASM-MIGRATION-PLAN.md for the full history.
16
+ */
17
+ export function createBoard(hfen) {
18
+ const wasmBoard = loadWasmBoard(hfen);
19
+ return snapshotFromWasmBoard(wasmBoard);
20
+ }
21
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/board/create.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,oCAAoC;AACpC,0CAA0C;AAC1C,iBAAiB;AACjB,+CAA+C;AAG/C,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEtE;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { Board } from '../types/board';
2
+ import { Piece, PieceType } from '../types/piece';
3
+ /** The starting type of an identity character, or `undefined` if it is not one. */
4
+ export declare function identityStartType(identity: string): PieceType | undefined;
5
+ /** True when `c` is a legal HFEN-I identity character (`A`-`X` / `a`-`x`). */
6
+ export declare function isIdentityChar(c: string): boolean;
7
+ /**
8
+ * Parse the position field of a HFEN/HFEN-I string into 144 squares.
9
+ *
10
+ * Index 0 is a1 and index 143 is l12, matching `Board.pieces`. Ranks in the
11
+ * string run 12 first down to 1 last.
12
+ *
13
+ * Handles the three token shapes the format allows, and nothing else:
14
+ * an inline `id:Type` pair (ONE square), a single piece/identity character,
15
+ * and a multi-digit empty-square run. Multi-digit runs matter — `12` is one
16
+ * fully empty rank, not two squares.
17
+ *
18
+ * Throws on a malformed field rather than returning a partly-filled board:
19
+ * a position that cannot be read is an error, never a default.
20
+ */
21
+ export declare function parseHfenPosition(position: string): (Piece | undefined)[];
22
+ /**
23
+ * Export a board to HFEN-I notation.
24
+ *
25
+ * Identity-preserving whenever the board carries identity. This function is
26
+ * fed straight back into the engine by `generateLegalMoves`, `applyMove` and
27
+ * friends, so a type-only implementation here silently erased piece identity
28
+ * on every single move the SDK made.
29
+ */
30
+ export declare function getBoardHfen(board: Board): string;
31
+ //# sourceMappingURL=hfen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hfen.d.ts","sourceRoot":"","sources":["../../src/board/hfen.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAkBlD,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAEzE;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,CA2EzE;AAuBD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAuDjD"}
@@ -0,0 +1,181 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-core
3
+ // File: packages/core/src/board/hfen.ts
4
+ // Version: 2.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ /**
7
+ * HFEN-I identity character → the piece type that identity *starts* as.
8
+ *
9
+ * Mirrors `piece_from_identity` in `crates/hyperchess-rules/src/core/
10
+ * piece_identity.rs`; the two must agree. Note that the identity alphabet and
11
+ * the type alphabet overlap while meaning different things: as an identity,
12
+ * `Q` is the white pawn from f3 and `K` the white hawk from k2.
13
+ */
14
+ const IDENTITY_START_TYPE = {
15
+ A: 'E', B: 'H', C: 'R', D: 'N', E: 'B', F: 'Q', G: 'K', H: 'B', I: 'N', J: 'R', K: 'H', L: 'E',
16
+ M: 'P', N: 'P', O: 'P', P: 'P', Q: 'P', R: 'P', S: 'P', T: 'P', U: 'P', V: 'P', W: 'P', X: 'P',
17
+ };
18
+ /** Type letters legal in the right half of an inline `id:Type` pair. */
19
+ const PROMOTION_TYPES = 'QRBNEH';
20
+ /** The starting type of an identity character, or `undefined` if it is not one. */
21
+ export function identityStartType(identity) {
22
+ return IDENTITY_START_TYPE[identity.toUpperCase()];
23
+ }
24
+ /** True when `c` is a legal HFEN-I identity character (`A`-`X` / `a`-`x`). */
25
+ export function isIdentityChar(c) {
26
+ return c.length === 1 && IDENTITY_START_TYPE[c.toUpperCase()] !== undefined;
27
+ }
28
+ /**
29
+ * Parse the position field of a HFEN/HFEN-I string into 144 squares.
30
+ *
31
+ * Index 0 is a1 and index 143 is l12, matching `Board.pieces`. Ranks in the
32
+ * string run 12 first down to 1 last.
33
+ *
34
+ * Handles the three token shapes the format allows, and nothing else:
35
+ * an inline `id:Type` pair (ONE square), a single piece/identity character,
36
+ * and a multi-digit empty-square run. Multi-digit runs matter — `12` is one
37
+ * fully empty rank, not two squares.
38
+ *
39
+ * Throws on a malformed field rather than returning a partly-filled board:
40
+ * a position that cannot be read is an error, never a default.
41
+ */
42
+ export function parseHfenPosition(position) {
43
+ const squares = new Array(144).fill(undefined);
44
+ const ranks = position.split('/');
45
+ if (ranks.length !== 12) {
46
+ throw new Error(`HFEN position needs 12 ranks, got ${ranks.length}`);
47
+ }
48
+ // Identity mode is on when any character is identity-only — i.e. an
49
+ // identity letter that is not also a legacy type letter. This mirrors
50
+ // `position_uses_identity` in the Rust engine.
51
+ const identityMode = /[ACDFGIJLMOSTUVWXacdfgijlmostuvwx]/.test(position);
52
+ ranks.forEach((rankStr, rankFromTop) => {
53
+ const rank = 11 - rankFromTop;
54
+ let file = 0;
55
+ const tokens = rankStr.match(/[A-Za-z]:[A-Za-z]|[A-Za-z]|\d+/g) ?? [];
56
+ // A rank whose characters did not all get consumed is malformed.
57
+ if (tokens.join('') !== rankStr) {
58
+ throw new Error(`Malformed HFEN rank ${rank + 1}: ${rankStr}`);
59
+ }
60
+ for (const token of tokens) {
61
+ if (/^\d+$/.test(token)) {
62
+ file += parseInt(token, 10);
63
+ continue;
64
+ }
65
+ if (file >= 12) {
66
+ throw new Error(`Too many squares on HFEN rank ${rank + 1}`);
67
+ }
68
+ let identity;
69
+ let typeChar;
70
+ if (token.length === 3) {
71
+ identity = token[0];
72
+ typeChar = token[2];
73
+ if (!PROMOTION_TYPES.includes(typeChar.toUpperCase())) {
74
+ throw new Error(`Invalid \`id:Type\` type letter '${typeChar}' in ${token}: the type half must be ` +
75
+ `one of ${PROMOTION_TYPES.split('').join(' ')}`);
76
+ }
77
+ const identityIsWhite = identity === identity.toUpperCase();
78
+ const typeIsWhite = typeChar === typeChar.toUpperCase();
79
+ if (identityIsWhite !== typeIsWhite) {
80
+ throw new Error(`Type override ${token} changes piece color`);
81
+ }
82
+ }
83
+ else if (identityMode && isIdentityChar(token)) {
84
+ identity = token;
85
+ typeChar = token;
86
+ }
87
+ else {
88
+ typeChar = token;
89
+ }
90
+ const color = typeChar === typeChar.toUpperCase() ? 'white' : 'black';
91
+ const type = token.length === 3 || !identity
92
+ ? typeChar.toUpperCase()
93
+ : identityStartType(identity);
94
+ if (!type) {
95
+ throw new Error(`Invalid HFEN piece character: ${token}`);
96
+ }
97
+ squares[rank * 12 + file] = identity ? { type, color, identity } : { type, color };
98
+ file += 1;
99
+ }
100
+ if (file !== 12) {
101
+ throw new Error(`HFEN rank ${rank + 1} has ${file} squares, expected 12`);
102
+ }
103
+ });
104
+ return squares;
105
+ }
106
+ /**
107
+ * Serialize one square for the position field.
108
+ *
109
+ * A piece carrying identity writes its identity letter alone when that letter
110
+ * already parses back to the piece's current type, and an inline `id:Type`
111
+ * pair when it does not — which in practice means after a promotion. This is
112
+ * what keeps the string self-contained: both *which* piece it is and *what*
113
+ * it currently is live inside the position field, with no side table.
114
+ */
115
+ function serializeSquare(piece) {
116
+ const typeChar = piece.color === 'white' ? piece.type : piece.type.toLowerCase();
117
+ if (!piece.identity) {
118
+ return typeChar;
119
+ }
120
+ if (identityStartType(piece.identity) === piece.type) {
121
+ return piece.identity;
122
+ }
123
+ return `${piece.identity}:${typeChar}`;
124
+ }
125
+ /**
126
+ * Export a board to HFEN-I notation.
127
+ *
128
+ * Identity-preserving whenever the board carries identity. This function is
129
+ * fed straight back into the engine by `generateLegalMoves`, `applyMove` and
130
+ * friends, so a type-only implementation here silently erased piece identity
131
+ * on every single move the SDK made.
132
+ */
133
+ export function getBoardHfen(board) {
134
+ // Piece placement. Standard FEN convention: highest rank first, so we walk
135
+ // rank indices 11 → 0 — within each rank, files still run 0 → 11.
136
+ const rankSegments = [];
137
+ for (let rank = 11; rank >= 0; rank--) {
138
+ let segment = '';
139
+ let emptyCount = 0;
140
+ for (let file = 0; file < 12; file++) {
141
+ const piece = board.pieces[rank * 12 + file];
142
+ if (!piece) {
143
+ emptyCount++;
144
+ }
145
+ else {
146
+ if (emptyCount > 0) {
147
+ segment += emptyCount;
148
+ emptyCount = 0;
149
+ }
150
+ segment += serializeSquare(piece);
151
+ }
152
+ }
153
+ if (emptyCount > 0)
154
+ segment += emptyCount;
155
+ rankSegments.push(segment);
156
+ }
157
+ let hfen = rankSegments.join('/');
158
+ // Side to move
159
+ hfen += ` ${board.toMove === 'white' ? 'w' : 'b'}`;
160
+ // Castling rights
161
+ const castling = (board.castlingRights.whiteKingSide ? 'K' : '') +
162
+ (board.castlingRights.whiteQueenSide ? 'Q' : '') +
163
+ (board.castlingRights.blackKingSide ? 'k' : '') +
164
+ (board.castlingRights.blackQueenSide ? 'q' : '');
165
+ hfen += ` ${castling || '-'}`;
166
+ // En passant
167
+ if (board.enPassantSquare === -1) {
168
+ hfen += ' -';
169
+ }
170
+ else {
171
+ const col = String.fromCharCode('a'.charCodeAt(0) + (board.enPassantSquare % 12));
172
+ const row = Math.floor(board.enPassantSquare / 12) + 1;
173
+ hfen += ` ${col}${row}`;
174
+ }
175
+ // Halfmove clock
176
+ hfen += ` ${board.halfmoveClock}`;
177
+ // Fullmove number
178
+ hfen += ` ${board.fullmoveNumber}`;
179
+ return hfen;
180
+ }
181
+ //# sourceMappingURL=hfen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hfen.js","sourceRoot":"","sources":["../../src/board/hfen.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,oCAAoC;AACpC,wCAAwC;AACxC,iBAAiB;AACjB,+CAA+C;AAM/C;;;;;;;GAOG;AACH,MAAM,mBAAmB,GAA8B;IACrD,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG;IAC9F,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG;CAC/F,CAAC;AAEF,wEAAwE;AACxE,MAAM,eAAe,GAAG,QAAQ,CAAC;AAEjC,mFAAmF;AACnF,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,mBAAmB,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,mBAAmB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,SAAS,CAAC;AAC9E,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,OAAO,GAA0B,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,oEAAoE;IACpE,sEAAsE;IACtE,+CAA+C;IAC/C,MAAM,YAAY,GAAG,oCAAoC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEzE,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,EAAE,GAAG,WAAW,CAAC;QAC9B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,IAAI,EAAE,CAAC;QAEtE,iEAAiE;QACjE,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC5B,SAAS;YACX,CAAC;YACD,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/D,CAAC;YAED,IAAI,QAA4B,CAAC;YACjC,IAAI,QAAgB,CAAC;YAErB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACpB,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACpB,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBACtD,MAAM,IAAI,KAAK,CACb,oCAAoC,QAAQ,QAAQ,KAAK,0BAA0B;wBACjF,UAAU,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAClD,CAAC;gBACJ,CAAC;gBACD,MAAM,eAAe,GAAG,QAAQ,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC5D,MAAM,WAAW,GAAG,QAAQ,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;gBACxD,IAAI,eAAe,KAAK,WAAW,EAAE,CAAC;oBACpC,MAAM,IAAI,KAAK,CAAC,iBAAiB,KAAK,sBAAsB,CAAC,CAAC;gBAChE,CAAC;YACH,CAAC;iBAAM,IAAI,YAAY,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,QAAQ,GAAG,KAAK,CAAC;gBACjB,QAAQ,GAAG,KAAK,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,QAAQ,GAAG,KAAK,CAAC;YACnB,CAAC;YAED,MAAM,KAAK,GAAU,QAAQ,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7E,MAAM,IAAI,GACR,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ;gBAC7B,CAAC,CAAE,QAAQ,CAAC,WAAW,EAAgB;gBACvC,CAAC,CAAE,iBAAiB,CAAC,QAAQ,CAAe,CAAC;YAEjD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,EAAE,CAAC,CAAC;YAC5D,CAAC;YAED,OAAO,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YACnF,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;QAED,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAC,KAAY;IACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAEjF,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;QACrD,OAAO,KAAK,CAAC,QAAQ,CAAC;IACxB,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAY;IACvC,2EAA2E;IAC3E,kEAAkE;IAClE,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,KAAK,IAAI,IAAI,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;QACtC,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,UAAU,EAAE,CAAC;YACf,CAAC;iBAAM,CAAC;gBACN,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;oBACnB,OAAO,IAAI,UAAU,CAAC;oBACtB,UAAU,GAAG,CAAC,CAAC;gBACjB,CAAC;gBACD,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAED,IAAI,UAAU,GAAG,CAAC;YAAE,OAAO,IAAI,UAAU,CAAC;QAC1C,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAElC,eAAe;IACf,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;IAEnD,kBAAkB;IAClB,MAAM,QAAQ,GACZ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACnD,IAAI,IAAI,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;IAE9B,aAAa;IACb,IAAI,KAAK,CAAC,eAAe,KAAK,CAAC,CAAC,EAAE,CAAC;QACjC,IAAI,IAAI,IAAI,CAAC;IACf,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC,CAAC;QAClF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QACvD,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;IAC1B,CAAC;IAED,iBAAiB;IACjB,IAAI,IAAI,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;IAElC,kBAAkB;IAClB,IAAI,IAAI,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;IAEnC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { createBoard } from './create';
2
+ export { applyMove } from './move';
3
+ export { isLegalMove } from './validate';
4
+ export { undoMove } from './undo';
5
+ export { getBoardHfen } from './hfen';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC"}
@@ -0,0 +1,11 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-core
3
+ // File: packages/core/src/board/index.ts
4
+ // Version: 1.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ export { createBoard } from './create';
7
+ export { applyMove } from './move';
8
+ export { isLegalMove } from './validate';
9
+ export { undoMove } from './undo';
10
+ export { getBoardHfen } from './hfen';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,oCAAoC;AACpC,yCAAyC;AACzC,iBAAiB;AACjB,+CAA+C;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { Board } from '../types/board';
2
+ import { Move } from '../types/move';
3
+ /**
4
+ * Apply a move to the board, returning a new board state.
5
+ * Does NOT validate legality - use isLegalMove() first.
6
+ *
7
+ * Delegates to the Rust engine via WASM — this used to hand-roll castling
8
+ * rook relocation, en passant capture removal, and castling-rights
9
+ * invalidation here, which drifted out of sync with the real rules more than
10
+ * once (see docs/sdk-plan/WASM-MIGRATION-PLAN.md). Round-trips through HFEN
11
+ * on each call rather than keeping a live `WasmBoard` across calls, trading
12
+ * some overhead for keeping `Board` a plain, directly-inspectable snapshot
13
+ * object everywhere else in the SDK.
14
+ */
15
+ export declare function applyMove(board: Board, move: Move): Board;
16
+ //# sourceMappingURL=move.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"move.d.ts","sourceRoot":"","sources":["../../src/board/move.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAIrC;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,KAAK,CAyBzD"}
@@ -0,0 +1,43 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-core
3
+ // File: packages/core/src/board/move.ts
4
+ // Version: 1.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ import { getBoardHfen } from './hfen';
7
+ import { loadWasmBoard, snapshotFromWasmBoard, moveToUci } from '../wasm/engine';
8
+ /**
9
+ * Apply a move to the board, returning a new board state.
10
+ * Does NOT validate legality - use isLegalMove() first.
11
+ *
12
+ * Delegates to the Rust engine via WASM — this used to hand-roll castling
13
+ * rook relocation, en passant capture removal, and castling-rights
14
+ * invalidation here, which drifted out of sync with the real rules more than
15
+ * once (see docs/sdk-plan/WASM-MIGRATION-PLAN.md). Round-trips through HFEN
16
+ * on each call rather than keeping a live `WasmBoard` across calls, trading
17
+ * some overhead for keeping `Board` a plain, directly-inspectable snapshot
18
+ * object everywhere else in the SDK.
19
+ */
20
+ export function applyMove(board, move) {
21
+ const piece = board.pieces[move.from];
22
+ if (!piece) {
23
+ throw new Error(`No piece at source square ${move.from}`);
24
+ }
25
+ const wasmBoard = loadWasmBoard(getBoardHfen(board));
26
+ const applied = wasmBoard.apply_move(moveToUci(move));
27
+ if (!applied) {
28
+ throw new Error(`Illegal move: ${move.from} -> ${move.to}`);
29
+ }
30
+ const history = [
31
+ ...board.history,
32
+ {
33
+ pieces: [...board.pieces],
34
+ toMove: board.toMove,
35
+ enPassantSquare: board.enPassantSquare,
36
+ castlingRights: { ...board.castlingRights },
37
+ halfmoveClock: board.halfmoveClock,
38
+ fullmoveNumber: board.fullmoveNumber,
39
+ },
40
+ ];
41
+ return snapshotFromWasmBoard(wasmBoard, history);
42
+ }
43
+ //# sourceMappingURL=move.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"move.js","sourceRoot":"","sources":["../../src/board/move.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,oCAAoC;AACpC,wCAAwC;AACxC,iBAAiB;AACjB,+CAA+C;AAI/C,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEjF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,KAAY,EAAE,IAAU;IAChD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,OAAO,GAAG;QACd,GAAG,KAAK,CAAC,OAAO;QAChB;YACE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;YACzB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,cAAc,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE;YAC3C,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;SACrC;KACF,CAAC;IAEF,OAAO,qBAAqB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Board } from '../types/board';
2
+ /** Undo the last move */
3
+ export declare function undoMove(board: Board): Board;
4
+ //# sourceMappingURL=undo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/board/undo.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC,yBAAyB;AACzB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAgB5C"}
@@ -0,0 +1,22 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-core
3
+ // File: packages/core/src/board/undo.ts
4
+ // Version: 1.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ /** Undo the last move */
7
+ export function undoMove(board) {
8
+ if (board.history.length === 0) {
9
+ throw new Error('No moves to undo');
10
+ }
11
+ const lastState = board.history[board.history.length - 1];
12
+ return {
13
+ pieces: lastState.pieces,
14
+ toMove: lastState.toMove,
15
+ enPassantSquare: lastState.enPassantSquare,
16
+ castlingRights: lastState.castlingRights,
17
+ halfmoveClock: lastState.halfmoveClock,
18
+ fullmoveNumber: lastState.fullmoveNumber,
19
+ history: board.history.slice(0, -1),
20
+ };
21
+ }
22
+ //# sourceMappingURL=undo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.js","sourceRoot":"","sources":["../../src/board/undo.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,oCAAoC;AACpC,wCAAwC;AACxC,iBAAiB;AACjB,+CAA+C;AAI/C,yBAAyB;AACzB,MAAM,UAAU,QAAQ,CAAC,KAAY;IACnC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE1D,OAAO;QACL,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,eAAe,EAAE,SAAS,CAAC,eAAe;QAC1C,cAAc,EAAE,SAAS,CAAC,cAAc;QACxC,aAAa,EAAE,SAAS,CAAC,aAAa;QACtC,cAAc,EAAE,SAAS,CAAC,cAAc;QACxC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Board } from '../types/board';
2
+ import { Move } from '../types/move';
3
+ /**
4
+ * Check if a move is legal (in the set of all legal moves for the position)
5
+ */
6
+ export declare function isLegalMove(board: Board, move: Move): boolean;
7
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/board/validate.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrC;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAI7D"}
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-core
3
+ // File: packages/core/src/board/validate.ts
4
+ // Version: 1.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ import { generateLegalMoves } from '../moves/index';
7
+ /**
8
+ * Check if a move is legal (in the set of all legal moves for the position)
9
+ */
10
+ export function isLegalMove(board, move) {
11
+ const legalMoves = generateLegalMoves(board);
12
+ return legalMoves.some((m) => m.from === move.from && m.to === move.to);
13
+ }
14
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/board/validate.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,oCAAoC;AACpC,4CAA4C;AAC5C,iBAAiB;AACjB,+CAA+C;AAI/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAY,EAAE,IAAU;IAClD,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAE7C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Game } from '../types/game';
2
+ /**
3
+ * Add a move to the game (in algebraic or HSAN notation)
4
+ * Returns new Game state or null if move is invalid
5
+ */
6
+ export declare function addMoveToGame(game: Game, moveStr: string): Game | null;
7
+ //# sourceMappingURL=add-move.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-move.d.ts","sourceRoot":"","sources":["../../src/game/add-move.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,IAAI,EAAc,MAAM,eAAe,CAAC;AAOjD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAoDtE"}
@@ -0,0 +1,78 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // HyperChess Core — hyperchess-core
3
+ // File: packages/core/src/game/add-move.ts
4
+ // Version: 1.0.0
5
+ // Copyright (c) 2026 HyperChess Developer Team
6
+ import { generateLegalMoves, isCheckmate, isStalemate } from '../moves/index';
7
+ import { applyMove } from '../board/move';
8
+ import { parseHsanMove } from '../io/hsan-parser';
9
+ import { SQUARE_TOKEN, algebraicToSquare, squareToAlgebraic } from '../utils/square-notation';
10
+ /**
11
+ * Add a move to the game (in algebraic or HSAN notation)
12
+ * Returns new Game state or null if move is invalid
13
+ */
14
+ export function addMoveToGame(game, moveStr) {
15
+ const board = game.board;
16
+ // Generate all legal moves
17
+ const legalMoves = generateLegalMoves(board);
18
+ if (legalMoves.length === 0) {
19
+ // Game already ended
20
+ return null;
21
+ }
22
+ // Try to parse as HSAN first, then as algebraic
23
+ let move = null;
24
+ // Try HSAN parsing
25
+ const hsanMove = parseHsanMove(board, moveStr);
26
+ if (hsanMove) {
27
+ move = hsanMove;
28
+ }
29
+ else {
30
+ // Try as algebraic (e.g., "e2e4" or "e2-e4")
31
+ move = parseAlgebraicMove(moveStr);
32
+ }
33
+ if (!move) {
34
+ return null; // Invalid move format
35
+ }
36
+ // Check if move is in legal moves
37
+ const isLegal = legalMoves.some((m) => m.from === move.from && m.to === move.to);
38
+ if (!isLegal) {
39
+ return null; // Illegal move
40
+ }
41
+ // Apply the move
42
+ const newBoard = applyMove(board, move);
43
+ // Determine new game status
44
+ let status = 'active';
45
+ if (isCheckmate(newBoard)) {
46
+ status = 'checkmate';
47
+ }
48
+ else if (isStalemate(newBoard)) {
49
+ status = 'stalemate';
50
+ }
51
+ // Add move to history (in algebraic format)
52
+ const algebraicMove = `${squareToAlgebraic(move.from)}${squareToAlgebraic(move.to)}${move.promotion ? '=' + move.promotion : ''}`;
53
+ return {
54
+ board: newBoard,
55
+ moves: [...game.moves, algebraicMove],
56
+ metadata: game.metadata,
57
+ status,
58
+ };
59
+ }
60
+ /**
61
+ * Parse algebraic move notation (e.g., "e2e4", "e2-e4", "e3e11").
62
+ *
63
+ * Ranks 10-12 are two digits on this 12x12 board, so the from/to squares
64
+ * can't be sliced at fixed offsets — each is matched as a variable-length
65
+ * file+rank token.
66
+ */
67
+ function parseAlgebraicMove(moveStr) {
68
+ const normalized = moveStr.replace('-', '');
69
+ const match = normalized.match(new RegExp(`^(${SQUARE_TOKEN})(${SQUARE_TOKEN})(?:=([QRBNEH]))?$`));
70
+ if (!match) {
71
+ return null;
72
+ }
73
+ const from = algebraicToSquare(match[1]);
74
+ const to = algebraicToSquare(match[2]);
75
+ const promotion = match[3];
76
+ return { from, to, promotion };
77
+ }
78
+ //# sourceMappingURL=add-move.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-move.js","sourceRoot":"","sources":["../../src/game/add-move.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { Game, GameStatus } from '../types/game';
2
+ import { HfenString } from '../types/board';
3
+ /**
4
+ * Create a new game from optional starting HFEN
5
+ */
6
+ export declare function createGame(hfen?: HfenString): Game;
7
+ /**
8
+ * Add a move to the game (in algebraic or HSAN notation)
9
+ * Returns new Game state or null if move is invalid
10
+ */
11
+ export declare function addMove(game: Game, moveStr: string): Game | null;
12
+ /**
13
+ * Remove the last move from the game
14
+ * Reconstructs the game state from move list
15
+ */
16
+ export declare function removeLastMove(game: Game): Game;
17
+ /**
18
+ * Get current game status
19
+ */
20
+ export declare function getGameStatus(game: Game): GameStatus;
21
+ //# sourceMappingURL=index.d.ts.map