@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.
- package/LICENSE.txt +674 -0
- package/README.md +61 -0
- package/dist/cjs/attacks.js +152 -0
- package/dist/cjs/attacks.js.map +1 -0
- package/dist/cjs/board.js +143 -0
- package/dist/cjs/board.js.map +1 -0
- package/dist/cjs/chess.js +638 -0
- package/dist/cjs/chess.js.map +1 -0
- package/dist/cjs/compat.js +89 -0
- package/dist/cjs/compat.js.map +1 -0
- package/dist/cjs/debug.js +103 -0
- package/dist/cjs/debug.js.map +1 -0
- package/dist/cjs/fen.js +325 -0
- package/dist/cjs/fen.js.map +1 -0
- package/dist/cjs/index.js +94 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/pgn.js +796 -0
- package/dist/cjs/pgn.js.map +1 -0
- package/dist/cjs/san.js +174 -0
- package/dist/cjs/san.js.map +1 -0
- package/dist/cjs/setup.js +167 -0
- package/dist/cjs/setup.js.map +1 -0
- package/dist/cjs/squareSet.js +206 -0
- package/dist/cjs/squareSet.js.map +1 -0
- package/dist/cjs/transform.js +57 -0
- package/dist/cjs/transform.js.map +1 -0
- package/dist/cjs/types.js +24 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/util.js +104 -0
- package/dist/cjs/util.js.map +1 -0
- package/dist/cjs/variant.js +833 -0
- package/dist/cjs/variant.js.map +1 -0
- package/dist/esm/attacks.js +140 -0
- package/dist/esm/attacks.js.map +1 -0
- package/dist/esm/board.js +138 -0
- package/dist/esm/board.js.map +1 -0
- package/dist/esm/chess.js +624 -0
- package/dist/esm/chess.js.map +1 -0
- package/dist/esm/compat.js +81 -0
- package/dist/esm/compat.js.map +1 -0
- package/dist/esm/debug.js +94 -0
- package/dist/esm/debug.js.map +1 -0
- package/dist/esm/fen.js +308 -0
- package/dist/esm/fen.js.map +1 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/pgn.js +769 -0
- package/dist/esm/pgn.js.map +1 -0
- package/dist/esm/san.js +167 -0
- package/dist/esm/san.js.map +1 -0
- package/dist/esm/setup.js +157 -0
- package/dist/esm/setup.js.map +1 -0
- package/dist/esm/squareSet.js +202 -0
- package/dist/esm/squareSet.js.map +1 -0
- package/dist/esm/transform.js +48 -0
- package/dist/esm/transform.js.map +1 -0
- package/dist/esm/types.js +19 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/util.js +87 -0
- package/dist/esm/util.js.map +1 -0
- package/dist/esm/variant.js +812 -0
- package/dist/esm/variant.js.map +1 -0
- package/dist/types/attacks.d.ts +58 -0
- package/dist/types/board.d.ts +62 -0
- package/dist/types/chess.d.ts +82 -0
- package/dist/types/compat.d.ts +26 -0
- package/dist/types/debug.d.ts +10 -0
- package/dist/types/fen.d.ts +40 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/pgn.d.ts +203 -0
- package/dist/types/san.d.ts +6 -0
- package/dist/types/setup.d.ts +65 -0
- package/dist/types/squareSet.d.ts +50 -0
- package/dist/types/transform.d.ts +9 -0
- package/dist/types/types.d.ts +58 -0
- package/dist/types/util.d.ts +21 -0
- package/dist/types/variant.d.ts +92 -0
- package/package.json +86 -0
- package/src/attacks.ts +160 -0
- package/src/board.ts +168 -0
- package/src/chess.ts +687 -0
- package/src/compat.ts +120 -0
- package/src/debug.ts +100 -0
- package/src/fen.ts +328 -0
- package/src/index.ts +85 -0
- package/src/pgn.ts +876 -0
- package/src/san.ts +190 -0
- package/src/setup.ts +203 -0
- package/src/squareSet.ts +243 -0
- package/src/transform.ts +49 -0
- package/src/types.ts +93 -0
- package/src/util.ts +116 -0
- package/src/variant.ts +939 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isImpossibleCheck = exports.isStandardMaterial = exports.isStandardMaterialSide = exports.normalizeMove = exports.castlingSide = exports.equalsIgnoreMoves = exports.pseudoDests = exports.Chess = exports.Position = exports.Castles = exports.PositionError = exports.IllegalSetup = 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 squareSet_js_1 = require("./squareSet.js");
|
|
8
|
+
const types_js_1 = require("./types.js");
|
|
9
|
+
const util_js_1 = require("./util.js");
|
|
10
|
+
var IllegalSetup;
|
|
11
|
+
(function (IllegalSetup) {
|
|
12
|
+
IllegalSetup["Empty"] = "ERR_EMPTY";
|
|
13
|
+
IllegalSetup["OppositeCheck"] = "ERR_OPPOSITE_CHECK";
|
|
14
|
+
IllegalSetup["PawnsOnBackrank"] = "ERR_PAWNS_ON_BACKRANK";
|
|
15
|
+
IllegalSetup["Kings"] = "ERR_KINGS";
|
|
16
|
+
IllegalSetup["Variant"] = "ERR_VARIANT";
|
|
17
|
+
})(IllegalSetup || (exports.IllegalSetup = IllegalSetup = {}));
|
|
18
|
+
class PositionError extends Error {
|
|
19
|
+
}
|
|
20
|
+
exports.PositionError = PositionError;
|
|
21
|
+
const attacksTo = (square, attacker, board, occupied) => board[attacker].intersect((0, attacks_js_1.rookAttacks)(square, occupied)
|
|
22
|
+
.intersect(board.rooksAndQueens())
|
|
23
|
+
.union((0, attacks_js_1.bishopAttacks)(square, occupied).intersect(board.bishopsAndQueens()))
|
|
24
|
+
.union((0, attacks_js_1.knightAttacks)(square).intersect(board.knight))
|
|
25
|
+
.union((0, attacks_js_1.kingAttacks)(square).intersect(board.king))
|
|
26
|
+
.union((0, attacks_js_1.pawnAttacks)((0, util_js_1.opposite)(attacker), square).intersect(board.pawn)));
|
|
27
|
+
class Castles {
|
|
28
|
+
constructor() { }
|
|
29
|
+
static default() {
|
|
30
|
+
const castles = new Castles();
|
|
31
|
+
castles.castlingRights = squareSet_js_1.SquareSet.corners();
|
|
32
|
+
castles.rook = {
|
|
33
|
+
white: { a: 0, h: 7 },
|
|
34
|
+
black: { a: 56, h: 63 },
|
|
35
|
+
};
|
|
36
|
+
castles.path = {
|
|
37
|
+
white: { a: new squareSet_js_1.SquareSet(0xe, 0), h: new squareSet_js_1.SquareSet(0x60, 0) },
|
|
38
|
+
black: { a: new squareSet_js_1.SquareSet(0, 0x0e000000), h: new squareSet_js_1.SquareSet(0, 0x60000000) },
|
|
39
|
+
};
|
|
40
|
+
return castles;
|
|
41
|
+
}
|
|
42
|
+
static empty() {
|
|
43
|
+
const castles = new Castles();
|
|
44
|
+
castles.castlingRights = squareSet_js_1.SquareSet.empty();
|
|
45
|
+
castles.rook = {
|
|
46
|
+
white: { a: undefined, h: undefined },
|
|
47
|
+
black: { a: undefined, h: undefined },
|
|
48
|
+
};
|
|
49
|
+
castles.path = {
|
|
50
|
+
white: { a: squareSet_js_1.SquareSet.empty(), h: squareSet_js_1.SquareSet.empty() },
|
|
51
|
+
black: { a: squareSet_js_1.SquareSet.empty(), h: squareSet_js_1.SquareSet.empty() },
|
|
52
|
+
};
|
|
53
|
+
return castles;
|
|
54
|
+
}
|
|
55
|
+
clone() {
|
|
56
|
+
const castles = new Castles();
|
|
57
|
+
castles.castlingRights = this.castlingRights;
|
|
58
|
+
castles.rook = {
|
|
59
|
+
white: { a: this.rook.white.a, h: this.rook.white.h },
|
|
60
|
+
black: { a: this.rook.black.a, h: this.rook.black.h },
|
|
61
|
+
};
|
|
62
|
+
castles.path = {
|
|
63
|
+
white: { a: this.path.white.a, h: this.path.white.h },
|
|
64
|
+
black: { a: this.path.black.a, h: this.path.black.h },
|
|
65
|
+
};
|
|
66
|
+
return castles;
|
|
67
|
+
}
|
|
68
|
+
add(color, side, king, rook) {
|
|
69
|
+
const kingTo = (0, util_js_1.kingCastlesTo)(color, side);
|
|
70
|
+
const rookTo = (0, util_js_1.rookCastlesTo)(color, side);
|
|
71
|
+
this.castlingRights = this.castlingRights.with(rook);
|
|
72
|
+
this.rook[color][side] = rook;
|
|
73
|
+
this.path[color][side] = (0, attacks_js_1.between)(rook, rookTo)
|
|
74
|
+
.with(rookTo)
|
|
75
|
+
.union((0, attacks_js_1.between)(king, kingTo).with(kingTo))
|
|
76
|
+
.without(king)
|
|
77
|
+
.without(rook);
|
|
78
|
+
}
|
|
79
|
+
static fromSetup(setup) {
|
|
80
|
+
const castles = Castles.empty();
|
|
81
|
+
const rooks = setup.castlingRights.intersect(setup.board.rook);
|
|
82
|
+
for (const color of types_js_1.COLORS) {
|
|
83
|
+
const backrank = squareSet_js_1.SquareSet.backrank(color);
|
|
84
|
+
const king = setup.board.kingOf(color);
|
|
85
|
+
if (!(0, util_js_1.defined)(king) || !backrank.has(king))
|
|
86
|
+
continue;
|
|
87
|
+
const side = rooks.intersect(setup.board[color]).intersect(backrank);
|
|
88
|
+
const aSide = side.first();
|
|
89
|
+
if ((0, util_js_1.defined)(aSide) && aSide < king)
|
|
90
|
+
castles.add(color, "a", king, aSide);
|
|
91
|
+
const hSide = side.last();
|
|
92
|
+
if ((0, util_js_1.defined)(hSide) && king < hSide)
|
|
93
|
+
castles.add(color, "h", king, hSide);
|
|
94
|
+
}
|
|
95
|
+
return castles;
|
|
96
|
+
}
|
|
97
|
+
discardRook(square) {
|
|
98
|
+
if (this.castlingRights.has(square)) {
|
|
99
|
+
this.castlingRights = this.castlingRights.without(square);
|
|
100
|
+
for (const color of types_js_1.COLORS) {
|
|
101
|
+
for (const side of types_js_1.CASTLING_SIDES) {
|
|
102
|
+
if (this.rook[color][side] === square)
|
|
103
|
+
this.rook[color][side] = undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
discardColor(color) {
|
|
109
|
+
this.castlingRights = this.castlingRights.diff(squareSet_js_1.SquareSet.backrank(color));
|
|
110
|
+
this.rook[color].a = undefined;
|
|
111
|
+
this.rook[color].h = undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.Castles = Castles;
|
|
115
|
+
class Position {
|
|
116
|
+
constructor(rules) {
|
|
117
|
+
this.rules = rules;
|
|
118
|
+
}
|
|
119
|
+
reset() {
|
|
120
|
+
this.board = board_js_1.Board.default();
|
|
121
|
+
this.pockets = undefined;
|
|
122
|
+
this.turn = "white";
|
|
123
|
+
this.castles = Castles.default();
|
|
124
|
+
this.epSquare = undefined;
|
|
125
|
+
this.remainingChecks = undefined;
|
|
126
|
+
this.halfmoves = 0;
|
|
127
|
+
this.fullmoves = 1;
|
|
128
|
+
}
|
|
129
|
+
setupUnchecked(setup) {
|
|
130
|
+
this.board = setup.board.clone();
|
|
131
|
+
this.board.promoted = squareSet_js_1.SquareSet.empty();
|
|
132
|
+
this.pockets = undefined;
|
|
133
|
+
this.turn = setup.turn;
|
|
134
|
+
this.castles = Castles.fromSetup(setup);
|
|
135
|
+
this.epSquare = validEpSquare(this, setup.epSquare);
|
|
136
|
+
this.remainingChecks = undefined;
|
|
137
|
+
this.halfmoves = setup.halfmoves;
|
|
138
|
+
this.fullmoves = setup.fullmoves;
|
|
139
|
+
}
|
|
140
|
+
// When subclassing overwrite at least:
|
|
141
|
+
//
|
|
142
|
+
// - static default()
|
|
143
|
+
// - static fromSetup()
|
|
144
|
+
// - static clone()
|
|
145
|
+
//
|
|
146
|
+
// - dests()
|
|
147
|
+
// - isVariantEnd()
|
|
148
|
+
// - variantOutcome()
|
|
149
|
+
// - hasInsufficientMaterial()
|
|
150
|
+
// - isStandardMaterial()
|
|
151
|
+
kingAttackers(square, attacker, occupied) {
|
|
152
|
+
return attacksTo(square, attacker, this.board, occupied);
|
|
153
|
+
}
|
|
154
|
+
playCaptureAt(square, captured) {
|
|
155
|
+
this.halfmoves = 0;
|
|
156
|
+
if (captured.role === "rook")
|
|
157
|
+
this.castles.discardRook(square);
|
|
158
|
+
if (this.pockets)
|
|
159
|
+
this.pockets[(0, util_js_1.opposite)(captured.color)][captured.promoted ? "pawn" : captured.role]++;
|
|
160
|
+
}
|
|
161
|
+
ctx() {
|
|
162
|
+
const variantEnd = this.isVariantEnd();
|
|
163
|
+
const king = this.board.kingOf(this.turn);
|
|
164
|
+
if (!(0, util_js_1.defined)(king)) {
|
|
165
|
+
return {
|
|
166
|
+
king,
|
|
167
|
+
blockers: squareSet_js_1.SquareSet.empty(),
|
|
168
|
+
checkers: squareSet_js_1.SquareSet.empty(),
|
|
169
|
+
variantEnd,
|
|
170
|
+
mustCapture: false,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const snipers = (0, attacks_js_1.rookAttacks)(king, squareSet_js_1.SquareSet.empty())
|
|
174
|
+
.intersect(this.board.rooksAndQueens())
|
|
175
|
+
.union((0, attacks_js_1.bishopAttacks)(king, squareSet_js_1.SquareSet.empty()).intersect(this.board.bishopsAndQueens()))
|
|
176
|
+
.intersect(this.board[(0, util_js_1.opposite)(this.turn)]);
|
|
177
|
+
let blockers = squareSet_js_1.SquareSet.empty();
|
|
178
|
+
for (const sniper of snipers) {
|
|
179
|
+
const b = (0, attacks_js_1.between)(king, sniper).intersect(this.board.occupied);
|
|
180
|
+
if (!b.moreThanOne())
|
|
181
|
+
blockers = blockers.union(b);
|
|
182
|
+
}
|
|
183
|
+
const checkers = this.kingAttackers(king, (0, util_js_1.opposite)(this.turn), this.board.occupied);
|
|
184
|
+
return {
|
|
185
|
+
king,
|
|
186
|
+
blockers,
|
|
187
|
+
checkers,
|
|
188
|
+
variantEnd,
|
|
189
|
+
mustCapture: false,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
clone() {
|
|
193
|
+
var _a, _b;
|
|
194
|
+
const pos = new this.constructor();
|
|
195
|
+
pos.board = this.board.clone();
|
|
196
|
+
pos.pockets = (_a = this.pockets) === null || _a === void 0 ? void 0 : _a.clone();
|
|
197
|
+
pos.turn = this.turn;
|
|
198
|
+
pos.castles = this.castles.clone();
|
|
199
|
+
pos.epSquare = this.epSquare;
|
|
200
|
+
pos.remainingChecks = (_b = this.remainingChecks) === null || _b === void 0 ? void 0 : _b.clone();
|
|
201
|
+
pos.halfmoves = this.halfmoves;
|
|
202
|
+
pos.fullmoves = this.fullmoves;
|
|
203
|
+
return pos;
|
|
204
|
+
}
|
|
205
|
+
validate() {
|
|
206
|
+
if (this.board.occupied.isEmpty())
|
|
207
|
+
return result_1.Result.err(new PositionError(IllegalSetup.Empty));
|
|
208
|
+
if (this.board.king.size() !== 2)
|
|
209
|
+
return result_1.Result.err(new PositionError(IllegalSetup.Kings));
|
|
210
|
+
if (!(0, util_js_1.defined)(this.board.kingOf(this.turn)))
|
|
211
|
+
return result_1.Result.err(new PositionError(IllegalSetup.Kings));
|
|
212
|
+
const otherKing = this.board.kingOf((0, util_js_1.opposite)(this.turn));
|
|
213
|
+
if (!(0, util_js_1.defined)(otherKing))
|
|
214
|
+
return result_1.Result.err(new PositionError(IllegalSetup.Kings));
|
|
215
|
+
if (this.kingAttackers(otherKing, this.turn, this.board.occupied).nonEmpty()) {
|
|
216
|
+
return result_1.Result.err(new PositionError(IllegalSetup.OppositeCheck));
|
|
217
|
+
}
|
|
218
|
+
if (squareSet_js_1.SquareSet.backranks().intersects(this.board.pawn)) {
|
|
219
|
+
return result_1.Result.err(new PositionError(IllegalSetup.PawnsOnBackrank));
|
|
220
|
+
}
|
|
221
|
+
return result_1.Result.ok(undefined);
|
|
222
|
+
}
|
|
223
|
+
dropDests(_ctx) {
|
|
224
|
+
return squareSet_js_1.SquareSet.empty();
|
|
225
|
+
}
|
|
226
|
+
dests(square, ctx) {
|
|
227
|
+
ctx = ctx || this.ctx();
|
|
228
|
+
if (ctx.variantEnd)
|
|
229
|
+
return squareSet_js_1.SquareSet.empty();
|
|
230
|
+
const piece = this.board.get(square);
|
|
231
|
+
if (!piece || piece.color !== this.turn)
|
|
232
|
+
return squareSet_js_1.SquareSet.empty();
|
|
233
|
+
let pseudo, legal;
|
|
234
|
+
if (piece.role === "pawn") {
|
|
235
|
+
pseudo = (0, attacks_js_1.pawnAttacks)(this.turn, square).intersect(this.board[(0, util_js_1.opposite)(this.turn)]);
|
|
236
|
+
const delta = this.turn === "white" ? 8 : -8;
|
|
237
|
+
const step = square + delta;
|
|
238
|
+
if (0 <= step && step < 64 && !this.board.occupied.has(step)) {
|
|
239
|
+
pseudo = pseudo.with(step);
|
|
240
|
+
const canDoubleStep = this.turn === "white" ? square < 16 : square >= 64 - 16;
|
|
241
|
+
const doubleStep = step + delta;
|
|
242
|
+
if (canDoubleStep && !this.board.occupied.has(doubleStep)) {
|
|
243
|
+
pseudo = pseudo.with(doubleStep);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if ((0, util_js_1.defined)(this.epSquare) && canCaptureEp(this, square, ctx)) {
|
|
247
|
+
legal = squareSet_js_1.SquareSet.fromSquare(this.epSquare);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else if (piece.role === "bishop")
|
|
251
|
+
pseudo = (0, attacks_js_1.bishopAttacks)(square, this.board.occupied);
|
|
252
|
+
else if (piece.role === "knight")
|
|
253
|
+
pseudo = (0, attacks_js_1.knightAttacks)(square);
|
|
254
|
+
else if (piece.role === "rook")
|
|
255
|
+
pseudo = (0, attacks_js_1.rookAttacks)(square, this.board.occupied);
|
|
256
|
+
else if (piece.role === "queen")
|
|
257
|
+
pseudo = (0, attacks_js_1.queenAttacks)(square, this.board.occupied);
|
|
258
|
+
else
|
|
259
|
+
pseudo = (0, attacks_js_1.kingAttacks)(square);
|
|
260
|
+
pseudo = pseudo.diff(this.board[this.turn]);
|
|
261
|
+
if ((0, util_js_1.defined)(ctx.king)) {
|
|
262
|
+
if (piece.role === "king") {
|
|
263
|
+
const occ = this.board.occupied.without(square);
|
|
264
|
+
for (const to of pseudo) {
|
|
265
|
+
if (this.kingAttackers(to, (0, util_js_1.opposite)(this.turn), occ).nonEmpty())
|
|
266
|
+
pseudo = pseudo.without(to);
|
|
267
|
+
}
|
|
268
|
+
return pseudo.union(castlingDest(this, "a", ctx)).union(castlingDest(this, "h", ctx));
|
|
269
|
+
}
|
|
270
|
+
if (ctx.checkers.nonEmpty()) {
|
|
271
|
+
const checker = ctx.checkers.singleSquare();
|
|
272
|
+
if (!(0, util_js_1.defined)(checker))
|
|
273
|
+
return squareSet_js_1.SquareSet.empty();
|
|
274
|
+
pseudo = pseudo.intersect((0, attacks_js_1.between)(checker, ctx.king).with(checker));
|
|
275
|
+
}
|
|
276
|
+
if (ctx.blockers.has(square))
|
|
277
|
+
pseudo = pseudo.intersect((0, attacks_js_1.ray)(square, ctx.king));
|
|
278
|
+
}
|
|
279
|
+
if (legal)
|
|
280
|
+
pseudo = pseudo.union(legal);
|
|
281
|
+
return pseudo;
|
|
282
|
+
}
|
|
283
|
+
isVariantEnd() {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
variantOutcome(_ctx) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
hasInsufficientMaterial(color) {
|
|
290
|
+
if (this.board[color].intersect(this.board.pawn.union(this.board.rooksAndQueens())).nonEmpty())
|
|
291
|
+
return false;
|
|
292
|
+
if (this.board[color].intersects(this.board.knight)) {
|
|
293
|
+
return (this.board[color].size() <= 2 &&
|
|
294
|
+
this.board[(0, util_js_1.opposite)(color)].diff(this.board.king).diff(this.board.queen).isEmpty());
|
|
295
|
+
}
|
|
296
|
+
if (this.board[color].intersects(this.board.bishop)) {
|
|
297
|
+
const sameColor = !this.board.bishop.intersects(squareSet_js_1.SquareSet.darkSquares()) ||
|
|
298
|
+
!this.board.bishop.intersects(squareSet_js_1.SquareSet.lightSquares());
|
|
299
|
+
return sameColor && this.board.pawn.isEmpty() && this.board.knight.isEmpty();
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
// The following should be identical in all subclasses
|
|
304
|
+
toSetup() {
|
|
305
|
+
var _a, _b;
|
|
306
|
+
return {
|
|
307
|
+
board: this.board.clone(),
|
|
308
|
+
pockets: (_a = this.pockets) === null || _a === void 0 ? void 0 : _a.clone(),
|
|
309
|
+
turn: this.turn,
|
|
310
|
+
castlingRights: this.castles.castlingRights,
|
|
311
|
+
epSquare: legalEpSquare(this),
|
|
312
|
+
remainingChecks: (_b = this.remainingChecks) === null || _b === void 0 ? void 0 : _b.clone(),
|
|
313
|
+
halfmoves: Math.min(this.halfmoves, 150),
|
|
314
|
+
fullmoves: Math.min(Math.max(this.fullmoves, 1), 9999),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
isInsufficientMaterial() {
|
|
318
|
+
return types_js_1.COLORS.every((color) => this.hasInsufficientMaterial(color));
|
|
319
|
+
}
|
|
320
|
+
hasDests(ctx) {
|
|
321
|
+
ctx = ctx || this.ctx();
|
|
322
|
+
for (const square of this.board[this.turn]) {
|
|
323
|
+
if (this.dests(square, ctx).nonEmpty())
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
return this.dropDests(ctx).nonEmpty();
|
|
327
|
+
}
|
|
328
|
+
isLegal(move, ctx) {
|
|
329
|
+
if ((0, types_js_1.isDrop)(move)) {
|
|
330
|
+
if (!this.pockets || this.pockets[this.turn][move.role] <= 0)
|
|
331
|
+
return false;
|
|
332
|
+
if (move.role === "pawn" && squareSet_js_1.SquareSet.backranks().has(move.to))
|
|
333
|
+
return false;
|
|
334
|
+
return this.dropDests(ctx).has(move.to);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
if (move.promotion === "pawn")
|
|
338
|
+
return false;
|
|
339
|
+
if (move.promotion === "king" && this.rules !== "antichess")
|
|
340
|
+
return false;
|
|
341
|
+
if (!!move.promotion !== (this.board.pawn.has(move.from) && squareSet_js_1.SquareSet.backranks().has(move.to)))
|
|
342
|
+
return false;
|
|
343
|
+
const dests = this.dests(move.from, ctx);
|
|
344
|
+
return dests.has(move.to) || dests.has((0, exports.normalizeMove)(this, move).to);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
isCheck() {
|
|
348
|
+
const king = this.board.kingOf(this.turn);
|
|
349
|
+
return ((0, util_js_1.defined)(king) && this.kingAttackers(king, (0, util_js_1.opposite)(this.turn), this.board.occupied).nonEmpty());
|
|
350
|
+
}
|
|
351
|
+
isEnd(ctx) {
|
|
352
|
+
if (ctx ? ctx.variantEnd : this.isVariantEnd())
|
|
353
|
+
return true;
|
|
354
|
+
return this.isInsufficientMaterial() || !this.hasDests(ctx);
|
|
355
|
+
}
|
|
356
|
+
isCheckmate(ctx) {
|
|
357
|
+
ctx = ctx || this.ctx();
|
|
358
|
+
return !ctx.variantEnd && ctx.checkers.nonEmpty() && !this.hasDests(ctx);
|
|
359
|
+
}
|
|
360
|
+
isStalemate(ctx) {
|
|
361
|
+
ctx = ctx || this.ctx();
|
|
362
|
+
return !ctx.variantEnd && ctx.checkers.isEmpty() && !this.hasDests(ctx);
|
|
363
|
+
}
|
|
364
|
+
outcome(ctx) {
|
|
365
|
+
const variantOutcome = this.variantOutcome(ctx);
|
|
366
|
+
if (variantOutcome)
|
|
367
|
+
return variantOutcome;
|
|
368
|
+
ctx = ctx || this.ctx();
|
|
369
|
+
if (this.isCheckmate(ctx))
|
|
370
|
+
return { winner: (0, util_js_1.opposite)(this.turn) };
|
|
371
|
+
else if (this.isInsufficientMaterial() || this.isStalemate(ctx))
|
|
372
|
+
return { winner: undefined };
|
|
373
|
+
else
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
allDests(ctx) {
|
|
377
|
+
ctx = ctx || this.ctx();
|
|
378
|
+
const d = new Map();
|
|
379
|
+
if (ctx.variantEnd)
|
|
380
|
+
return d;
|
|
381
|
+
for (const square of this.board[this.turn]) {
|
|
382
|
+
d.set(square, this.dests(square, ctx));
|
|
383
|
+
}
|
|
384
|
+
return d;
|
|
385
|
+
}
|
|
386
|
+
play(move) {
|
|
387
|
+
const turn = this.turn;
|
|
388
|
+
const epSquare = this.epSquare;
|
|
389
|
+
const castling = (0, exports.castlingSide)(this, move);
|
|
390
|
+
this.epSquare = undefined;
|
|
391
|
+
this.halfmoves += 1;
|
|
392
|
+
if (turn === "black")
|
|
393
|
+
this.fullmoves += 1;
|
|
394
|
+
this.turn = (0, util_js_1.opposite)(turn);
|
|
395
|
+
if ((0, types_js_1.isDrop)(move)) {
|
|
396
|
+
this.board.set(move.to, { role: move.role, color: turn });
|
|
397
|
+
if (this.pockets)
|
|
398
|
+
this.pockets[turn][move.role]--;
|
|
399
|
+
if (move.role === "pawn")
|
|
400
|
+
this.halfmoves = 0;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
const piece = this.board.take(move.from);
|
|
404
|
+
if (!piece)
|
|
405
|
+
return;
|
|
406
|
+
let epCapture;
|
|
407
|
+
if (piece.role === "pawn") {
|
|
408
|
+
this.halfmoves = 0;
|
|
409
|
+
if (move.to === epSquare) {
|
|
410
|
+
epCapture = this.board.take(move.to + (turn === "white" ? -8 : 8));
|
|
411
|
+
}
|
|
412
|
+
const delta = move.from - move.to;
|
|
413
|
+
if (Math.abs(delta) === 16 && 8 <= move.from && move.from <= 55) {
|
|
414
|
+
this.epSquare = (move.from + move.to) >> 1;
|
|
415
|
+
}
|
|
416
|
+
if (move.promotion) {
|
|
417
|
+
piece.role = move.promotion;
|
|
418
|
+
piece.promoted = !!this.pockets;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else if (piece.role === "rook") {
|
|
422
|
+
this.castles.discardRook(move.from);
|
|
423
|
+
}
|
|
424
|
+
else if (piece.role === "king") {
|
|
425
|
+
if (castling) {
|
|
426
|
+
const rookFrom = this.castles.rook[turn][castling];
|
|
427
|
+
if ((0, util_js_1.defined)(rookFrom)) {
|
|
428
|
+
const rook = this.board.take(rookFrom);
|
|
429
|
+
this.board.set((0, util_js_1.kingCastlesTo)(turn, castling), piece);
|
|
430
|
+
if (rook)
|
|
431
|
+
this.board.set((0, util_js_1.rookCastlesTo)(turn, castling), rook);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
this.castles.discardColor(turn);
|
|
435
|
+
}
|
|
436
|
+
if (!castling) {
|
|
437
|
+
const capture = this.board.set(move.to, piece) || epCapture;
|
|
438
|
+
if (capture)
|
|
439
|
+
this.playCaptureAt(move.to, capture);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (this.remainingChecks) {
|
|
443
|
+
if (this.isCheck())
|
|
444
|
+
this.remainingChecks[turn] = Math.max(this.remainingChecks[turn] - 1, 0);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
exports.Position = Position;
|
|
449
|
+
class Chess extends Position {
|
|
450
|
+
constructor() {
|
|
451
|
+
super("chess");
|
|
452
|
+
}
|
|
453
|
+
static default() {
|
|
454
|
+
const pos = new this();
|
|
455
|
+
pos.reset();
|
|
456
|
+
return pos;
|
|
457
|
+
}
|
|
458
|
+
static fromSetup(setup) {
|
|
459
|
+
const pos = new this();
|
|
460
|
+
pos.setupUnchecked(setup);
|
|
461
|
+
return pos.validate().map((_) => pos);
|
|
462
|
+
}
|
|
463
|
+
clone() {
|
|
464
|
+
return super.clone();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
exports.Chess = Chess;
|
|
468
|
+
const validEpSquare = (pos, square) => {
|
|
469
|
+
if (!(0, util_js_1.defined)(square))
|
|
470
|
+
return;
|
|
471
|
+
const epRank = pos.turn === "white" ? 5 : 2;
|
|
472
|
+
const forward = pos.turn === "white" ? 8 : -8;
|
|
473
|
+
if ((0, util_js_1.squareRank)(square) !== epRank)
|
|
474
|
+
return;
|
|
475
|
+
if (pos.board.occupied.has(square + forward))
|
|
476
|
+
return;
|
|
477
|
+
const pawn = square - forward;
|
|
478
|
+
if (!pos.board.pawn.has(pawn) || !pos.board[(0, util_js_1.opposite)(pos.turn)].has(pawn))
|
|
479
|
+
return;
|
|
480
|
+
return square;
|
|
481
|
+
};
|
|
482
|
+
const legalEpSquare = (pos) => {
|
|
483
|
+
if (!(0, util_js_1.defined)(pos.epSquare))
|
|
484
|
+
return;
|
|
485
|
+
const ctx = pos.ctx();
|
|
486
|
+
const ourPawns = pos.board.pieces(pos.turn, "pawn");
|
|
487
|
+
const candidates = ourPawns.intersect((0, attacks_js_1.pawnAttacks)((0, util_js_1.opposite)(pos.turn), pos.epSquare));
|
|
488
|
+
for (const candidate of candidates) {
|
|
489
|
+
if (pos.dests(candidate, ctx).has(pos.epSquare))
|
|
490
|
+
return pos.epSquare;
|
|
491
|
+
}
|
|
492
|
+
return;
|
|
493
|
+
};
|
|
494
|
+
const canCaptureEp = (pos, pawnFrom, ctx) => {
|
|
495
|
+
if (!(0, util_js_1.defined)(pos.epSquare))
|
|
496
|
+
return false;
|
|
497
|
+
if (!(0, attacks_js_1.pawnAttacks)(pos.turn, pawnFrom).has(pos.epSquare))
|
|
498
|
+
return false;
|
|
499
|
+
if (!(0, util_js_1.defined)(ctx.king))
|
|
500
|
+
return true;
|
|
501
|
+
const delta = pos.turn === "white" ? 8 : -8;
|
|
502
|
+
const captured = pos.epSquare - delta;
|
|
503
|
+
return pos
|
|
504
|
+
.kingAttackers(ctx.king, (0, util_js_1.opposite)(pos.turn), pos.board.occupied.toggle(pawnFrom).toggle(captured).with(pos.epSquare))
|
|
505
|
+
.without(captured)
|
|
506
|
+
.isEmpty();
|
|
507
|
+
};
|
|
508
|
+
const castlingDest = (pos, side, ctx) => {
|
|
509
|
+
if (!(0, util_js_1.defined)(ctx.king) || ctx.checkers.nonEmpty())
|
|
510
|
+
return squareSet_js_1.SquareSet.empty();
|
|
511
|
+
const rook = pos.castles.rook[pos.turn][side];
|
|
512
|
+
if (!(0, util_js_1.defined)(rook))
|
|
513
|
+
return squareSet_js_1.SquareSet.empty();
|
|
514
|
+
if (pos.castles.path[pos.turn][side].intersects(pos.board.occupied))
|
|
515
|
+
return squareSet_js_1.SquareSet.empty();
|
|
516
|
+
const kingTo = (0, util_js_1.kingCastlesTo)(pos.turn, side);
|
|
517
|
+
const kingPath = (0, attacks_js_1.between)(ctx.king, kingTo);
|
|
518
|
+
const occ = pos.board.occupied.without(ctx.king);
|
|
519
|
+
for (const sq of kingPath) {
|
|
520
|
+
if (pos.kingAttackers(sq, (0, util_js_1.opposite)(pos.turn), occ).nonEmpty())
|
|
521
|
+
return squareSet_js_1.SquareSet.empty();
|
|
522
|
+
}
|
|
523
|
+
const rookTo = (0, util_js_1.rookCastlesTo)(pos.turn, side);
|
|
524
|
+
const after = pos.board.occupied.toggle(ctx.king).toggle(rook).toggle(rookTo);
|
|
525
|
+
if (pos.kingAttackers(kingTo, (0, util_js_1.opposite)(pos.turn), after).nonEmpty())
|
|
526
|
+
return squareSet_js_1.SquareSet.empty();
|
|
527
|
+
return squareSet_js_1.SquareSet.fromSquare(rook);
|
|
528
|
+
};
|
|
529
|
+
const pseudoDests = (pos, square, ctx) => {
|
|
530
|
+
if (ctx.variantEnd)
|
|
531
|
+
return squareSet_js_1.SquareSet.empty();
|
|
532
|
+
const piece = pos.board.get(square);
|
|
533
|
+
if (!piece || piece.color !== pos.turn)
|
|
534
|
+
return squareSet_js_1.SquareSet.empty();
|
|
535
|
+
let pseudo = (0, attacks_js_1.attacks)(piece, square, pos.board.occupied);
|
|
536
|
+
if (piece.role === "pawn") {
|
|
537
|
+
let captureTargets = pos.board[(0, util_js_1.opposite)(pos.turn)];
|
|
538
|
+
if ((0, util_js_1.defined)(pos.epSquare))
|
|
539
|
+
captureTargets = captureTargets.with(pos.epSquare);
|
|
540
|
+
pseudo = pseudo.intersect(captureTargets);
|
|
541
|
+
const delta = pos.turn === "white" ? 8 : -8;
|
|
542
|
+
const step = square + delta;
|
|
543
|
+
if (0 <= step && step < 64 && !pos.board.occupied.has(step)) {
|
|
544
|
+
pseudo = pseudo.with(step);
|
|
545
|
+
const canDoubleStep = pos.turn === "white" ? square < 16 : square >= 64 - 16;
|
|
546
|
+
const doubleStep = step + delta;
|
|
547
|
+
if (canDoubleStep && !pos.board.occupied.has(doubleStep)) {
|
|
548
|
+
pseudo = pseudo.with(doubleStep);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return pseudo;
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
pseudo = pseudo.diff(pos.board[pos.turn]);
|
|
555
|
+
}
|
|
556
|
+
if (square === ctx.king)
|
|
557
|
+
return pseudo.union(castlingDest(pos, "a", ctx)).union(castlingDest(pos, "h", ctx));
|
|
558
|
+
else
|
|
559
|
+
return pseudo;
|
|
560
|
+
};
|
|
561
|
+
exports.pseudoDests = pseudoDests;
|
|
562
|
+
const equalsIgnoreMoves = (left, right) => {
|
|
563
|
+
var _a, _b;
|
|
564
|
+
return left.rules === right.rules &&
|
|
565
|
+
(0, board_js_1.boardEquals)(left.board, right.board) &&
|
|
566
|
+
((right.pockets && ((_a = left.pockets) === null || _a === void 0 ? void 0 : _a.equals(right.pockets))) || (!left.pockets && !right.pockets)) &&
|
|
567
|
+
left.turn === right.turn &&
|
|
568
|
+
left.castles.castlingRights.equals(right.castles.castlingRights) &&
|
|
569
|
+
legalEpSquare(left) === legalEpSquare(right) &&
|
|
570
|
+
((right.remainingChecks && ((_b = left.remainingChecks) === null || _b === void 0 ? void 0 : _b.equals(right.remainingChecks))) ||
|
|
571
|
+
(!left.remainingChecks && !right.remainingChecks));
|
|
572
|
+
};
|
|
573
|
+
exports.equalsIgnoreMoves = equalsIgnoreMoves;
|
|
574
|
+
const castlingSide = (pos, move) => {
|
|
575
|
+
if ((0, types_js_1.isDrop)(move))
|
|
576
|
+
return;
|
|
577
|
+
const delta = move.to - move.from;
|
|
578
|
+
if (Math.abs(delta) !== 2 && !pos.board[pos.turn].has(move.to))
|
|
579
|
+
return;
|
|
580
|
+
if (!pos.board.king.has(move.from))
|
|
581
|
+
return;
|
|
582
|
+
return delta > 0 ? "h" : "a";
|
|
583
|
+
};
|
|
584
|
+
exports.castlingSide = castlingSide;
|
|
585
|
+
const normalizeMove = (pos, move) => {
|
|
586
|
+
const side = (0, exports.castlingSide)(pos, move);
|
|
587
|
+
if (!side)
|
|
588
|
+
return move;
|
|
589
|
+
const rookFrom = pos.castles.rook[pos.turn][side];
|
|
590
|
+
return {
|
|
591
|
+
from: move.from,
|
|
592
|
+
to: (0, util_js_1.defined)(rookFrom) ? rookFrom : move.to,
|
|
593
|
+
};
|
|
594
|
+
};
|
|
595
|
+
exports.normalizeMove = normalizeMove;
|
|
596
|
+
const isStandardMaterialSide = (board, color) => {
|
|
597
|
+
const promoted = Math.max(board.pieces(color, "queen").size() - 1, 0) +
|
|
598
|
+
Math.max(board.pieces(color, "rook").size() - 2, 0) +
|
|
599
|
+
Math.max(board.pieces(color, "knight").size() - 2, 0) +
|
|
600
|
+
Math.max(board.pieces(color, "bishop").intersect(squareSet_js_1.SquareSet.lightSquares()).size() - 1, 0) +
|
|
601
|
+
Math.max(board.pieces(color, "bishop").intersect(squareSet_js_1.SquareSet.darkSquares()).size() - 1, 0);
|
|
602
|
+
return board.pieces(color, "pawn").size() + promoted <= 8;
|
|
603
|
+
};
|
|
604
|
+
exports.isStandardMaterialSide = isStandardMaterialSide;
|
|
605
|
+
const isStandardMaterial = (pos) => types_js_1.COLORS.every((color) => (0, exports.isStandardMaterialSide)(pos.board, color));
|
|
606
|
+
exports.isStandardMaterial = isStandardMaterial;
|
|
607
|
+
const isImpossibleCheck = (pos) => {
|
|
608
|
+
const ourKing = pos.board.kingOf(pos.turn);
|
|
609
|
+
if (!(0, util_js_1.defined)(ourKing))
|
|
610
|
+
return false;
|
|
611
|
+
const checkers = pos.kingAttackers(ourKing, (0, util_js_1.opposite)(pos.turn), pos.board.occupied);
|
|
612
|
+
if (checkers.isEmpty())
|
|
613
|
+
return false;
|
|
614
|
+
if ((0, util_js_1.defined)(pos.epSquare)) {
|
|
615
|
+
// The pushed pawn must be the only checker, or it has uncovered
|
|
616
|
+
// check by a single sliding piece.
|
|
617
|
+
const pushedTo = pos.epSquare ^ 8;
|
|
618
|
+
const pushedFrom = pos.epSquare ^ 24;
|
|
619
|
+
return (checkers.moreThanOne() ||
|
|
620
|
+
(checkers.first() !== pushedTo &&
|
|
621
|
+
pos
|
|
622
|
+
.kingAttackers(ourKing, (0, util_js_1.opposite)(pos.turn), pos.board.occupied.without(pushedTo).with(pushedFrom))
|
|
623
|
+
.nonEmpty()));
|
|
624
|
+
}
|
|
625
|
+
else if (pos.rules === "atomic") {
|
|
626
|
+
// Other king moving away can cause many checks to be given at the same
|
|
627
|
+
// time. Not checking details, or even that the king is close enough.
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
// Sliding checkers aligned with king.
|
|
632
|
+
return (checkers.size() > 2 ||
|
|
633
|
+
(checkers.size() === 2 && (0, attacks_js_1.ray)(checkers.first(), checkers.last()).has(ourKing)) || // Sliding checkers aligned with king
|
|
634
|
+
checkers.intersect(pos.board.steppers()).moreThanOne());
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
exports.isImpossibleCheck = isImpossibleCheck;
|
|
638
|
+
//# sourceMappingURL=chess.js.map
|