1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
3 export class AllmateRules
extends ChessRules
{
5 static get HasEnpassant() {
14 static GenRandInitFen(options
) {
15 return ChessRules
.GenRandInitFen(options
).slice(0, -2);
18 getPotentialMovesFrom([x
, y
]) {
19 let moves
= super.getPotentialMovesFrom([x
, y
]);
20 // Remove standard captures (without removing castling):
21 moves
= moves
.filter(m
=> {
22 return m
.vanish
.length
== 1 || m
.appear
.length
== 2;
25 // Augment moves with "mate-captures":
26 // TODO: this is coded in a highly inefficient way...
27 const color
= this.turn
;
28 const oppCol
= V
.GetOppCol(this.turn
);
33 // While something has been captured:
34 // remove it, and keep looking for captures
35 outerLoop: while (true) {
37 // 1) What is attacked?
39 for (let i
=0; i
<V
.size
.x
; i
++) {
40 for (let j
=0; j
<V
.size
.y
; j
++) {
41 if (this.getColor(i
,j
) == oppCol
&& this.isAttacked([i
,j
], color
))
42 attacked
[i
+"_"+j
] = [i
,j
];
45 if (Object
.keys(attacked
).length
== 0) break outerLoop
;
47 // 2) Among attacked pieces, which cannot escape capture?
48 // Avoid "oppMoves = this.getAllValidMoves();" => infinite recursion
49 for (let i
=0; i
<V
.size
.x
; i
++) {
50 for (let j
=0; j
<V
.size
.y
; j
++) {
51 if (this.getColor(i
,j
) == oppCol
) {
53 switch (this.getPiece(i
, j
)) {
55 oppMoves
= this.getPotentialPawnMoves([i
, j
]);
58 oppMoves
= this.getPotentialRookMoves([i
, j
]);
61 oppMoves
= this.getPotentialKnightMoves([i
, j
]);
64 oppMoves
= this.getPotentialBishopMoves([i
, j
]);
67 oppMoves
= this.getPotentialQueenMoves([i
, j
]);
70 // Do not allow castling to escape from check
71 oppMoves
= super.getSlideNJumpMoves(
72 [i
, j
], V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]), 1);
75 for (let om
of oppMoves
) {
76 V
.PlayOnBoard(this.board
, om
);
77 Object
.values(attacked
).forEach(sq
=> {
78 const origSq
= [sq
[0], sq
[1]];
79 if (om
.start
.x
== sq
[0] && om
.start
.y
== sq
[1])
81 sq
= [om
.appear
[0].x
, om
.appear
[0].y
];
82 if (!this.isAttacked(sq
, color
))
83 delete attacked
[origSq
[0]+"_"+origSq
[1]];
85 V
.UndoOnBoard(this.board
, om
);
86 if (Object
.keys(attacked
).length
== 0)
87 // No need to explore more moves
94 // 3) Add mate-captures and remove pieces from board:
95 Object
.keys(attacked
).forEach(k
=> {
98 p: this.getPiece(attacked
[k
][0], attacked
[k
][1])
100 this.board
[attacked
[k
][0]][attacked
[k
][1]] = V
.EMPTY
;
104 // Put removed pieces back on board:
105 allAttacks
.forEach(v
=> {
106 this.board
[v
.sq
[0]][v
.sq
[1]] = oppCol
+ v
.p
;
109 allAttacks
.forEach(v
=> {
124 // No "under check" conditions in castling
126 return super.getCastleMoves(sq
, null, "castleInCheck");
129 // TODO: allow pieces to "commit suicide"? (Currently yes except king)
131 // Remove moves which let the king mate-captured:
132 if (moves
.length
== 0) return [];
133 const color
= this.turn
;
134 const oppCol
= V
.GetOppCol(color
);
135 return moves
.filter(m
=> {
138 if (this.underCheck(color
)) {
140 const attacked
= this.kingPos
[color
];
141 // Try to find a move to escape check
142 // TODO: very inefficient method.
143 outerLoop: for (let i
=0; i
<V
.size
.x
; i
++) {
144 for (let j
=0; j
<V
.size
.y
; j
++) {
145 if (this.getColor(i
,j
) == color
) {
147 // Artficial turn change to "play twice":
149 switch (this.getPiece(i
, j
)) {
151 emoves
= this.getPotentialPawnMoves([i
, j
]);
154 emoves
= this.getPotentialRookMoves([i
, j
]);
157 emoves
= this.getPotentialKnightMoves([i
, j
]);
160 emoves
= this.getPotentialBishopMoves([i
, j
]);
163 emoves
= this.getPotentialQueenMoves([i
, j
]);
166 emoves
= this.getPotentialKingMoves([i
, j
]);
170 for (let em
of emoves
) {
171 V
.PlayOnBoard(this.board
, em
);
173 if (em
.start
.x
== attacked
[0] && em
.start
.y
== attacked
[1])
175 sq
= [em
.appear
[0].x
, em
.appear
[0].y
];
176 if (!this.isAttacked(sq
, oppCol
))
178 V
.UndoOnBoard(this.board
, em
);
180 // No need to explore more moves
193 super.postPlay(move);
194 if (move.vanish
.length
>= 2 && move.appear
.length
== 1) {
195 for (let i
= 1; i
<move.vanish
.length
; i
++) {
196 const v
= move.vanish
[i
];
197 // Did opponent king disappeared?
199 this.kingPos
[this.turn
] = [-1, -1];
201 else if (v
.p
== V
.ROOK
) {
202 if (v
.y
< this.kingPos
[v
.c
][1])
203 this.castleFlags
[v
.c
][0] = 8;
205 // v.y > this.kingPos[v.c][1]
206 this.castleFlags
[v
.c
][1] = 8;
214 const oppCol
= this.turn
;
215 if (move.vanish
.length
>= 2 && move.appear
.length
== 1) {
216 // Did opponent king disappeared?
217 const psq
= move.vanish
.find(v
=> v
.p
== V
.KING
&& v
.c
== oppCol
)
219 this.kingPos
[psq
.c
] = [psq
.x
, psq
.y
];
224 const color
= this.turn
;
225 const kp
= this.kingPos
[color
];
228 return color
== "w" ? "0-1" : "1-0";
229 if (this.atLeastOneMove()) return "*";
230 // Kings still there, no moves:
234 static get SEARCH_DEPTH() {
239 let notation
= super.getNotation(move);
240 // Add a capture mark (not describing what is captured...):
241 if (move.vanish
.length
> 1 && move.appear
.length
== 1) {
242 if (!!(notation
.match(/^[a-h]x/)))
243 // Pawn capture: remove initial "b" in bxc4 for example
244 notation
= notation
.substr(1);
245 notation
= notation
.replace("x","") + "X";