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