Remove castling as an option to escape from check in Allmate variants
[vchess.git] / client / src / variants / Allmate1.js
CommitLineData
e9b736ee
BA
1import { ChessRules, PiPo, Move } from "@/base_rules";
2
32f6285e 3export class Allmate1Rules extends ChessRules {
e9b736ee
BA
4 static get HasEnpassant() {
5 return false;
6 }
7
e9b736ee
BA
8 getCheckSquares() {
9 // No notion of check
10 return [];
11 }
12
7ba4a5bc 13 static GenRandInitFen(randomness) {
3a2a7b5f 14 return ChessRules.GenRandInitFen(randomness).slice(0, -2);
e9b736ee
BA
15 }
16
17 getPotentialMovesFrom([x, y]) {
18 let moves = super.getPotentialMovesFrom([x, y]);
4404e58c
BA
19 // Remove standard captures (without removing castling):
20 moves = moves.filter(m => {
21 return m.vanish.length == 1 || m.appear.length == 2;
22 });
e9b736ee
BA
23
24 // Augment moves with "mate-captures":
25 // TODO: this is coded in a highly inefficient way...
26 const color = this.turn;
27 const oppCol = V.GetOppCol(this.turn);
28 moves.forEach(m => {
29 this.play(m);
30
31 // 1) What is attacked?
32 let attacked = {};
33 for (let i=0; i<V.size.x; i++) {
34 for (let j=0; j<V.size.y; j++) {
32f6285e 35 if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], color))
e9b736ee
BA
36 attacked[i+"_"+j] = [i,j];
37 }
38 }
39
40 // 2) Among attacked pieces, which cannot escape capture?
41 // Avoid "oppMoves = this.getAllValidMoves();" => infinite recursion
42 outerLoop: for (let i=0; i<V.size.x; i++) {
43 for (let j=0; j<V.size.y; j++) {
44 if (this.getColor(i,j) == oppCol) {
45 let oppMoves = [];
46 switch (this.getPiece(i, j)) {
47 case V.PAWN:
48 oppMoves = this.getPotentialPawnMoves([i, j]);
49 break;
50 case V.ROOK:
51 oppMoves = this.getPotentialRookMoves([i, j]);
52 break;
53 case V.KNIGHT:
54 oppMoves = this.getPotentialKnightMoves([i, j]);
55 break;
56 case V.BISHOP:
57 oppMoves = this.getPotentialBishopMoves([i, j]);
58 break;
59 case V.QUEEN:
60 oppMoves = this.getPotentialQueenMoves([i, j]);
61 break;
62 case V.KING:
fd7295e8
BA
63 // Do not allow castling to escape from check
64 oppMoves = super.getSlideNJumpMoves(
65 sq,
66 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
67 "oneStep"
68 );
e9b736ee
BA
69 break;
70 }
71 for (let om of oppMoves) {
72 V.PlayOnBoard(this.board, om);
73 Object.values(attacked).forEach(sq => {
74 const origSq = [sq[0], sq[1]];
75 if (om.start.x == sq[0] && om.start.y == sq[1])
76 // Piece moved:
77 sq = [om.appear[0].x, om.appear[0].y];
32f6285e 78 if (!this.isAttacked(sq, color))
e9b736ee
BA
79 delete attacked[origSq[0]+"_"+origSq[1]];
80 });
81 V.UndoOnBoard(this.board, om);
82 if (Object.keys(attacked).length == 0)
83 // No need to explore more moves
84 break outerLoop;
85 }
86 }
87 }
88 }
89
90 // 3) Add mate-captures:
91 Object.values(attacked).forEach(sq => {
92 m.vanish.push(new PiPo({
93 x: sq[0],
94 y: sq[1],
95 c: oppCol,
96 p: this.getPiece(sq[0], sq[1])
97 }));
98 });
99
100 this.undo(m);
101 });
102
103 return moves;
104 }
105
106 // No "under check" conditions in castling
a6836242
BA
107 getCastleMoves(sq) {
108 return super.getCastleMoves(sq, "castleInCheck");
e9b736ee
BA
109 }
110
54b7b81b 111 // TODO: allow pieces to "commit suicide"? (Currently yes except king)
e9b736ee 112 filterValid(moves) {
54b7b81b
BA
113 // Remove moves which let the king mate-captured:
114 if (moves.length == 0) return [];
115 const color = this.turn;
116 const oppCol = V.GetOppCol(color);
117 return moves.filter(m => {
118 let res = true;
119 this.play(m);
120 if (this.underCheck(color)) {
121 res = false;
122 const attacked = this.kingPos[color];
123 // Try to find a move to escape check
124 // TODO: very inefficient method.
125 outerLoop: for (let i=0; i<V.size.x; i++) {
126 for (let j=0; j<V.size.y; j++) {
127 if (this.getColor(i,j) == color) {
128 let emoves = [];
129 // Artficial turn change to "play twice":
130 this.turn = color;
131 switch (this.getPiece(i, j)) {
132 case V.PAWN:
133 emoves = this.getPotentialPawnMoves([i, j]);
134 break;
135 case V.ROOK:
136 emoves = this.getPotentialRookMoves([i, j]);
137 break;
138 case V.KNIGHT:
139 emoves = this.getPotentialKnightMoves([i, j]);
140 break;
141 case V.BISHOP:
142 emoves = this.getPotentialBishopMoves([i, j]);
143 break;
144 case V.QUEEN:
145 emoves = this.getPotentialQueenMoves([i, j]);
146 break;
147 case V.KING:
148 emoves = this.getPotentialKingMoves([i, j]);
149 break;
150 }
151 this.turn = oppCol;
152 for (let em of emoves) {
153 V.PlayOnBoard(this.board, em);
154 let sq = attacked;
155 if (em.start.x == attacked[0] && em.start.y == attacked[1])
156 // King moved:
157 sq = [em.appear[0].x, em.appear[0].y];
32f6285e 158 if (!this.isAttacked(sq, oppCol))
54b7b81b
BA
159 res = true;
160 V.UndoOnBoard(this.board, em);
161 if (res)
162 // No need to explore more moves
163 break outerLoop;
164 }
165 }
166 }
167 }
168 }
169 this.undo(m);
170 return res;
171 });
e9b736ee
BA
172 }
173
3a2a7b5f
BA
174 postPlay(move) {
175 super.postPlay(move);
094e11f2 176 if (move.vanish.length >= 2 && move.appear.length == 1) {
3a2a7b5f
BA
177 for (let i = 1; i<move.vanish.length; i++) {
178 const v = move.vanish[i];
094e11f2
BA
179 // Did opponent king disappeared?
180 if (v.p == V.KING)
181 this.kingPos[this.turn] = [-1, -1];
182 // Or maybe a rook?
183 else if (v.p == V.ROOK) {
184 if (v.y < this.INIT_COL_KING[v.c])
3a2a7b5f 185 this.castleFlags[v.c][0] = 8;
094e11f2
BA
186 else
187 // v.y > this.INIT_COL_KING[v.c]
3a2a7b5f 188 this.castleFlags[v.c][1] = 8;
094e11f2 189 }
3a2a7b5f 190 }
e9b736ee
BA
191 }
192 }
193
3a2a7b5f
BA
194 preUndo(move) {
195 super.preUndo(move);
196 const oppCol = this.turn;
094e11f2 197 if (move.vanish.length >= 2 && move.appear.length == 1) {
e9b736ee 198 // Did opponent king disappeared?
3a2a7b5f 199 const psq = move.vanish.find(v => v.p == V.KING && v.c == oppCol)
094e11f2
BA
200 if (psq)
201 this.kingPos[psq.c] = [psq.x, psq.y];
e9b736ee
BA
202 }
203 }
204
205 getCurrentScore() {
206 const color = this.turn;
207 const kp = this.kingPos[color];
208 if (kp[0] < 0)
209 // King disappeared
210 return color == "w" ? "0-1" : "1-0";
bb688df5 211 if (this.atLeastOneMove()) return "*";
e9b736ee
BA
212 // Kings still there, no moves:
213 return "1/2";
214 }
215
216 static get SEARCH_DEPTH() {
b83a675a 217 return 1;
e9b736ee
BA
218 }
219
220 getNotation(move) {
221 let notation = super.getNotation(move);
222 // Add a capture mark (not describing what is captured...):
23ecf008 223 if (move.vanish.length > 1 && move.appear.length == 1) {
188b4a8f 224 if (!!(notation.match(/^[a-h]x/)))
23ecf008
BA
225 // Pawn capture: remove initial "b" in bxc4 for example
226 notation = notation.substr(1);
227 notation = notation.replace("x","") + "X";
54b7b81b 228 }
e9b736ee
BA
229 return notation;
230 }
231};