@connectorvol/chessops 0.15.1

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 (93) hide show
  1. package/LICENSE.txt +674 -0
  2. package/README.md +61 -0
  3. package/dist/cjs/attacks.js +152 -0
  4. package/dist/cjs/attacks.js.map +1 -0
  5. package/dist/cjs/board.js +143 -0
  6. package/dist/cjs/board.js.map +1 -0
  7. package/dist/cjs/chess.js +638 -0
  8. package/dist/cjs/chess.js.map +1 -0
  9. package/dist/cjs/compat.js +89 -0
  10. package/dist/cjs/compat.js.map +1 -0
  11. package/dist/cjs/debug.js +103 -0
  12. package/dist/cjs/debug.js.map +1 -0
  13. package/dist/cjs/fen.js +325 -0
  14. package/dist/cjs/fen.js.map +1 -0
  15. package/dist/cjs/index.js +94 -0
  16. package/dist/cjs/index.js.map +1 -0
  17. package/dist/cjs/pgn.js +796 -0
  18. package/dist/cjs/pgn.js.map +1 -0
  19. package/dist/cjs/san.js +174 -0
  20. package/dist/cjs/san.js.map +1 -0
  21. package/dist/cjs/setup.js +167 -0
  22. package/dist/cjs/setup.js.map +1 -0
  23. package/dist/cjs/squareSet.js +206 -0
  24. package/dist/cjs/squareSet.js.map +1 -0
  25. package/dist/cjs/transform.js +57 -0
  26. package/dist/cjs/transform.js.map +1 -0
  27. package/dist/cjs/types.js +24 -0
  28. package/dist/cjs/types.js.map +1 -0
  29. package/dist/cjs/util.js +104 -0
  30. package/dist/cjs/util.js.map +1 -0
  31. package/dist/cjs/variant.js +833 -0
  32. package/dist/cjs/variant.js.map +1 -0
  33. package/dist/esm/attacks.js +140 -0
  34. package/dist/esm/attacks.js.map +1 -0
  35. package/dist/esm/board.js +138 -0
  36. package/dist/esm/board.js.map +1 -0
  37. package/dist/esm/chess.js +624 -0
  38. package/dist/esm/chess.js.map +1 -0
  39. package/dist/esm/compat.js +81 -0
  40. package/dist/esm/compat.js.map +1 -0
  41. package/dist/esm/debug.js +94 -0
  42. package/dist/esm/debug.js.map +1 -0
  43. package/dist/esm/fen.js +308 -0
  44. package/dist/esm/fen.js.map +1 -0
  45. package/dist/esm/index.js +15 -0
  46. package/dist/esm/index.js.map +1 -0
  47. package/dist/esm/pgn.js +769 -0
  48. package/dist/esm/pgn.js.map +1 -0
  49. package/dist/esm/san.js +167 -0
  50. package/dist/esm/san.js.map +1 -0
  51. package/dist/esm/setup.js +157 -0
  52. package/dist/esm/setup.js.map +1 -0
  53. package/dist/esm/squareSet.js +202 -0
  54. package/dist/esm/squareSet.js.map +1 -0
  55. package/dist/esm/transform.js +48 -0
  56. package/dist/esm/transform.js.map +1 -0
  57. package/dist/esm/types.js +19 -0
  58. package/dist/esm/types.js.map +1 -0
  59. package/dist/esm/util.js +87 -0
  60. package/dist/esm/util.js.map +1 -0
  61. package/dist/esm/variant.js +812 -0
  62. package/dist/esm/variant.js.map +1 -0
  63. package/dist/types/attacks.d.ts +58 -0
  64. package/dist/types/board.d.ts +62 -0
  65. package/dist/types/chess.d.ts +82 -0
  66. package/dist/types/compat.d.ts +26 -0
  67. package/dist/types/debug.d.ts +10 -0
  68. package/dist/types/fen.d.ts +40 -0
  69. package/dist/types/index.d.ts +14 -0
  70. package/dist/types/pgn.d.ts +203 -0
  71. package/dist/types/san.d.ts +6 -0
  72. package/dist/types/setup.d.ts +65 -0
  73. package/dist/types/squareSet.d.ts +50 -0
  74. package/dist/types/transform.d.ts +9 -0
  75. package/dist/types/types.d.ts +58 -0
  76. package/dist/types/util.d.ts +21 -0
  77. package/dist/types/variant.d.ts +92 -0
  78. package/package.json +86 -0
  79. package/src/attacks.ts +160 -0
  80. package/src/board.ts +168 -0
  81. package/src/chess.ts +687 -0
  82. package/src/compat.ts +120 -0
  83. package/src/debug.ts +100 -0
  84. package/src/fen.ts +328 -0
  85. package/src/index.ts +85 -0
  86. package/src/pgn.ts +876 -0
  87. package/src/san.ts +190 -0
  88. package/src/setup.ts +203 -0
  89. package/src/squareSet.ts +243 -0
  90. package/src/transform.ts +49 -0
  91. package/src/types.ts +93 -0
  92. package/src/util.ts +116 -0
  93. package/src/variant.ts +939 -0
