js-chess-engine 1.0.3 → 2.0.1-rc.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 (104) hide show
  1. package/README.md +586 -205
  2. package/dist/adapters/APIAdapter.d.ts +88 -0
  3. package/dist/adapters/APIAdapter.d.ts.map +1 -0
  4. package/dist/adapters/APIAdapter.js +225 -0
  5. package/dist/adapters/APIAdapter.js.map +1 -0
  6. package/dist/ai/AIEngine.d.ts +42 -0
  7. package/dist/ai/AIEngine.d.ts.map +1 -0
  8. package/dist/ai/AIEngine.js +62 -0
  9. package/dist/ai/AIEngine.js.map +1 -0
  10. package/dist/ai/Evaluator.d.ts +48 -0
  11. package/dist/ai/Evaluator.d.ts.map +1 -0
  12. package/dist/ai/Evaluator.js +248 -0
  13. package/dist/ai/Evaluator.js.map +1 -0
  14. package/dist/ai/MoveOrdering.d.ts +60 -0
  15. package/dist/ai/MoveOrdering.d.ts.map +1 -0
  16. package/dist/ai/MoveOrdering.js +173 -0
  17. package/dist/ai/MoveOrdering.js.map +1 -0
  18. package/dist/ai/Search.d.ts +62 -0
  19. package/dist/ai/Search.d.ts.map +1 -0
  20. package/dist/ai/Search.js +340 -0
  21. package/dist/ai/Search.js.map +1 -0
  22. package/dist/ai/TranspositionTable.d.ts +100 -0
  23. package/dist/ai/TranspositionTable.d.ts.map +1 -0
  24. package/dist/ai/TranspositionTable.js +176 -0
  25. package/dist/ai/TranspositionTable.js.map +1 -0
  26. package/dist/core/AttackDetector.d.ts +52 -0
  27. package/dist/core/AttackDetector.d.ts.map +1 -0
  28. package/dist/core/AttackDetector.js +387 -0
  29. package/dist/core/AttackDetector.js.map +1 -0
  30. package/dist/core/Board.d.ts +109 -0
  31. package/dist/core/Board.d.ts.map +1 -0
  32. package/dist/core/Board.js +410 -0
  33. package/dist/core/Board.js.map +1 -0
  34. package/dist/core/MoveGenerator.d.ts +48 -0
  35. package/dist/core/MoveGenerator.d.ts.map +1 -0
  36. package/dist/core/MoveGenerator.js +678 -0
  37. package/dist/core/MoveGenerator.js.map +1 -0
  38. package/dist/core/Position.d.ts +135 -0
  39. package/dist/core/Position.d.ts.map +1 -0
  40. package/dist/core/Position.js +351 -0
  41. package/dist/core/Position.js.map +1 -0
  42. package/dist/core/index.d.ts +9 -0
  43. package/dist/core/index.d.ts.map +1 -0
  44. package/dist/core/index.js +25 -0
  45. package/dist/core/index.js.map +1 -0
  46. package/dist/core/zobrist.d.ts +93 -0
  47. package/dist/core/zobrist.d.ts.map +1 -0
  48. package/dist/core/zobrist.js +273 -0
  49. package/dist/core/zobrist.js.map +1 -0
  50. package/dist/index.d.ts +154 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +353 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/types/ai.types.d.ts +97 -0
  55. package/dist/types/ai.types.d.ts.map +1 -0
  56. package/dist/types/ai.types.js +17 -0
  57. package/dist/types/ai.types.js.map +1 -0
  58. package/dist/types/board.types.d.ts +140 -0
  59. package/dist/types/board.types.d.ts.map +1 -0
  60. package/dist/types/board.types.js +34 -0
  61. package/dist/types/board.types.js.map +1 -0
  62. package/dist/types/index.d.ts +7 -0
  63. package/dist/types/index.d.ts.map +1 -0
  64. package/dist/types/index.js +26 -0
  65. package/dist/types/index.js.map +1 -0
  66. package/dist/types/move.types.d.ts +70 -0
  67. package/dist/types/move.types.d.ts.map +1 -0
  68. package/dist/types/move.types.js +53 -0
  69. package/dist/types/move.types.js.map +1 -0
  70. package/dist/utils/constants.d.ts +123 -0
  71. package/dist/utils/constants.d.ts.map +1 -0
  72. package/dist/utils/constants.js +259 -0
  73. package/dist/utils/constants.js.map +1 -0
  74. package/dist/utils/conversion.d.ts +152 -0
  75. package/dist/utils/conversion.d.ts.map +1 -0
  76. package/dist/utils/conversion.js +288 -0
  77. package/dist/utils/conversion.js.map +1 -0
  78. package/dist/utils/environment.d.ts +33 -0
  79. package/dist/utils/environment.d.ts.map +1 -0
  80. package/dist/utils/environment.js +71 -0
  81. package/dist/utils/environment.js.map +1 -0
  82. package/dist/utils/fen.d.ts +28 -0
  83. package/dist/utils/fen.d.ts.map +1 -0
  84. package/dist/utils/fen.js +203 -0
  85. package/dist/utils/fen.js.map +1 -0
  86. package/package.json +25 -29
  87. package/.eslintrc.json +0 -16
  88. package/.github/workflows/main.yml +0 -20
  89. package/.nvmrc +0 -1
  90. package/CHANGELOG.md +0 -597
  91. package/dist/js-chess-engine.js +0 -1
  92. package/example/aiMatch.mjs +0 -21
  93. package/example/console.mjs +0 -37
  94. package/example/server.mjs +0 -27
  95. package/lib/Board.mjs +0 -946
  96. package/lib/const/board.mjs +0 -838
  97. package/lib/js-chess-engine.mjs +0 -99
  98. package/lib/utils.mjs +0 -154
  99. package/test/.eslintrc.json +0 -11
  100. package/test/ai.test.mjs +0 -132
  101. package/test/badge.svg +0 -1
  102. package/test/importExport.mjs +0 -108
  103. package/test/moves.test.mjs +0 -798
  104. package/webpack.config.js +0 -12
