1 import { ChessRules
} from "@/base_rules";
3 export class EmpireRules
extends ChessRules
{
5 static get PawnSpecs() {
9 { promotions: [V
.QUEEN
] }
13 static get LoseOnRepetition() {
17 static IsGoodFlags(flags
) {
18 // Only black can castle
19 return !!flags
.match(/^[a-z]{2,2}$/);
23 return (b
[0] == 'w' ? "Empire/" : "") + b
;
26 static GenRandInitFen(randomness
) {
28 return "rnbqkbnr/pppppppp/8/8/8/PPPSSPPP/8/TECDKCET w 0 ah -";
30 // Mapping kingdom --> empire:
39 const baseFen
= ChessRules
.GenRandInitFen(randomness
);
41 baseFen
.substr(0, 24) + "PPPSSPPP/8/" +
42 baseFen
.substr(35, 8).split('').map(p
=> piecesMap
[p
]).join('') +
43 baseFen
.substr(43, 5) + baseFen
.substr(50)
48 return this.castleFlags
['b'].map(V
.CoordToColumn
).join("");
52 this.castleFlags
= { 'b': [-1, -1] };
53 for (let i
= 0; i
< 2; i
++)
54 this.castleFlags
['b'][i
] = V
.ColumnToCoord(fenflags
.charAt(i
));
63 static get CARDINAL() {
69 static get SOLDIER() {
72 // Kaiser is technically a King, so let's keep things simple.
75 return ChessRules
.PIECES
.concat(
76 [V
.TOWER
, V
.EAGLE
, V
.CARDINAL
, V
.DUKE
, V
.SOLDIER
]);
79 getPotentialMovesFrom(sq
) {
81 const piece
= this.getPiece(sq
[0], sq
[1]);
84 moves
= this.getPotentialTowerMoves(sq
);
87 moves
= this.getPotentialEagleMoves(sq
);
90 moves
= this.getPotentialCardinalMoves(sq
);
93 moves
= this.getPotentialDukeMoves(sq
);
96 moves
= this.getPotentialSoldierMoves(sq
);
99 moves
= super.getPotentialMovesFrom(sq
);
103 this.kingPos
['w'][0] != this.kingPos
['b'][0] &&
104 this.kingPos
['w'][1] != this.kingPos
['b'][1]
108 // TODO: factor two next "if" into one (rank/column...)
109 if (this.kingPos
['w'][1] == this.kingPos
['b'][1]) {
110 const colKing
= this.kingPos
['w'][1];
111 let intercept
= 0; //count intercepting pieces
112 let [kingPos1
, kingPos2
] = [this.kingPos
['w'][0], this.kingPos
['b'][0]];
113 if (kingPos1
> kingPos2
) [kingPos1
, kingPos2
] = [kingPos2
, kingPos1
];
114 for (let i
= kingPos1
+ 1; i
< kingPos2
; i
++) {
115 if (this.board
[i
][colKing
] != V
.EMPTY
) intercept
++;
117 if (intercept
>= 2) return moves
;
118 // intercept == 1 (0 is impossible):
119 // Any move not removing intercept is OK
120 return moves
.filter(m
=> {
122 // From another column?
123 m
.start
.y
!= colKing
||
124 // From behind a king? (including kings themselves!)
125 m
.start
.x
<= kingPos1
||
126 m
.start
.x
>= kingPos2
||
127 // Intercept piece moving: must remain in-between
129 m
.end
.y
== colKing
&&
130 m
.end
.x
> kingPos1
&&
136 if (this.kingPos
['w'][0] == this.kingPos
['b'][0]) {
137 const rowKing
= this.kingPos
['w'][0];
138 let intercept
= 0; //count intercepting pieces
139 let [kingPos1
, kingPos2
] = [this.kingPos
['w'][1], this.kingPos
['b'][1]];
140 if (kingPos1
> kingPos2
) [kingPos1
, kingPos2
] = [kingPos2
, kingPos1
];
141 for (let i
= kingPos1
+ 1; i
< kingPos2
; i
++) {
142 if (this.board
[rowKing
][i
] != V
.EMPTY
) intercept
++;
144 if (intercept
>= 2) return moves
;
145 // intercept == 1 (0 is impossible):
146 // Any move not removing intercept is OK
147 return moves
.filter(m
=> {
150 m
.start
.x
!= rowKing
||
151 // From "behind" a king? (including kings themselves!)
152 m
.start
.y
<= kingPos1
||
153 m
.start
.y
>= kingPos2
||
154 // Intercept piece moving: must remain in-between
156 m
.end
.x
== rowKing
&&
157 m
.end
.y
> kingPos1
&&
163 // piece == king: check only if move.end.y == enemy king column,
164 // or if move.end.x == enemy king rank.
165 const color
= this.getColor(sq
[0], sq
[1]);
166 const oppCol
= V
.GetOppCol(color
);
167 return moves
.filter(m
=> {
169 m
.end
.y
!= this.kingPos
[oppCol
][1] &&
170 m
.end
.x
!= this.kingPos
[oppCol
][0]
174 // check == -1 if (row, or col) unchecked, 1 if checked and occupied,
175 // 0 if checked and clear
176 let check
= [-1, -1];
177 // TODO: factor two next "if"...
178 if (m
.end
.x
== this.kingPos
[oppCol
][0]) {
182 let [kingPos1
, kingPos2
] = [m
.end
.y
, this.kingPos
[oppCol
][1]];
183 if (kingPos1
> kingPos2
) [kingPos1
, kingPos2
] = [kingPos2
, kingPos1
];
184 for (let i
= kingPos1
+ 1; i
< kingPos2
; i
++) {
185 if (this.board
[m
.end
.x
][i
] != V
.EMPTY
) {
190 return check
[0] == 1;
192 // Check already done:
193 return check
[0] == 1;
195 //if (m.end.y == this.kingPos[oppCol][1]) //true...
199 let [kingPos1
, kingPos2
] = [m
.end
.x
, this.kingPos
[oppCol
][0]];
200 if (kingPos1
> kingPos2
) [kingPos1
, kingPos2
] = [kingPos2
, kingPos1
];
201 for (let i
= kingPos1
+ 1; i
< kingPos2
; i
++) {
202 if (this.board
[i
][m
.end
.y
] != V
.EMPTY
) {
207 return check
[1] == 1;
209 // Check already done:
210 return check
[1] == 1;
214 getSlideNJumpMoves_([x
, y
], steps
, oneStep
) {
216 outerLoop: for (let step
of steps
) {
220 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
221 if (!step
.onlyTake
) moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
222 // NOTE: (bad) HACK here, since onlyTake is true only for Eagle
223 // capturing moves, which are oneStep...
224 if (!!oneStep
|| !!step
.onlyTake
) continue outerLoop
;
228 if (V
.OnBoard(i
, j
) && this.canTake([x
, y
], [i
, j
]) && !step
.onlyMove
)
229 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
243 { s: [-1, -1], onlyMove: true },
244 { s: [-1, 1], onlyMove: true },
245 { s: [1, -1], onlyMove: true },
246 { s: [1, 1], onlyMove: true }
249 { s: [-1, 0], onlyMove: true },
250 { s: [1, 0], onlyMove: true },
251 { s: [0, -1], onlyMove: true },
252 { s: [0, 1], onlyMove: true },
259 { s: [-1, 0], onlyMove: true },
260 { s: [1, 0], onlyMove: true },
261 { s: [0, -1], onlyMove: true },
262 { s: [0, 1], onlyMove: true },
263 { s: [-1, -1], onlyMove: true },
264 { s: [-1, 1], onlyMove: true },
265 { s: [1, -1], onlyMove: true },
266 { s: [1, 1], onlyMove: true },
267 { s: [-2, -1], onlyTake: true },
268 { s: [-2, 1], onlyTake: true },
269 { s: [-1, -2], onlyTake: true },
270 { s: [-1, 2], onlyTake: true },
271 { s: [1, -2], onlyTake: true },
272 { s: [1, 2], onlyTake: true },
273 { s: [2, -1], onlyTake: true },
274 { s: [2, 1], onlyTake: true }
282 getPotentialTowerMoves(sq
) {
283 return this.getSlideNJumpMoves_(sq
, V
.steps
[V
.TOWER
]);
286 getPotentialCardinalMoves(sq
) {
287 return this.getSlideNJumpMoves_(sq
, V
.steps
[V
.CARDINAL
]);
290 getPotentialEagleMoves(sq
) {
291 return this.getSlideNJumpMoves_(sq
, V
.steps
[V
.EAGLE
]);
294 getPotentialDukeMoves([x
, y
]) {
295 // Anything to capture around? mark other steps to explore after
297 const oppCol
= V
.GetOppCol(this.getColor(x
, y
));
299 for (let s
of V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
])) {
300 const [i
, j
] = [x
+ s
[0], y
+ s
[1]];
303 this.board
[i
][j
] != V
.EMPTY
&&
304 this.getColor(i
, j
) == oppCol
306 moves
.push(super.getBasicMove([x
, y
], [i
, j
]));
308 else steps
.push({ s: s
, onlyMove: true });
310 if (steps
.length
> 0) {
311 const noncapturingMoves
= this.getSlideNJumpMoves_([x
, y
], steps
);
312 Array
.prototype.push
.apply(moves
, noncapturingMoves
);
317 getPotentialKingMoves([x
, y
]) {
318 if (this.getColor(x
, y
) == 'b') return super.getPotentialKingMoves([x
, y
]);
319 // Empire doesn't castle:
320 return super.getSlideNJumpMoves(
322 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
327 getPotentialSoldierMoves([x
, y
]) {
328 const c
= this.getColor(x
, y
);
329 const shiftX
= (c
== 'w' ? -1 : 1);
330 const lastRank
= (c
== 'w' && x
== 0 || c
== 'b' && x
== 9);
332 if (!lastRank
) steps
.push([shiftX
, 0]);
333 if (y
> 0) steps
.push([0, -1]);
334 if (y
< 9) steps
.push([0, 1]);
335 return super.getSlideNJumpMoves([x
, y
], steps
, "oneStep");
338 isAttacked(sq
, color
) {
339 if (color
== 'b') return super.isAttacked(sq
, color
);
340 // Empire: only pawn and king (+ queen if promotion) in common:
342 super.isAttackedByPawn(sq
, color
) ||
343 this.isAttackedByTower(sq
, color
) ||
344 this.isAttackedByEagle(sq
, color
) ||
345 this.isAttackedByCardinal(sq
, color
) ||
346 this.isAttackedByDuke(sq
, color
) ||
347 this.isAttackedBySoldier(sq
, color
) ||
348 super.isAttackedByKing(sq
, color
) ||
349 super.isAttackedByQueen(sq
, color
)
353 isAttackedByTower(sq
, color
) {
354 return super.isAttackedBySlideNJump(sq
, color
, V
.TOWER
, V
.steps
[V
.ROOK
]);
357 isAttackedByEagle(sq
, color
) {
358 return super.isAttackedBySlideNJump(
359 sq
, color
, V
.EAGLE
, V
.steps
[V
.KNIGHT
], "oneStep");
362 isAttackedByCardinal(sq
, color
) {
363 return super.isAttackedBySlideNJump(
364 sq
, color
, V
.CARDINAL
, V
.steps
[V
.BISHOP
]);
367 isAttackedByDuke(sq
, color
) {
369 super.isAttackedBySlideNJump(
371 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]), "oneStep"
376 isAttackedBySoldier([x
, y
], color
) {
377 const shiftX
= (color
== 'w' ? 1 : -1); //shift from king
378 return super.isAttackedBySlideNJump(
379 [x
, y
], color
, V
.SOLDIER
, [[shiftX
, 0], [0, 1], [0, -1]], "oneStep");
382 updateCastleFlags(move, piece
) {
383 // Only black can castle:
385 if (piece
== V
.KING
&& move.appear
[0].c
== 'b')
386 this.castleFlags
['b'] = [8, 8];
388 move.start
.x
== firstRank
&&
389 this.castleFlags
['b'].includes(move.start
.y
)
391 const flagIdx
= (move.start
.y
== this.castleFlags
['b'][0] ? 0 : 1);
392 this.castleFlags
['b'][flagIdx
] = 8;
395 move.end
.x
== firstRank
&&
396 this.castleFlags
['b'].includes(move.end
.y
)
398 const flagIdx
= (move.end
.y
== this.castleFlags
['b'][0] ? 0 : 1);
399 this.castleFlags
['b'][flagIdx
] = 8;
405 const color
= V
.GetOppCol(this.turn
);
406 const lastRank
= (color
== 'w' ? 0 : 7);
407 if (this.kingPos
[color
][0] == lastRank
)
408 // The opposing edge is reached!
409 return color
== "w" ? "1-0" : "0-1";
410 if (this.atLeastOneMove()) return "*";
412 const oppCol
= this.turn
;
413 return (oppCol
== "w" ? "0-1" : "1-0");
416 static get VALUES() {
417 return Object
.assign(
430 static get SEARCH_DEPTH() {