@@ -0,0 +1,833 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isStandardMaterial = exports.setupPosition = exports.defaultPosition = exports.Horde = exports.RacingKings = exports.ThreeCheck = exports.KingOfTheHill = exports.Antichess = exports.Atomic = exports.Crazyhouse = exports.PositionError = exports.Position = exports.normalizeMove = exports.isImpossibleCheck = exports.IllegalSetup = exports.equalsIgnoreMoves = exports.Chess = exports.castlingSide = exports.Castles = void 0;
4
+ const result_1 = require("@badrap/result");
5
+ const attacks_js_1 = require("./attacks.js");
6
+ const board_js_1 = require("./board.js");
7
+ const chess_js_1 = require("./chess.js");
8
+ Object.defineProperty(exports, "Castles", { enumerable: true, get: function () { return chess_js_1.Castles; } });
9
+ Object.defineProperty(exports, "castlingSide", { enumerable: true, get: function () { return chess_js_1.castlingSide; } });
10
+ Object.defineProperty(exports, "Chess", { enumerable: true, get: function () { return chess_js_1.Chess; } });
11
+ Object.defineProperty(exports, "equalsIgnoreMoves", { enumerable: true, get: function () { return chess_js_1.equalsIgnoreMoves; } });
12
+ Object.defineProperty(exports, "IllegalSetup", { enumerable: true, get: function () { return chess_js_1.IllegalSetup; } });
13
+ Object.defineProperty(exports, "isImpossibleCheck", { enumerable: true, get: function () { return chess_js_1.isImpossibleCheck; } });
14
+ Object.defineProperty(exports, "normalizeMove", { enumerable: true, get: function () { return chess_js_1.normalizeMove; } });
15
+ Object.defineProperty(exports, "Position", { enumerable: true, get: function () { return chess_js_1.Position; } });
16
+ Object.defineProperty(exports, "PositionError", { enumerable: true, get: function () { return chess_js_1.PositionError; } });
17
+ const setup_js_1 = require("./setup.js");
18
+ const squareSet_js_1 = require("./squareSet.js");
19
+ const types_js_1 = require("./types.js");
20
+ const util_js_1 = require("./util.js");
21
+ class Crazyhouse extends chess_js_1.Position {
22
+ constructor() {
23
+ super("crazyhouse");
24
+ }
25
+ reset() {
26
+ super.reset();
27
+ this.pockets = setup_js_1.Material.empty();
28
+ }
29
+ setupUnchecked(setup) {
30
+ super.setupUnchecked(setup);
31
+ this.board.promoted = setup.board.promoted
32
+ .intersect(setup.board.occupied)
33
+ .diff(setup.board.king)
34
+ .diff(setup.board.pawn);
35
+ this.pockets = setup.pockets ? setup.pockets.clone() : setup_js_1.Material.empty();
36
+ }
37
+ static default() {
38
+ const pos = new this();
39
+ pos.reset();
40
+ return pos;
41
+ }
42
+ static fromSetup(setup) {
43
+ const pos = new this();
44
+ pos.setupUnchecked(setup);
45
+ return pos.validate().map((_) => pos);
46
+ }
47
+ clone() {
48
+ return super.clone();
49
+ }
50
+ validate() {
51
+ return super.validate().chain((_) => {
52
+ var _a, _b;
53
+ if ((_a = this.pockets) === null || _a === void 0 ? void 0 : _a.count("king")) {
54
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Kings));
55
+ }
56
+ if ((((_b = this.pockets) === null || _b === void 0 ? void 0 : _b.size()) || 0) + this.board.occupied.size() > 64) {
57
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Variant));
58
+ }
59
+ return result_1.Result.ok(undefined);
60
+ });
61
+ }
62
+ hasInsufficientMaterial(color) {
63
+ // No material can leave the game, but we can easily check this for
64
+ // custom positions.
65
+ if (!this.pockets)
66
+ return super.hasInsufficientMaterial(color);
67
+ return (this.board.occupied.size() + this.pockets.size() <= 3 &&
68
+ this.board.pawn.isEmpty() &&
69
+ this.board.promoted.isEmpty() &&
70
+ this.board.rooksAndQueens().isEmpty() &&
71
+ this.pockets.count("pawn") <= 0 &&
72
+ this.pockets.count("rook") <= 0 &&
73
+ this.pockets.count("queen") <= 0);
74
+ }
75
+ dropDests(ctx) {
76
+ var _a, _b;
77
+ const mask = this.board.occupied
78
+ .complement()
79
+ .intersect(((_a = this.pockets) === null || _a === void 0 ? void 0 : _a[this.turn].hasNonPawns())
80
+ ? squareSet_js_1.SquareSet.full()
81
+ : ((_b = this.pockets) === null || _b === void 0 ? void 0 : _b[this.turn].hasPawns())
82
+ ? squareSet_js_1.SquareSet.backranks().complement()
83
+ : squareSet_js_1.SquareSet.empty());
84
+ ctx = ctx || this.ctx();
85
+ if ((0, util_js_1.defined)(ctx.king) && ctx.checkers.nonEmpty()) {
86
+ const checker = ctx.checkers.singleSquare();
87
+ if (!(0, util_js_1.defined)(checker))
88
+ return squareSet_js_1.SquareSet.empty();
89
+ return mask.intersect((0, attacks_js_1.between)(checker, ctx.king));
90
+ }
91
+ else
92
+ return mask;
93
+ }
94
+ }
95
+ exports.Crazyhouse = Crazyhouse;
96
+ class Atomic extends chess_js_1.Position {
97
+ constructor() {
98
+ super("atomic");
99
+ }
100
+ static default() {
101
+ const pos = new this();
102
+ pos.reset();
103
+ return pos;
104
+ }
105
+ static fromSetup(setup) {
106
+ const pos = new this();
107
+ pos.setupUnchecked(setup);
108
+ return pos.validate().map((_) => pos);
109
+ }
110
+ clone() {
111
+ return super.clone();
112
+ }
113
+ validate() {
114
+ // Like chess, but allow our king to be missing.
115
+ if (this.board.occupied.isEmpty())
116
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Empty));
117
+ if (this.board.king.size() > 2)
118
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Kings));
119
+ const otherKing = this.board.kingOf((0, util_js_1.opposite)(this.turn));
120
+ if (!(0, util_js_1.defined)(otherKing))
121
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Kings));
122
+ if (this.kingAttackers(otherKing, this.turn, this.board.occupied).nonEmpty()) {
123
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.OppositeCheck));
124
+ }
125
+ if (squareSet_js_1.SquareSet.backranks().intersects(this.board.pawn)) {
126
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.PawnsOnBackrank));
127
+ }
128
+ return result_1.Result.ok(undefined);
129
+ }
130
+ kingAttackers(square, attacker, occupied) {
131
+ const attackerKings = this.board.pieces(attacker, "king");
132
+ if (attackerKings.isEmpty() || (0, attacks_js_1.kingAttacks)(square).intersects(attackerKings)) {
133
+ return squareSet_js_1.SquareSet.empty();
134
+ }
135
+ return super.kingAttackers(square, attacker, occupied);
136
+ }
137
+ playCaptureAt(square, captured) {
138
+ super.playCaptureAt(square, captured);
139
+ this.board.take(square);
140
+ for (const explode of (0, attacks_js_1.kingAttacks)(square)
141
+ .intersect(this.board.occupied)
142
+ .diff(this.board.pawn)) {
143
+ const piece = this.board.take(explode);
144
+ if ((piece === null || piece === void 0 ? void 0 : piece.role) === "rook")
145
+ this.castles.discardRook(explode);
146
+ if ((piece === null || piece === void 0 ? void 0 : piece.role) === "king")
147
+ this.castles.discardColor(piece.color);
148
+ }
149
+ }
150
+ hasInsufficientMaterial(color) {
151
+ // Remaining material does not matter if the enemy king is already
152
+ // exploded.
153
+ if (this.board.pieces((0, util_js_1.opposite)(color), "king").isEmpty())
154
+ return false;
155
+ // Bare king cannot mate.
156
+ if (this.board[color].diff(this.board.king).isEmpty())
157
+ return true;
158
+ // As long as the enemy king is not alone, there is always a chance their
159
+ // own pieces explode next to it.
160
+ if (this.board[(0, util_js_1.opposite)(color)].diff(this.board.king).nonEmpty()) {
161
+ // Unless there are only bishops that cannot explode each other.
162
+ if (this.board.occupied.equals(this.board.bishop.union(this.board.king))) {
163
+ if (!this.board.bishop.intersect(this.board.white).intersects(squareSet_js_1.SquareSet.darkSquares())) {
164
+ return !this.board.bishop
165
+ .intersect(this.board.black)
166
+ .intersects(squareSet_js_1.SquareSet.lightSquares());
167
+ }
168
+ if (!this.board.bishop.intersect(this.board.white).intersects(squareSet_js_1.SquareSet.lightSquares())) {
169
+ return !this.board.bishop.intersect(this.board.black).intersects(squareSet_js_1.SquareSet.darkSquares());
170
+ }
171
+ }
172
+ return false;
173
+ }
174
+ // Queen or pawn (future queen) can give mate against bare king.
175
+ if (this.board.queen.nonEmpty() || this.board.pawn.nonEmpty())
176
+ return false;
177
+ // Single knight, bishop or rook cannot mate against bare king.
178
+ if (this.board.knight.union(this.board.bishop).union(this.board.rook).size() === 1)
179
+ return true;
180
+ // If only knights, more than two are required to mate bare king.
181
+ if (this.board.occupied.equals(this.board.knight.union(this.board.king))) {
182
+ return this.board.knight.size() <= 2;
183
+ }
184
+ return false;
185
+ }
186
+ dests(square, ctx) {
187
+ ctx = ctx || this.ctx();
188
+ let dests = squareSet_js_1.SquareSet.empty();
189
+ for (const to of (0, chess_js_1.pseudoDests)(this, square, ctx)) {
190
+ const after = this.clone();
191
+ after.play({ from: square, to });
192
+ const ourKing = after.board.kingOf(this.turn);
193
+ if ((0, util_js_1.defined)(ourKing) &&
194
+ (!(0, util_js_1.defined)(after.board.kingOf(after.turn)) ||
195
+ after.kingAttackers(ourKing, after.turn, after.board.occupied).isEmpty())) {
196
+ dests = dests.with(to);
197
+ }
198
+ }
199
+ return dests;
200
+ }
201
+ isVariantEnd() {
202
+ return !!this.variantOutcome();
203
+ }
204
+ variantOutcome(_ctx) {
205
+ for (const color of types_js_1.COLORS) {
206
+ if (this.board.pieces(color, "king").isEmpty())
207
+ return { winner: (0, util_js_1.opposite)(color) };
208
+ }
209
+ return;
210
+ }
211
+ }
212
+ exports.Atomic = Atomic;
213
+ class Antichess extends chess_js_1.Position {
214
+ constructor() {
215
+ super("antichess");
216
+ }
217
+ reset() {
218
+ super.reset();
219
+ this.castles = chess_js_1.Castles.empty();
220
+ }
221
+ setupUnchecked(setup) {
222
+ super.setupUnchecked(setup);
223
+ this.castles = chess_js_1.Castles.empty();
224
+ }
225
+ static default() {
226
+ const pos = new this();
227
+ pos.reset();
228
+ return pos;
229
+ }
230
+ static fromSetup(setup) {
231
+ const pos = new this();
232
+ pos.setupUnchecked(setup);
233
+ return pos.validate().map((_) => pos);
234
+ }
235
+ clone() {
236
+ return super.clone();
237
+ }
238
+ validate() {
239
+ if (this.board.occupied.isEmpty())
240
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Empty));
241
+ if (squareSet_js_1.SquareSet.backranks().intersects(this.board.pawn)) {
242
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.PawnsOnBackrank));
243
+ }
244
+ return result_1.Result.ok(undefined);
245
+ }
246
+ kingAttackers(_square, _attacker, _occupied) {
247
+ return squareSet_js_1.SquareSet.empty();
248
+ }
249
+ ctx() {
250
+ const ctx = super.ctx();
251
+ if ((0, util_js_1.defined)(this.epSquare) &&
252
+ (0, attacks_js_1.pawnAttacks)((0, util_js_1.opposite)(this.turn), this.epSquare).intersects(this.board.pieces(this.turn, "pawn"))) {
253
+ ctx.mustCapture = true;
254
+ return ctx;
255
+ }
256
+ const enemy = this.board[(0, util_js_1.opposite)(this.turn)];
257
+ for (const from of this.board[this.turn]) {
258
+ if ((0, chess_js_1.pseudoDests)(this, from, ctx).intersects(enemy)) {
259
+ ctx.mustCapture = true;
260
+ return ctx;
261
+ }
262
+ }
263
+ return ctx;
264
+ }
265
+ dests(square, ctx) {
266
+ ctx = ctx || this.ctx();
267
+ const dests = (0, chess_js_1.pseudoDests)(this, square, ctx);
268
+ const enemy = this.board[(0, util_js_1.opposite)(this.turn)];
269
+ return dests.intersect(ctx.mustCapture
270
+ ? (0, util_js_1.defined)(this.epSquare) && this.board.getRole(square) === "pawn"
271
+ ? enemy.with(this.epSquare)
272
+ : enemy
273
+ : squareSet_js_1.SquareSet.full());
274
+ }
275
+ hasInsufficientMaterial(color) {
276
+ if (this.board[color].isEmpty())
277
+ return false;
278
+ if (this.board[(0, util_js_1.opposite)(color)].isEmpty())
279
+ return true;
280
+ if (this.board.occupied.equals(this.board.bishop)) {
281
+ const weSomeOnLight = this.board[color].intersects(squareSet_js_1.SquareSet.lightSquares());
282
+ const weSomeOnDark = this.board[color].intersects(squareSet_js_1.SquareSet.darkSquares());
283
+ const theyAllOnDark = this.board[(0, util_js_1.opposite)(color)].isDisjoint(squareSet_js_1.SquareSet.lightSquares());
284
+ const theyAllOnLight = this.board[(0, util_js_1.opposite)(color)].isDisjoint(squareSet_js_1.SquareSet.darkSquares());
285
+ return (weSomeOnLight && theyAllOnDark) || (weSomeOnDark && theyAllOnLight);
286
+ }
287
+ if (this.board.occupied.equals(this.board.knight) && this.board.occupied.size() === 2) {
288
+ return ((this.board.white.intersects(squareSet_js_1.SquareSet.lightSquares()) !==
289
+ this.board.black.intersects(squareSet_js_1.SquareSet.darkSquares())) !==
290
+ (this.turn === color));
291
+ }
292
+ return false;
293
+ }
294
+ isVariantEnd() {
295
+ return this.board[this.turn].isEmpty();
296
+ }
297
+ variantOutcome(ctx) {
298
+ ctx = ctx || this.ctx();
299
+ if (ctx.variantEnd || this.isStalemate(ctx)) {
300
+ return { winner: this.turn };
301
+ }
302
+ return;
303
+ }
304
+ }
305
+ exports.Antichess = Antichess;
306
+ class KingOfTheHill extends chess_js_1.Position {
307
+ constructor() {
308
+ super("kingofthehill");
309
+ }
310
+ static default() {
311
+ const pos = new this();
312
+ pos.reset();
313
+ return pos;
314
+ }
315
+ static fromSetup(setup) {
316
+ const pos = new this();
317
+ pos.setupUnchecked(setup);
318
+ return pos.validate().map((_) => pos);
319
+ }
320
+ clone() {
321
+ return super.clone();
322
+ }
323
+ hasInsufficientMaterial(_color) {
324
+ return false;
325
+ }
326
+ isVariantEnd() {
327
+ return this.board.king.intersects(squareSet_js_1.SquareSet.center());
328
+ }
329
+ variantOutcome(_ctx) {
330
+ for (const color of types_js_1.COLORS) {
331
+ if (this.board.pieces(color, "king").intersects(squareSet_js_1.SquareSet.center()))
332
+ return { winner: color };
333
+ }
334
+ return;
335
+ }
336
+ }
337
+ exports.KingOfTheHill = KingOfTheHill;
338
+ class ThreeCheck extends chess_js_1.Position {
339
+ constructor() {
340
+ super("3check");
341
+ }
342
+ reset() {
343
+ super.reset();
344
+ this.remainingChecks = setup_js_1.RemainingChecks.default();
345
+ }
346
+ setupUnchecked(setup) {
347
+ var _a;
348
+ super.setupUnchecked(setup);
349
+ this.remainingChecks = ((_a = setup.remainingChecks) === null || _a === void 0 ? void 0 : _a.clone()) || setup_js_1.RemainingChecks.default();
350
+ }
351
+ static default() {
352
+ const pos = new this();
353
+ pos.reset();
354
+ return pos;
355
+ }
356
+ static fromSetup(setup) {
357
+ const pos = new this();
358
+ pos.setupUnchecked(setup);
359
+ return pos.validate().map((_) => pos);
360
+ }
361
+ clone() {
362
+ return super.clone();
363
+ }
364
+ hasInsufficientMaterial(color) {
365
+ return this.board.pieces(color, "king").equals(this.board[color]);
366
+ }
367
+ isVariantEnd() {
368
+ return (!!this.remainingChecks && (this.remainingChecks.white <= 0 || this.remainingChecks.black <= 0));
369
+ }
370
+ variantOutcome(_ctx) {
371
+ if (this.remainingChecks) {
372
+ for (const color of types_js_1.COLORS) {
373
+ if (this.remainingChecks[color] <= 0)
374
+ return { winner: color };
375
+ }
376
+ }
377
+ return;
378
+ }
379
+ }
380
+ exports.ThreeCheck = ThreeCheck;
381
+ const racingKingsBoard = () => {
382
+ const board = board_js_1.Board.empty();
383
+ board.occupied = new squareSet_js_1.SquareSet(0xffff, 0);
384
+ board.promoted = squareSet_js_1.SquareSet.empty();
385
+ board.white = new squareSet_js_1.SquareSet(0xf0f0, 0);
386
+ board.black = new squareSet_js_1.SquareSet(0x0f0f, 0);
387
+ board.pawn = squareSet_js_1.SquareSet.empty();
388
+ board.knight = new squareSet_js_1.SquareSet(0x1818, 0);
389
+ board.bishop = new squareSet_js_1.SquareSet(0x2424, 0);
390
+ board.rook = new squareSet_js_1.SquareSet(0x4242, 0);
391
+ board.queen = new squareSet_js_1.SquareSet(0x0081, 0);
392
+ board.king = new squareSet_js_1.SquareSet(0x8100, 0);
393
+ return board;
394
+ };
395
+ class RacingKings extends chess_js_1.Position {
396
+ constructor() {
397
+ super("racingkings");
398
+ }
399
+ reset() {
400
+ this.board = racingKingsBoard();
401
+ this.pockets = undefined;
402
+ this.turn = "white";
403
+ this.castles = chess_js_1.Castles.empty();
404
+ this.epSquare = undefined;
405
+ this.remainingChecks = undefined;
406
+ this.halfmoves = 0;
407
+ this.fullmoves = 1;
408
+ }
409
+ setupUnchecked(setup) {
410
+ super.setupUnchecked(setup);
411
+ this.castles = chess_js_1.Castles.empty();
412
+ }
413
+ static default() {
414
+ const pos = new this();
415
+ pos.reset();
416
+ return pos;
417
+ }
418
+ static fromSetup(setup) {
419
+ const pos = new this();
420
+ pos.setupUnchecked(setup);
421
+ return pos.validate().map((_) => pos);
422
+ }
423
+ clone() {
424
+ return super.clone();
425
+ }
426
+ validate() {
427
+ if (this.isCheck() || this.board.pawn.nonEmpty())
428
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Variant));
429
+ return super.validate();
430
+ }
431
+ dests(square, ctx) {
432
+ ctx = ctx || this.ctx();
433
+ // Kings cannot give check.
434
+ if (square === ctx.king)
435
+ return super.dests(square, ctx);
436
+ // Do not allow giving check.
437
+ let dests = squareSet_js_1.SquareSet.empty();
438
+ for (const to of super.dests(square, ctx)) {
439
+ // Valid, because there are no promotions (or even pawns).
440
+ const move = { from: square, to };
441
+ const after = this.clone();
442
+ after.play(move);
443
+ if (!after.isCheck())
444
+ dests = dests.with(to);
445
+ }
446
+ return dests;
447
+ }
448
+ hasInsufficientMaterial(_color) {
449
+ return false;
450
+ }
451
+ isVariantEnd() {
452
+ const goal = squareSet_js_1.SquareSet.fromRank(7);
453
+ const inGoal = this.board.king.intersect(goal);
454
+ if (inGoal.isEmpty())
455
+ return false;
456
+ if (this.turn === "white" || inGoal.intersects(this.board.black))
457
+ return true;
458
+ // White has reached the backrank. Check if black can catch up.
459
+ const blackKing = this.board.kingOf("black");
460
+ if ((0, util_js_1.defined)(blackKing)) {
461
+ const occ = this.board.occupied.without(blackKing);
462
+ for (const target of (0, attacks_js_1.kingAttacks)(blackKing).intersect(goal).diff(this.board.black)) {
463
+ if (this.kingAttackers(target, "white", occ).isEmpty())
464
+ return false;
465
+ }
466
+ }
467
+ return true;
468
+ }
469
+ variantOutcome(ctx) {
470
+ if (ctx ? !ctx.variantEnd : !this.isVariantEnd())
471
+ return;
472
+ const goal = squareSet_js_1.SquareSet.fromRank(7);
473
+ const blackInGoal = this.board.pieces("black", "king").intersects(goal);
474
+ const whiteInGoal = this.board.pieces("white", "king").intersects(goal);
475
+ if (blackInGoal && !whiteInGoal)
476
+ return { winner: "black" };
477
+ if (whiteInGoal && !blackInGoal)
478
+ return { winner: "white" };
479
+ return { winner: undefined };
480
+ }
481
+ }
482
+ exports.RacingKings = RacingKings;
483
+ const hordeBoard = () => {
484
+ const board = board_js_1.Board.empty();
485
+ board.occupied = new squareSet_js_1.SquareSet(4294967295, 4294901862);
486
+ board.promoted = squareSet_js_1.SquareSet.empty();
487
+ board.white = new squareSet_js_1.SquareSet(4294967295, 102);
488
+ board.black = new squareSet_js_1.SquareSet(0, 4294901760);
489
+ board.pawn = new squareSet_js_1.SquareSet(4294967295, 16711782);
490
+ board.knight = new squareSet_js_1.SquareSet(0, 1107296256);
491
+ board.bishop = new squareSet_js_1.SquareSet(0, 603979776);
492
+ board.rook = new squareSet_js_1.SquareSet(0, 2164260864);
493
+ board.queen = new squareSet_js_1.SquareSet(0, 134217728);
494
+ board.king = new squareSet_js_1.SquareSet(0, 268435456);
495
+ return board;
496
+ };
497
+ class Horde extends chess_js_1.Position {
498
+ constructor() {
499
+ super("horde");
500
+ }
501
+ reset() {
502
+ this.board = hordeBoard();
503
+ this.pockets = undefined;
504
+ this.turn = "white";
505
+ this.castles = chess_js_1.Castles.default();
506
+ this.castles.discardColor("white");
507
+ this.epSquare = undefined;
508
+ this.remainingChecks = undefined;
509
+ this.halfmoves = 0;
510
+ this.fullmoves = 1;
511
+ }
512
+ static default() {
513
+ const pos = new this();
514
+ pos.reset();
515
+ return pos;
516
+ }
517
+ static fromSetup(setup) {
518
+ const pos = new this();
519
+ pos.setupUnchecked(setup);
520
+ return pos.validate().map((_) => pos);
521
+ }
522
+ clone() {
523
+ return super.clone();
524
+ }
525
+ validate() {
526
+ if (this.board.occupied.isEmpty())
527
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Empty));
528
+ if (this.board.king.size() !== 1)
529
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.Kings));
530
+ const otherKing = this.board.kingOf((0, util_js_1.opposite)(this.turn));
531
+ if ((0, util_js_1.defined)(otherKing) &&
532
+ this.kingAttackers(otherKing, this.turn, this.board.occupied).nonEmpty()) {
533
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.OppositeCheck));
534
+ }
535
+ for (const color of types_js_1.COLORS) {
536
+ const backranks = this.board.pieces(color, "king").isEmpty()
537
+ ? squareSet_js_1.SquareSet.backrank((0, util_js_1.opposite)(color))
538
+ : squareSet_js_1.SquareSet.backranks();
539
+ if (this.board.pieces(color, "pawn").intersects(backranks)) {
540
+ return result_1.Result.err(new chess_js_1.PositionError(chess_js_1.IllegalSetup.PawnsOnBackrank));
541
+ }
542
+ }
543
+ return result_1.Result.ok(undefined);
544
+ }
545
+ hasInsufficientMaterial(color) {
546
+ // The side with the king can always win by capturing the horde.
547
+ if (this.board.pieces(color, "king").nonEmpty())
548
+ return false;
549
+ const oppositeSquareColor = (squareColor) => squareColor === "light" ? "dark" : "light";
550
+ const coloredSquares = (squareColor) => squareColor === "light" ? squareSet_js_1.SquareSet.lightSquares() : squareSet_js_1.SquareSet.darkSquares();
551
+ const hasBishopPair = (side) => {
552
+ const bishops = this.board.pieces(side, "bishop");
553
+ return (bishops.intersects(squareSet_js_1.SquareSet.darkSquares()) && bishops.intersects(squareSet_js_1.SquareSet.lightSquares()));
554
+ };
555
+ // By this point: color is the horde.
556
+ // Based on
557
+ // https://github.com/stevepapazis/horde-insufficient-material-tests.
558
+ const horde = setup_js_1.MaterialSide.fromBoard(this.board, color);
559
+ const hordeBishops = (squareColor) => coloredSquares(squareColor).intersect(this.board.pieces(color, "bishop")).size();
560
+ const hordeBishopColor = hordeBishops("light") >= 1 ? "light" : "dark";
561
+ const hordeNum = horde.pawn +
562
+ horde.knight +
563
+ horde.rook +
564
+ horde.queen +
565
+ Math.min(hordeBishops("dark"), 2) +
566
+ Math.min(hordeBishops("light"), 2);
567
+ const pieces = setup_js_1.MaterialSide.fromBoard(this.board, (0, util_js_1.opposite)(color));
568
+ const piecesBishops = (squareColor) => coloredSquares(squareColor)
569
+ .intersect(this.board.pieces((0, util_js_1.opposite)(color), "bishop"))
570
+ .size();
571
+ const piecesNum = pieces.size();
572
+ const piecesOfRoleNot = (piece) => piecesNum - piece;
573
+ if (hordeNum === 0)
574
+ return true;
575
+ if (hordeNum >= 4) {
576
+ // Four or more pieces can always deliver mate.
577
+ return false;
578
+ }
579
+ if ((horde.pawn >= 1 || horde.queen >= 1) && hordeNum >= 2) {
580
+ // Pawns/queens are never insufficient material when paired with any other
581
+ // piece (a pawn promotes to a queen and delivers mate).
582
+ return false;
583
+ }
584
+ if (horde.rook >= 1 && hordeNum >= 2) {
585
+ // A rook is insufficient material only when it is paired with a bishop
586
+ // against a lone king. The horde can mate in any other case.
587
+ // A rook on A1 and a bishop on C3 mate a king on B1 when there is a
588
+ // friendly pawn/opposite-color-bishop/rook/queen on C2.
589
+ // A rook on B8 and a bishop C3 mate a king on A1 when there is a friendly
590
+ // knight on A2.
591
+ if (!(hordeNum === 2 &&
592
+ horde.rook === 1 &&
593
+ horde.bishop === 1 &&
594
+ piecesOfRoleNot(piecesBishops(hordeBishopColor)) === 1)) {
595
+ return false;
596
+ }
597
+ }
598
+ if (hordeNum === 1) {
599
+ if (piecesNum === 1) {
600
+ // A lone piece cannot mate a lone king.
601
+ return true;
602
+ }
603
+ else if (horde.queen === 1) {
604
+ // The horde has a lone queen.
605
+ // A lone queen mates a king on A1 bounded by:
606
+ // -- a pawn/rook on A2
607
+ // -- two same color bishops on A2, B1
608
+ // We ignore every other mating case, since it can be reduced to
609
+ // the two previous cases (e.g. a black pawn on A2 and a black
610
+ // bishop on B1).
611
+ return !(pieces.pawn >= 1 ||
612
+ pieces.rook >= 1 ||
613
+ piecesBishops("light") >= 2 ||
614
+ piecesBishops("dark") >= 2);
615
+ }
616
+ else if (horde.pawn === 1) {
617
+ // Promote the pawn to a queen or a knight and check whether white
618
+ // can mate.
619
+ const pawnSquare = this.board.pieces(color, "pawn").last();
620
+ const promoteToQueen = this.clone();
621
+ promoteToQueen.board.set(pawnSquare, { color, role: "queen" });
622
+ const promoteToKnight = this.clone();
623
+ promoteToKnight.board.set(pawnSquare, { color, role: "knight" });
624
+ return (promoteToQueen.hasInsufficientMaterial(color) &&
625
+ promoteToKnight.hasInsufficientMaterial(color));
626
+ }
627
+ else if (horde.rook === 1) {
628
+ // A lone rook mates a king on A8 bounded by a pawn/rook on A7 and a
629
+ // pawn/knight on B7. We ignore every other case, since it can be
630
+ // reduced to the two previous cases.
631
+ // (e.g. three pawns on A7, B7, C7)
632
+ return !(pieces.pawn >= 2 ||
633
+ (pieces.rook >= 1 && pieces.pawn >= 1) ||
634
+ (pieces.rook >= 1 && pieces.knight >= 1) ||
635
+ (pieces.pawn >= 1 && pieces.knight >= 1));
636
+ }
637
+ else if (horde.bishop === 1) {
638
+ // The horde has a lone bishop.
639
+ return !(
640
+ // The king can be mated on A1 if there is a pawn/opposite-color-bishop
641
+ // on A2 and an opposite-color-bishop on B1.
642
+ // If black has two or more pawns, white gets the benefit of the doubt;
643
+ // there is an outside chance that white promotes its pawns to
644
+ // opposite-color-bishops and selfmates theirself.
645
+ // Every other case that the king is mated by the bishop requires that
646
+ // black has two pawns or two opposite-color-bishop or a pawn and an
647
+ // opposite-color-bishop.
648
+ // For example a king on A3 can be mated if there is
649
+ // a pawn/opposite-color-bishop on A4, a pawn/opposite-color-bishop on
650
+ // B3, a pawn/bishop/rook/queen on A2 and any other piece on B2.
651
+ (piecesBishops(oppositeSquareColor(hordeBishopColor)) >= 2 ||
652
+ (piecesBishops(oppositeSquareColor(hordeBishopColor)) >= 1 && pieces.pawn >= 1) ||
653
+ pieces.pawn >= 2));
654
+ }
655
+ else if (horde.knight === 1) {
656
+ // The horde has a lone knight.
657
+ return !(
658
+ // The king on A1 can be smother mated by a knight on C2 if there is
659
+ // a pawn/knight/bishop on B2, a knight/rook on B1 and any other piece
660
+ // on A2.
661
+ // Moreover, when black has four or more pieces and two of them are
662
+ // pawns, black can promote their pawns and selfmate theirself.
663
+ (piecesNum >= 4 &&
664
+ (pieces.knight >= 2 ||
665
+ pieces.pawn >= 2 ||
666
+ (pieces.rook >= 1 && pieces.knight >= 1) ||
667
+ (pieces.rook >= 1 && pieces.bishop >= 1) ||
668
+ (pieces.knight >= 1 && pieces.bishop >= 1) ||
669
+ (pieces.rook >= 1 && pieces.pawn >= 1) ||
670
+ (pieces.knight >= 1 && pieces.pawn >= 1) ||
671
+ (pieces.bishop >= 1 && pieces.pawn >= 1) ||
672
+ (hasBishopPair((0, util_js_1.opposite)(color)) && pieces.pawn >= 1)) &&
673
+ (piecesBishops("dark") < 2 || piecesOfRoleNot(piecesBishops("dark")) >= 3) &&
674
+ (piecesBishops("light") < 2 || piecesOfRoleNot(piecesBishops("light")) >= 3)));
675
+ }
676
+ // By this point, we only need to deal with white's minor pieces.
677
+ }
678
+ else if (hordeNum === 2) {
679
+ if (piecesNum === 1) {
680
+ // Two minor pieces cannot mate a lone king.
681
+ return true;
682
+ }
683
+ else if (horde.knight === 2) {
684
+ // A king on A1 is mated by two knights, if it is obstructed by a
685
+ // pawn/bishop/knight on B2. On the other hand, if black only has
686
+ // major pieces it is a draw.
687
+ return pieces.pawn + pieces.bishop + pieces.knight < 1;
688
+ }
689
+ else if (hasBishopPair(color)) {
690
+ return !(
691
+ // A king on A1 obstructed by a pawn/bishop on A2 is mated
692
+ // by the bishop pair.
693
+ (pieces.pawn >= 1 ||
694
+ pieces.bishop >= 1 ||
695
+ // A pawn/bishop/knight on B4, a pawn/bishop/rook/queen on
696
+ // A4 and the king on A3 enable Boden's mate by the bishop
697
+ // pair. In every other case white cannot win.
698
+ (pieces.knight >= 1 && pieces.rook + pieces.queen >= 1)));
699
+ }
700
+ else if (horde.bishop >= 1 && horde.knight >= 1) {
701
+ // The horde has a bishop and a knight.
702
+ return !(
703
+ // A king on A1 obstructed by a pawn/opposite-color-bishop on
704
+ // A2 is mated by a knight on D2 and a bishop on C3.
705
+ (pieces.pawn >= 1 ||
706
+ piecesBishops(oppositeSquareColor(hordeBishopColor)) >= 1 ||
707
+ // A king on A1 bounded by two friendly pieces on A2 and B1 is
708
+ // mated when the knight moves from D4 to C2 so that both the
709
+ // knight and the bishop deliver check.
710
+ piecesOfRoleNot(piecesBishops(hordeBishopColor)) >= 3));
711
+ }
712
+ else {
713
+ // The horde has two or more bishops on the same color.
714
+ // White can only win if black has enough material to obstruct
715
+ // the squares of the opposite color around the king.
716
+ return !(
717
+ // A king on A1 obstructed by a pawn/opposite-bishop/knight
718
+ // on A2 and a opposite-bishop/knight on B1 is mated by two
719
+ // bishops on B2 and C3. This position is theoretically
720
+ // achievable even when black has two pawns or when they
721
+ // have a pawn and an opposite color bishop.
722
+ ((pieces.pawn >= 1 && piecesBishops(oppositeSquareColor(hordeBishopColor)) >= 1) ||
723
+ (pieces.pawn >= 1 && pieces.knight >= 1) ||
724
+ (piecesBishops(oppositeSquareColor(hordeBishopColor)) >= 1 && pieces.knight >= 1) ||
725
+ piecesBishops(oppositeSquareColor(hordeBishopColor)) >= 2 ||
726
+ pieces.knight >= 2 ||
727
+ pieces.pawn >= 2)
728
+ // In every other case, white can only draw.
729
+ );
730
+ }
731
+ }
732
+ else if (hordeNum === 3) {
733
+ // A king in the corner is mated by two knights and a bishop or three
734
+ // knights or the bishop pair and a knight/bishop.
735
+ if ((horde.knight === 2 && horde.bishop === 1) ||
736
+ horde.knight === 3 ||
737
+ hasBishopPair(color)) {
738
+ return false;
739
+ }
740
+ else {
741
+ // White has two same color bishops and a knight.
742
+ // A king on A1 is mated by a bishop on B2, a bishop on C1 and a
743
+ // knight on C3, as long as there is another black piece to waste
744
+ // a tempo.
745
+ return piecesNum === 1;
746
+ }
747
+ }
748
+ return true;
749
+ }
750
+ isVariantEnd() {
751
+ return this.board.white.isEmpty() || this.board.black.isEmpty();
752
+ }
753
+ variantOutcome(_ctx) {
754
+ if (this.board.white.isEmpty())
755
+ return { winner: "black" };
756
+ if (this.board.black.isEmpty())
757
+ return { winner: "white" };
758
+ return;
759
+ }
760
+ }
761
+ exports.Horde = Horde;
762
+ const defaultPosition = (rules) => {
763
+ switch (rules) {
764
+ case "chess":
765
+ return chess_js_1.Chess.default();
766
+ case "antichess":
767
+ return Antichess.default();
768
+ case "atomic":
769
+ return Atomic.default();
770
+ case "horde":
771
+ return Horde.default();
772
+ case "racingkings":
773
+ return RacingKings.default();
774
+ case "kingofthehill":
775
+ return KingOfTheHill.default();
776
+ case "3check":
777
+ return ThreeCheck.default();
778
+ case "crazyhouse":
779
+ return Crazyhouse.default();
780
+ }
781
+ };
782
+ exports.defaultPosition = defaultPosition;
783
+ const setupPosition = (rules, setup) => {
784
+ switch (rules) {
785
+ case "chess":
786
+ return chess_js_1.Chess.fromSetup(setup);
787
+ case "antichess":
788
+ return Antichess.fromSetup(setup);
789
+ case "atomic":
790
+ return Atomic.fromSetup(setup);
791
+ case "horde":
792
+ return Horde.fromSetup(setup);
793
+ case "racingkings":
794
+ return RacingKings.fromSetup(setup);
795
+ case "kingofthehill":
796
+ return KingOfTheHill.fromSetup(setup);
797
+ case "3check":
798
+ return ThreeCheck.fromSetup(setup);
799
+ case "crazyhouse":
800
+ return Crazyhouse.fromSetup(setup);
801
+ }
802
+ };
803
+ exports.setupPosition = setupPosition;
804
+ const isStandardMaterial = (pos) => {
805
+ var _a, _b, _c, _d, _e;
806
+ switch (pos.rules) {
807
+ case "chess":
808
+ case "antichess":
809
+ case "atomic":
810
+ case "kingofthehill":
811
+ case "3check":
812
+ return types_js_1.COLORS.every((color) => (0, chess_js_1.isStandardMaterialSide)(pos.board, color));
813
+ case "crazyhouse": {
814
+ const promoted = pos.board.promoted;
815
+ return (promoted.size() + pos.board.pawn.size() + (((_a = pos.pockets) === null || _a === void 0 ? void 0 : _a.count("pawn")) || 0) <= 16 &&
816
+ pos.board.knight.diff(promoted).size() + (((_b = pos.pockets) === null || _b === void 0 ? void 0 : _b.count("knight")) || 0) <= 4 &&
817
+ pos.board.bishop.diff(promoted).size() + (((_c = pos.pockets) === null || _c === void 0 ? void 0 : _c.count("bishop")) || 0) <= 4 &&
818
+ pos.board.rook.diff(promoted).size() + (((_d = pos.pockets) === null || _d === void 0 ? void 0 : _d.count("rook")) || 0) <= 4 &&
819
+ pos.board.queen.diff(promoted).size() + (((_e = pos.pockets) === null || _e === void 0 ? void 0 : _e.count("queen")) || 0) <= 2);
820
+ }
821
+ case "horde":
822
+ return types_js_1.COLORS.every((color) => pos.board.pieces(color, "king").nonEmpty()
823
+ ? (0, chess_js_1.isStandardMaterialSide)(pos.board, color)
824
+ : pos.board[color].size() <= 36);
825
+ case "racingkings":
826
+ return types_js_1.COLORS.every((color) => pos.board.pieces(color, "knight").size() <= 2 &&
827
+ pos.board.pieces(color, "bishop").size() <= 2 &&
828
+ pos.board.pieces(color, "rook").size() <= 2 &&
829
+ pos.board.pieces(color, "queen").size() <= 1);
830
+ }
831
+ };
832
+ exports.isStandardMaterial = isStandardMaterial;
833
+ //# sourceMappingURL=variant.js.map