@@ -0,0 +1,678 @@
1
+ "use strict";
2
+ /**
3
+ * Move generation for all piece types
4
+ *
5
+ * This module generates all legal moves for a given position using
6
+ * bitboard-based algorithms for performance.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.generateLegalMoves = generateLegalMoves;
10
+ exports.generatePseudoLegalMoves = generatePseudoLegalMoves;
11
+ exports.getMovesForPiece = getMovesForPiece;
12
+ exports.isMoveLegal = isMoveLegal;
13
+ exports.applyMoveComplete = applyMoveComplete;
14
+ const types_1 = require("../types");
15
+ const Position_1 = require("./Position");
16
+ const AttackDetector_1 = require("./AttackDetector");
17
+ const conversion_1 = require("../utils/conversion");
18
+ const Board_1 = require("./Board");
19
+ const constants_1 = require("../utils/constants");
20
+ /**
21
+ * Generate all legal moves for the current position
22
+ *
23
+ * @param board - Board state
24
+ * @returns Array of legal moves
25
+ */
26
+ function generateLegalMoves(board) {
27
+ const pseudoLegalMoves = generatePseudoLegalMoves(board);
28
+ const currentColor = board.turn;
29
+ // Check if the current player has a king
30
+ const ourKingBitboard = currentColor === types_1.InternalColor.WHITE ? board.whiteKing : board.blackKing;
31
+ if (ourKingBitboard === 0n) {
32
+ // No king - return all pseudo-legal moves (for test scenarios)
33
+ return pseudoLegalMoves;
34
+ }
35
+ // Filter to only legal moves
36
+ return pseudoLegalMoves.filter(move => {
37
+ // Special handling for castling - already checked in generation
38
+ if (move.flags & types_1.MoveFlag.CASTLING) {
39
+ return true;
40
+ }
41
+ // Make the move on a temporary board copy to check if it's legal
42
+ const testBoard = (0, Board_1.copyBoard)(board);
43
+ const originalTurn = testBoard.turn;
44
+ makeMove(testBoard, move);
45
+ // After making the move, check if OUR king (the one that just moved) is in check
46
+ // makeMove switches the turn, so we need to check the OPPOSITE color
47
+ const kingBitboardAfter = originalTurn === types_1.InternalColor.WHITE ? testBoard.whiteKing : testBoard.blackKing;
48
+ if (kingBitboardAfter === 0n) {
49
+ return true; // King was captured (shouldn't happen in legal game)
50
+ }
51
+ const kingSquare = (0, conversion_1.getLowestSetBit)(kingBitboardAfter);
52
+ const opponentColor = originalTurn === types_1.InternalColor.WHITE ? types_1.InternalColor.BLACK : types_1.InternalColor.WHITE;
53
+ // The move is legal if our king is NOT being attacked after the move
54
+ return !(0, AttackDetector_1.isSquareAttacked)(testBoard, kingSquare, opponentColor);
55
+ });
56
+ }
57
+ /**
58
+ * Apply a move to a board (mutates the board)
59
+ * Used internally for legal move checking
60
+ *
61
+ * @param board - Board to modify
62
+ * @param move - Move to apply
63
+ */
64
+ function makeMove(board, move) {
65
+ // Handle castling specially
66
+ if (move.flags & types_1.MoveFlag.CASTLING) {
67
+ // Move the king
68
+ (0, Board_1.removePiece)(board, move.from);
69
+ (0, Board_1.setPiece)(board, move.to, move.piece);
70
+ // Move the rook
71
+ const color = board.turn;
72
+ if (color === types_1.InternalColor.WHITE) {
73
+ if (move.to === constants_1.CASTLING.WHITE_SHORT.kingTo) {
74
+ // White short castling
75
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.WHITE_SHORT.rookFrom);
76
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.WHITE_SHORT.rookTo, types_1.Piece.WHITE_ROOK);
77
+ }
78
+ else {
79
+ // White long castling
80
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.WHITE_LONG.rookFrom);
81
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.WHITE_LONG.rookTo, types_1.Piece.WHITE_ROOK);
82
+ }
83
+ }
84
+ else {
85
+ if (move.to === constants_1.CASTLING.BLACK_SHORT.kingTo) {
86
+ // Black short castling
87
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.BLACK_SHORT.rookFrom);
88
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.BLACK_SHORT.rookTo, types_1.Piece.BLACK_ROOK);
89
+ }
90
+ else {
91
+ // Black long castling
92
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.BLACK_LONG.rookFrom);
93
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.BLACK_LONG.rookTo, types_1.Piece.BLACK_ROOK);
94
+ }
95
+ }
96
+ }
97
+ else if (move.flags & types_1.MoveFlag.EN_PASSANT) {
98
+ // En passant capture
99
+ (0, Board_1.removePiece)(board, move.from);
100
+ (0, Board_1.setPiece)(board, move.to, move.piece);
101
+ // Remove the captured pawn (on different square than move.to)
102
+ const capturedPawnSquare = board.turn === types_1.InternalColor.WHITE
103
+ ? (move.to - 8) // Captured pawn is one rank below
104
+ : (move.to + 8); // Captured pawn is one rank above
105
+ (0, Board_1.removePiece)(board, capturedPawnSquare);
106
+ }
107
+ else if (move.flags & types_1.MoveFlag.PROMOTION) {
108
+ // Promotion
109
+ (0, Board_1.removePiece)(board, move.from);
110
+ if (move.capturedPiece !== types_1.Piece.EMPTY) {
111
+ (0, Board_1.removePiece)(board, move.to);
112
+ }
113
+ (0, Board_1.setPiece)(board, move.to, move.promotionPiece);
114
+ }
115
+ else {
116
+ // Normal move or capture
117
+ (0, Board_1.removePiece)(board, move.from);
118
+ if (move.capturedPiece !== types_1.Piece.EMPTY) {
119
+ (0, Board_1.removePiece)(board, move.to);
120
+ }
121
+ (0, Board_1.setPiece)(board, move.to, move.piece);
122
+ }
123
+ // Update en passant square
124
+ if (move.flags & types_1.MoveFlag.PAWN_DOUBLE_PUSH) {
125
+ const epSquare = board.turn === types_1.InternalColor.WHITE
126
+ ? (move.from + 8)
127
+ : (move.from - 8);
128
+ board.enPassantSquare = epSquare;
129
+ }
130
+ else {
131
+ board.enPassantSquare = null;
132
+ }
133
+ // Switch turn (needed for isKingInCheck to check the right king)
134
+ board.turn = board.turn === types_1.InternalColor.WHITE ? types_1.InternalColor.BLACK : types_1.InternalColor.WHITE;
135
+ }
136
+ /**
137
+ * Generate all pseudo-legal moves (may leave king in check)
138
+ *
139
+ * @param board - Board state
140
+ * @returns Array of pseudo-legal moves
141
+ */
142
+ function generatePseudoLegalMoves(board) {
143
+ const moves = [];
144
+ const color = board.turn;
145
+ const friendlyPieces = color === types_1.InternalColor.WHITE ? board.whitePieces : board.blackPieces;
146
+ const enemyPieces = color === types_1.InternalColor.WHITE ? board.blackPieces : board.whitePieces;
147
+ // Generate moves for each piece type
148
+ generatePawnMoves(board, moves, color, friendlyPieces, enemyPieces);
149
+ generateKnightMoves(board, moves, color, friendlyPieces);
150
+ generateBishopMoves(board, moves, color, friendlyPieces);
151
+ generateRookMoves(board, moves, color, friendlyPieces);
152
+ generateQueenMoves(board, moves, color, friendlyPieces);
153
+ generateKingMoves(board, moves, color, friendlyPieces);
154
+ generateCastlingMoves(board, moves, color);
155
+ return moves;
156
+ }
157
+ /**
158
+ * Generate pawn moves (including promotions and en passant)
159
+ */
160
+ function generatePawnMoves(board, moves, color, _friendlyPieces, enemyPieces) {
161
+ const pawns = color === types_1.InternalColor.WHITE ? board.whitePawns : board.blackPawns;
162
+ const pawnPiece = color === types_1.InternalColor.WHITE ? types_1.Piece.WHITE_PAWN : types_1.Piece.BLACK_PAWN;
163
+ const promotionRank = color === types_1.InternalColor.WHITE ? 7 : 0;
164
+ const empty = ~board.allPieces;
165
+ if (color === types_1.InternalColor.WHITE) {
166
+ // Single push
167
+ const singlePush = (0, Position_1.shiftNorth)(pawns) & empty;
168
+ const singlePushIndices = (0, conversion_1.bitboardToIndices)(singlePush);
169
+ for (const to of singlePushIndices) {
170
+ const from = (to - 8);
171
+ const toRank = (0, conversion_1.getRankIndex)(to);
172
+ // Check for promotion
173
+ if (toRank === promotionRank) {
174
+ // Add all promotion moves
175
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.WHITE_QUEEN));
176
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.WHITE_ROOK));
177
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.WHITE_BISHOP));
178
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.WHITE_KNIGHT));
179
+ }
180
+ else {
181
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.NONE));
182
+ }
183
+ }
184
+ // Double push
185
+ const doublePushSource = pawns & 0x000000000000ff00n; // Rank 2
186
+ const doublePush = (0, Position_1.shiftNorth)((0, Position_1.shiftNorth)(doublePushSource) & empty) & empty;
187
+ const doublePushIndices = (0, conversion_1.bitboardToIndices)(doublePush);
188
+ for (const to of doublePushIndices) {
189
+ const from = (to - 16);
190
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PAWN_DOUBLE_PUSH));
191
+ }
192
+ // Captures north-east
193
+ const capturesNE = (0, Position_1.shiftNorthEast)(pawns) & enemyPieces;
194
+ const capturesNEIndices = (0, conversion_1.bitboardToIndices)(capturesNE);
195
+ for (const to of capturesNEIndices) {
196
+ const from = (to - 9);
197
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
198
+ const toRank = (0, conversion_1.getRankIndex)(to);
199
+ if (toRank === promotionRank) {
200
+ // Promotion capture
201
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_QUEEN));
202
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_ROOK));
203
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_BISHOP));
204
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_KNIGHT));
205
+ }
206
+ else {
207
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.CAPTURE));
208
+ }
209
+ }
210
+ // Captures north-west
211
+ const capturesNW = (0, Position_1.shiftNorthWest)(pawns) & enemyPieces;
212
+ const capturesNWIndices = (0, conversion_1.bitboardToIndices)(capturesNW);
213
+ for (const to of capturesNWIndices) {
214
+ const from = (to - 7);
215
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
216
+ const toRank = (0, conversion_1.getRankIndex)(to);
217
+ if (toRank === promotionRank) {
218
+ // Promotion capture
219
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_QUEEN));
220
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_ROOK));
221
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_BISHOP));
222
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.WHITE_KNIGHT));
223
+ }
224
+ else {
225
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.CAPTURE));
226
+ }
227
+ }
228
+ // En passant
229
+ if (board.enPassantSquare !== null) {
230
+ const epSquare = board.enPassantSquare;
231
+ const epTarget = 1n << BigInt(epSquare);
232
+ // Check if any pawn can capture en passant
233
+ const canCaptureEP = ((0, Position_1.shiftSouthWest)(epTarget) | (0, Position_1.shiftSouthEast)(epTarget)) & pawns;
234
+ if (canCaptureEP !== 0n) {
235
+ const attackerIndices = (0, conversion_1.bitboardToIndices)(canCaptureEP);
236
+ for (const from of attackerIndices) {
237
+ const capturedPiece = types_1.Piece.BLACK_PAWN;
238
+ moves.push(createMove(from, epSquare, pawnPiece, capturedPiece, types_1.MoveFlag.EN_PASSANT | types_1.MoveFlag.CAPTURE));
239
+ }
240
+ }
241
+ }
242
+ }
243
+ else {
244
+ // Black pawns (move south)
245
+ // Single push
246
+ const singlePush = (0, Position_1.shiftSouth)(pawns) & empty;
247
+ const singlePushIndices = (0, conversion_1.bitboardToIndices)(singlePush);
248
+ for (const to of singlePushIndices) {
249
+ const from = (to + 8);
250
+ const toRank = (0, conversion_1.getRankIndex)(to);
251
+ // Check for promotion
252
+ if (toRank === promotionRank) {
253
+ // Add all promotion moves
254
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.BLACK_QUEEN));
255
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.BLACK_ROOK));
256
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.BLACK_BISHOP));
257
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PROMOTION, types_1.Piece.BLACK_KNIGHT));
258
+ }
259
+ else {
260
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.NONE));
261
+ }
262
+ }
263
+ // Double push
264
+ const doublePushSource = pawns & 0x00ff000000000000n; // Rank 7
265
+ const doublePush = (0, Position_1.shiftSouth)((0, Position_1.shiftSouth)(doublePushSource) & empty) & empty;
266
+ const doublePushIndices = (0, conversion_1.bitboardToIndices)(doublePush);
267
+ for (const to of doublePushIndices) {
268
+ const from = (to + 16);
269
+ moves.push(createMove(from, to, pawnPiece, types_1.Piece.EMPTY, types_1.MoveFlag.PAWN_DOUBLE_PUSH));
270
+ }
271
+ // Captures south-east
272
+ const capturesSE = (0, Position_1.shiftSouthEast)(pawns) & enemyPieces;
273
+ const capturesSEIndices = (0, conversion_1.bitboardToIndices)(capturesSE);
274
+ for (const to of capturesSEIndices) {
275
+ const from = (to + 7);
276
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
277
+ const toRank = (0, conversion_1.getRankIndex)(to);
278
+ if (toRank === promotionRank) {
279
+ // Promotion capture
280
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_QUEEN));
281
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_ROOK));
282
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_BISHOP));
283
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_KNIGHT));
284
+ }
285
+ else {
286
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.CAPTURE));
287
+ }
288
+ }
289
+ // Captures south-west
290
+ const capturesSW = (0, Position_1.shiftSouthWest)(pawns) & enemyPieces;
291
+ const capturesSWIndices = (0, conversion_1.bitboardToIndices)(capturesSW);
292
+ for (const to of capturesSWIndices) {
293
+ const from = (to + 9);
294
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
295
+ const toRank = (0, conversion_1.getRankIndex)(to);
296
+ if (toRank === promotionRank) {
297
+ // Promotion capture
298
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_QUEEN));
299
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_ROOK));
300
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_BISHOP));
301
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.PROMOTION | types_1.MoveFlag.CAPTURE, types_1.Piece.BLACK_KNIGHT));
302
+ }
303
+ else {
304
+ moves.push(createMove(from, to, pawnPiece, capturedPiece, types_1.MoveFlag.CAPTURE));
305
+ }
306
+ }
307
+ // En passant
308
+ if (board.enPassantSquare !== null) {
309
+ const epSquare = board.enPassantSquare;
310
+ const epTarget = 1n << BigInt(epSquare);
311
+ // Check if any pawn can capture en passant
312
+ const canCaptureEP = ((0, Position_1.shiftNorthWest)(epTarget) | (0, Position_1.shiftNorthEast)(epTarget)) & pawns;
313
+ if (canCaptureEP !== 0n) {
314
+ const attackerIndices = (0, conversion_1.bitboardToIndices)(canCaptureEP);
315
+ for (const from of attackerIndices) {
316
+ const capturedPiece = types_1.Piece.WHITE_PAWN;
317
+ moves.push(createMove(from, epSquare, pawnPiece, capturedPiece, types_1.MoveFlag.EN_PASSANT | types_1.MoveFlag.CAPTURE));
318
+ }
319
+ }
320
+ }
321
+ }
322
+ }
323
+ /**
324
+ * Generate knight moves
325
+ */
326
+ function generateKnightMoves(board, moves, color, friendlyPieces) {
327
+ const knights = color === types_1.InternalColor.WHITE ? board.whiteKnights : board.blackKnights;
328
+ const knightPiece = color === types_1.InternalColor.WHITE ? types_1.Piece.WHITE_KNIGHT : types_1.Piece.BLACK_KNIGHT;
329
+ let knightsBB = knights;
330
+ while (knightsBB !== 0n) {
331
+ const from = (0, conversion_1.getLowestSetBit)(knightsBB);
332
+ const attacks = (0, Position_1.getKnightAttacks)(from) & ~friendlyPieces;
333
+ const attackIndices = (0, conversion_1.bitboardToIndices)(attacks);
334
+ for (const to of attackIndices) {
335
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
336
+ const flags = capturedPiece !== types_1.Piece.EMPTY ? types_1.MoveFlag.CAPTURE : types_1.MoveFlag.NONE;
337
+ moves.push(createMove(from, to, knightPiece, capturedPiece, flags));
338
+ }
339
+ knightsBB &= knightsBB - 1n; // Clear lowest bit
340
+ }
341
+ }
342
+ /**
343
+ * Generate bishop moves
344
+ */
345
+ function generateBishopMoves(board, moves, color, friendlyPieces) {
346
+ const bishops = color === types_1.InternalColor.WHITE ? board.whiteBishops : board.blackBishops;
347
+ const bishopPiece = color === types_1.InternalColor.WHITE ? types_1.Piece.WHITE_BISHOP : types_1.Piece.BLACK_BISHOP;
348
+ let bishopsBB = bishops;
349
+ while (bishopsBB !== 0n) {
350
+ const from = (0, conversion_1.getLowestSetBit)(bishopsBB);
351
+ const attacks = (0, Position_1.getBishopAttacks)(from, board.allPieces) & ~friendlyPieces;
352
+ const attackIndices = (0, conversion_1.bitboardToIndices)(attacks);
353
+ for (const to of attackIndices) {
354
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
355
+ const flags = capturedPiece !== types_1.Piece.EMPTY ? types_1.MoveFlag.CAPTURE : types_1.MoveFlag.NONE;
356
+ moves.push(createMove(from, to, bishopPiece, capturedPiece, flags));
357
+ }
358
+ bishopsBB &= bishopsBB - 1n;
359
+ }
360
+ }
361
+ /**
362
+ * Generate rook moves
363
+ */
364
+ function generateRookMoves(board, moves, color, friendlyPieces) {
365
+ const rooks = color === types_1.InternalColor.WHITE ? board.whiteRooks : board.blackRooks;
366
+ const rookPiece = color === types_1.InternalColor.WHITE ? types_1.Piece.WHITE_ROOK : types_1.Piece.BLACK_ROOK;
367
+ let rooksBB = rooks;
368
+ while (rooksBB !== 0n) {
369
+ const from = (0, conversion_1.getLowestSetBit)(rooksBB);
370
+ const attacks = (0, Position_1.getRookAttacks)(from, board.allPieces) & ~friendlyPieces;
371
+ const attackIndices = (0, conversion_1.bitboardToIndices)(attacks);
372
+ for (const to of attackIndices) {
373
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
374
+ const flags = capturedPiece !== types_1.Piece.EMPTY ? types_1.MoveFlag.CAPTURE : types_1.MoveFlag.NONE;
375
+ moves.push(createMove(from, to, rookPiece, capturedPiece, flags));
376
+ }
377
+ rooksBB &= rooksBB - 1n;
378
+ }
379
+ }
380
+ /**
381
+ * Generate queen moves
382
+ */
383
+ function generateQueenMoves(board, moves, color, friendlyPieces) {
384
+ const queens = color === types_1.InternalColor.WHITE ? board.whiteQueens : board.blackQueens;
385
+ const queenPiece = color === types_1.InternalColor.WHITE ? types_1.Piece.WHITE_QUEEN : types_1.Piece.BLACK_QUEEN;
386
+ let queensBB = queens;
387
+ while (queensBB !== 0n) {
388
+ const from = (0, conversion_1.getLowestSetBit)(queensBB);
389
+ const attacks = (0, Position_1.getQueenAttacks)(from, board.allPieces) & ~friendlyPieces;
390
+ const attackIndices = (0, conversion_1.bitboardToIndices)(attacks);
391
+ for (const to of attackIndices) {
392
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
393
+ const flags = capturedPiece !== types_1.Piece.EMPTY ? types_1.MoveFlag.CAPTURE : types_1.MoveFlag.NONE;
394
+ moves.push(createMove(from, to, queenPiece, capturedPiece, flags));
395
+ }
396
+ queensBB &= queensBB - 1n;
397
+ }
398
+ }
399
+ /**
400
+ * Generate king moves (excluding castling)
401
+ */
402
+ function generateKingMoves(board, moves, color, friendlyPieces) {
403
+ const king = color === types_1.InternalColor.WHITE ? board.whiteKing : board.blackKing;
404
+ const kingPiece = color === types_1.InternalColor.WHITE ? types_1.Piece.WHITE_KING : types_1.Piece.BLACK_KING;
405
+ if (king === 0n)
406
+ return;
407
+ const from = (0, conversion_1.getLowestSetBit)(king);
408
+ const attacks = (0, Position_1.getKingAttacks)(from) & ~friendlyPieces;
409
+ const attackIndices = (0, conversion_1.bitboardToIndices)(attacks);
410
+ for (const to of attackIndices) {
411
+ const capturedPiece = (0, Board_1.getPiece)(board, to);
412
+ const flags = capturedPiece !== types_1.Piece.EMPTY ? types_1.MoveFlag.CAPTURE : types_1.MoveFlag.NONE;
413
+ moves.push(createMove(from, to, kingPiece, capturedPiece, flags));
414
+ }
415
+ }
416
+ /**
417
+ * Generate castling moves
418
+ */
419
+ function generateCastlingMoves(board, moves, color) {
420
+ const opponentColor = color === types_1.InternalColor.WHITE ? types_1.InternalColor.BLACK : types_1.InternalColor.WHITE;
421
+ if (color === types_1.InternalColor.WHITE) {
422
+ // White short castling (O-O)
423
+ if (board.castlingRights.whiteShort &&
424
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.WHITE_SHORT.kingFrom) === types_1.Piece.WHITE_KING &&
425
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.WHITE_SHORT.rookFrom) === types_1.Piece.WHITE_ROOK &&
426
+ (0, Board_1.isSquareEmpty)(board, 5) && // F1
427
+ (0, Board_1.isSquareEmpty)(board, 6) && // G1
428
+ !(0, AttackDetector_1.isSquareAttacked)(board, 4, opponentColor) && // E1 not in check
429
+ !(0, AttackDetector_1.isSquareAttacked)(board, 5, opponentColor) && // F1 not attacked
430
+ !(0, AttackDetector_1.isSquareAttacked)(board, 6, opponentColor) // G1 not attacked
431
+ ) {
432
+ moves.push(createMove(constants_1.CASTLING.WHITE_SHORT.kingFrom, constants_1.CASTLING.WHITE_SHORT.kingTo, types_1.Piece.WHITE_KING, types_1.Piece.EMPTY, types_1.MoveFlag.CASTLING));
433
+ }
434
+ // White long castling (O-O-O)
435
+ if (board.castlingRights.whiteLong &&
436
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.WHITE_LONG.kingFrom) === types_1.Piece.WHITE_KING &&
437
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.WHITE_LONG.rookFrom) === types_1.Piece.WHITE_ROOK &&
438
+ (0, Board_1.isSquareEmpty)(board, 1) && // B1
439
+ (0, Board_1.isSquareEmpty)(board, 2) && // C1
440
+ (0, Board_1.isSquareEmpty)(board, 3) && // D1
441
+ !(0, AttackDetector_1.isSquareAttacked)(board, 4, opponentColor) && // E1 not in check
442
+ !(0, AttackDetector_1.isSquareAttacked)(board, 3, opponentColor) && // D1 not attacked
443
+ !(0, AttackDetector_1.isSquareAttacked)(board, 2, opponentColor) // C1 not attacked
444
+ ) {
445
+ moves.push(createMove(constants_1.CASTLING.WHITE_LONG.kingFrom, constants_1.CASTLING.WHITE_LONG.kingTo, types_1.Piece.WHITE_KING, types_1.Piece.EMPTY, types_1.MoveFlag.CASTLING));
446
+ }
447
+ }
448
+ else {
449
+ // Black short castling (O-O)
450
+ if (board.castlingRights.blackShort &&
451
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.BLACK_SHORT.kingFrom) === types_1.Piece.BLACK_KING &&
452
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.BLACK_SHORT.rookFrom) === types_1.Piece.BLACK_ROOK &&
453
+ (0, Board_1.isSquareEmpty)(board, 61) && // F8
454
+ (0, Board_1.isSquareEmpty)(board, 62) && // G8
455
+ !(0, AttackDetector_1.isSquareAttacked)(board, 60, opponentColor) && // E8 not in check
456
+ !(0, AttackDetector_1.isSquareAttacked)(board, 61, opponentColor) && // F8 not attacked
457
+ !(0, AttackDetector_1.isSquareAttacked)(board, 62, opponentColor) // G8 not attacked
458
+ ) {
459
+ moves.push(createMove(constants_1.CASTLING.BLACK_SHORT.kingFrom, constants_1.CASTLING.BLACK_SHORT.kingTo, types_1.Piece.BLACK_KING, types_1.Piece.EMPTY, types_1.MoveFlag.CASTLING));
460
+ }
461
+ // Black long castling (O-O-O)
462
+ if (board.castlingRights.blackLong &&
463
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.BLACK_LONG.kingFrom) === types_1.Piece.BLACK_KING &&
464
+ (0, Board_1.getPiece)(board, constants_1.CASTLING.BLACK_LONG.rookFrom) === types_1.Piece.BLACK_ROOK &&
465
+ (0, Board_1.isSquareEmpty)(board, 57) && // B8
466
+ (0, Board_1.isSquareEmpty)(board, 58) && // C8
467
+ (0, Board_1.isSquareEmpty)(board, 59) && // D8
468
+ !(0, AttackDetector_1.isSquareAttacked)(board, 60, opponentColor) && // E8 not in check
469
+ !(0, AttackDetector_1.isSquareAttacked)(board, 59, opponentColor) && // D8 not attacked
470
+ !(0, AttackDetector_1.isSquareAttacked)(board, 58, opponentColor) // C8 not attacked
471
+ ) {
472
+ moves.push(createMove(constants_1.CASTLING.BLACK_LONG.kingFrom, constants_1.CASTLING.BLACK_LONG.kingTo, types_1.Piece.BLACK_KING, types_1.Piece.EMPTY, types_1.MoveFlag.CASTLING));
473
+ }
474
+ }
475
+ }
476
+ /**
477
+ * Helper to create a move object
478
+ */
479
+ function createMove(from, to, piece, capturedPiece, flags, promotionPiece) {
480
+ return {
481
+ from,
482
+ to,
483
+ piece,
484
+ capturedPiece,
485
+ flags,
486
+ promotionPiece,
487
+ };
488
+ }
489
+ /**
490
+ * Get all legal moves for a specific piece
491
+ *
492
+ * @param board - Board state
493
+ * @param square - Square of the piece
494
+ * @returns Array of legal moves for that piece
495
+ */
496
+ function getMovesForPiece(board, square) {
497
+ const allMoves = generateLegalMoves(board);
498
+ return allMoves.filter(move => move.from === square);
499
+ }
500
+ /**
501
+ * Check if a move is legal
502
+ *
503
+ * @param board - Board state
504
+ * @param from - From square
505
+ * @param to - To square
506
+ * @returns true if move is legal
507
+ */
508
+ function isMoveLegal(board, from, to) {
509
+ const legalMoves = generateLegalMoves(board);
510
+ return legalMoves.some(move => move.from === from && move.to === to);
511
+ }
512
+ /**
513
+ * Apply a move to the board with full state updates (mutates the board)
514
+ * Updates turn, castling rights, en passant, move counters, and game status
515
+ *
516
+ * @param board - Board state to modify
517
+ * @param move - Move to apply
518
+ * @returns The applied move
519
+ */
520
+ function applyMoveComplete(board, move) {
521
+ const { from, to, piece, capturedPiece, flags, promotionPiece } = move;
522
+ // Reset en passant square (will be set if this is a double pawn push)
523
+ board.enPassantSquare = null;
524
+ // Handle captures
525
+ if (capturedPiece !== types_1.Piece.EMPTY) {
526
+ (0, Board_1.removePiece)(board, to);
527
+ board.halfMoveClock = 0;
528
+ }
529
+ else {
530
+ board.halfMoveClock++;
531
+ }
532
+ // Handle en passant capture
533
+ if (flags & types_1.MoveFlag.EN_PASSANT) {
534
+ const captureSquare = board.turn === types_1.InternalColor.WHITE ? to - 8 : to + 8;
535
+ (0, Board_1.removePiece)(board, captureSquare);
536
+ board.halfMoveClock = 0;
537
+ }
538
+ // Handle castling
539
+ if (flags & types_1.MoveFlag.CASTLING) {
540
+ // Move the rook
541
+ if (to === constants_1.CASTLING.WHITE_SHORT.kingTo) {
542
+ // White kingside
543
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.WHITE_SHORT.rookFrom);
544
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.WHITE_SHORT.rookTo, types_1.Piece.WHITE_ROOK);
545
+ }
546
+ else if (to === constants_1.CASTLING.WHITE_LONG.kingTo) {
547
+ // White queenside
548
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.WHITE_LONG.rookFrom);
549
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.WHITE_LONG.rookTo, types_1.Piece.WHITE_ROOK);
550
+ }
551
+ else if (to === constants_1.CASTLING.BLACK_SHORT.kingTo) {
552
+ // Black kingside
553
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.BLACK_SHORT.rookFrom);
554
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.BLACK_SHORT.rookTo, types_1.Piece.BLACK_ROOK);
555
+ }
556
+ else if (to === constants_1.CASTLING.BLACK_LONG.kingTo) {
557
+ // Black queenside
558
+ (0, Board_1.removePiece)(board, constants_1.CASTLING.BLACK_LONG.rookFrom);
559
+ (0, Board_1.setPiece)(board, constants_1.CASTLING.BLACK_LONG.rookTo, types_1.Piece.BLACK_ROOK);
560
+ }
561
+ }
562
+ // Move the piece
563
+ (0, Board_1.removePiece)(board, from);
564
+ // Handle promotion
565
+ if (flags & types_1.MoveFlag.PROMOTION && promotionPiece) {
566
+ (0, Board_1.setPiece)(board, to, promotionPiece);
567
+ }
568
+ else {
569
+ (0, Board_1.setPiece)(board, to, piece);
570
+ }
571
+ // Reset half-move clock on pawn moves
572
+ if (piece === types_1.Piece.WHITE_PAWN || piece === types_1.Piece.BLACK_PAWN) {
573
+ board.halfMoveClock = 0;
574
+ }
575
+ // Handle double pawn push (set en passant square)
576
+ if (flags & types_1.MoveFlag.PAWN_DOUBLE_PUSH) {
577
+ const enPassantSquare = board.turn === types_1.InternalColor.WHITE ? from + 8 : from - 8;
578
+ board.enPassantSquare = enPassantSquare;
579
+ }
580
+ // Update castling rights
581
+ updateCastlingRights(board, from, to, piece);
582
+ // Switch turn
583
+ board.turn = board.turn === types_1.InternalColor.WHITE ? types_1.InternalColor.BLACK : types_1.InternalColor.WHITE;
584
+ // Increment full move number after black's move
585
+ if (board.turn === types_1.InternalColor.WHITE) {
586
+ board.fullMoveNumber++;
587
+ }
588
+ // Update game status (check, checkmate, stalemate)
589
+ updateGameStatus(board);
590
+ return move;
591
+ }
592
+ /**
593
+ * Update castling rights after a move
594
+ *
595
+ * @param board - Board state
596
+ * @param from - From square
597
+ * @param to - To square
598
+ * @param piece - Piece that moved
599
+ */
600
+ function updateCastlingRights(board, from, to, piece) {
601
+ // If king moves, lose all castling rights for that color
602
+ if (piece === types_1.Piece.WHITE_KING) {
603
+ board.castlingRights.whiteShort = false;
604
+ board.castlingRights.whiteLong = false;
605
+ }
606
+ else if (piece === types_1.Piece.BLACK_KING) {
607
+ board.castlingRights.blackShort = false;
608
+ board.castlingRights.blackLong = false;
609
+ }
610
+ // If rook moves from starting square, lose castling right for that side
611
+ if (piece === types_1.Piece.WHITE_ROOK) {
612
+ if (from === constants_1.CASTLING.WHITE_SHORT.rookFrom) {
613
+ board.castlingRights.whiteShort = false;
614
+ }
615
+ else if (from === constants_1.CASTLING.WHITE_LONG.rookFrom) {
616
+ board.castlingRights.whiteLong = false;
617
+ }
618
+ }
619
+ else if (piece === types_1.Piece.BLACK_ROOK) {
620
+ if (from === constants_1.CASTLING.BLACK_SHORT.rookFrom) {
621
+ board.castlingRights.blackShort = false;
622
+ }
623
+ else if (from === constants_1.CASTLING.BLACK_LONG.rookFrom) {
624
+ board.castlingRights.blackLong = false;
625
+ }
626
+ }
627
+ // If rook is captured, lose castling right for that side
628
+ if (to === constants_1.CASTLING.WHITE_SHORT.rookFrom) {
629
+ board.castlingRights.whiteShort = false;
630
+ }
631
+ else if (to === constants_1.CASTLING.WHITE_LONG.rookFrom) {
632
+ board.castlingRights.whiteLong = false;
633
+ }
634
+ else if (to === constants_1.CASTLING.BLACK_SHORT.rookFrom) {
635
+ board.castlingRights.blackShort = false;
636
+ }
637
+ else if (to === constants_1.CASTLING.BLACK_LONG.rookFrom) {
638
+ board.castlingRights.blackLong = false;
639
+ }
640
+ }
641
+ /**
642
+ * Update game status (check, checkmate, stalemate)
643
+ *
644
+ * @param board - Board state
645
+ */
646
+ function updateGameStatus(board) {
647
+ const currentColor = board.turn;
648
+ const kingBitboard = currentColor === types_1.InternalColor.WHITE ? board.whiteKing : board.blackKing;
649
+ if (kingBitboard === 0n) {
650
+ board.isCheck = false;
651
+ board.isCheckmate = false;
652
+ board.isStalemate = false;
653
+ return;
654
+ }
655
+ const kingSquare = (0, conversion_1.getLowestSetBit)(kingBitboard);
656
+ // Check if the current player's king is attacked by the OPPONENT
657
+ const opponentColor = currentColor === types_1.InternalColor.WHITE ? types_1.InternalColor.BLACK : types_1.InternalColor.WHITE;
658
+ const inCheck = (0, AttackDetector_1.isSquareAttacked)(board, kingSquare, opponentColor);
659
+ board.isCheck = inCheck;
660
+ // Check if there are any legal moves
661
+ const legalMoves = generateLegalMoves(board);
662
+ const hasLegalMoves = legalMoves.length > 0;
663
+ if (!hasLegalMoves) {
664
+ if (inCheck) {
665
+ board.isCheckmate = true;
666
+ board.isStalemate = false;
667
+ }
668
+ else {
669
+ board.isCheckmate = false;
670
+ board.isStalemate = true;
671
+ }
672
+ }
673
+ else {
674
+ board.isCheckmate = false;
675
+ board.isStalemate = false;
676
+ }
677
+ }
678
+ //# sourceMappingURL=MoveGenerator.js.map