Commit | Line | Data |
---|---|---|
6c7cbfed | 1 | import { ChessRules } from "@/base_rules"; |
5d75c82c | 2 | import { SuicideRules } from "@/variants/Suicide"; |
6c7cbfed BA |
3 | |
4 | export class ChakartRules extends ChessRules { | |
5d75c82c BA |
5 | static get PawnSpecs() { |
6 | return SuicideRules.PawnSpecs; | |
7 | } | |
8 | ||
9 | static get HasCastle() { | |
10 | return false; | |
11 | } | |
12 | ||
13 | static get HasEnpassant() { | |
14 | // TODO: maybe enable them later, but then the capturing pawn take the | |
15 | // mushroom and continue diagonally?! | |
16 | return false; | |
17 | } | |
18 | ||
ad030c7d BA |
19 | static get CorrConfirm() { |
20 | // Because of bonus effects | |
21 | return false; | |
22 | } | |
90df90bc | 23 | |
ad030c7d BA |
24 | static get CanAnalyze() { |
25 | return false; | |
26 | } | |
6c7cbfed | 27 | |
90df90bc | 28 | hoverHighlight(x, y) { |
ad030c7d BA |
29 | if ( |
30 | this.firstMove.appear.length == 0 || | |
31 | this.firstMove.vanish.length == 0 || | |
32 | this.board[x][y] != V.EMPTY | |
33 | ) { | |
34 | return false; | |
35 | } | |
36 | const deltaX = Math.abs(this.firstMove.end.x - x); | |
37 | const deltaY = Math.abs(this.firstMove.end.y - y); | |
38 | return ( | |
39 | this.subTurn == 2 && | |
40 | // Condition: rook or bishop move, may capture, but no bonus move | |
41 | [V.ROOK, V.BISHOP].includes(this.firstMove.vanish[0].p) && | |
42 | ( | |
43 | this.firstMove.vanish.length == 1 || | |
44 | ['w', 'b'].includes(this.firstMove.vanish[1].c) | |
45 | ) && | |
46 | ( | |
47 | this.firstMove.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1 || | |
48 | this.firstMove.vanish[0].p == V.BISHOP && deltaX + deltaY == 1 | |
49 | ) | |
50 | ); | |
90df90bc BA |
51 | } |
52 | ||
b9ce3d0f BA |
53 | static get IMMOBILIZE_CODE() { |
54 | return { | |
55 | 'p': 's', | |
56 | 'r': 'u', | |
57 | 'n': 'o', | |
58 | 'b': 'c', | |
59 | 'q': 't', | |
60 | 'k': 'l' | |
61 | }; | |
62 | } | |
63 | ||
64 | static get IMMOBILIZE_DECODE() { | |
65 | return { | |
66 | 's': 'p', | |
67 | 'u': 'r', | |
68 | 'o': 'n', | |
69 | 'c': 'b', | |
70 | 't': 'q', | |
71 | 'l': 'k' | |
72 | }; | |
73 | } | |
74 | ||
75 | static get INVISIBLE_QUEEN() { | |
76 | return 'i'; | |
77 | } | |
78 | ||
ad030c7d BA |
79 | // Fictive color 'a', bomb banana mushroom egg |
80 | static get BOMB() { | |
81 | // Doesn't collide with bishop because color 'a' | |
82 | return 'b'; | |
83 | } | |
84 | static get BANANA() { | |
85 | return 'n'; | |
86 | } | |
87 | static get EGG() { | |
88 | return 'e'; | |
89 | } | |
90 | static get MUSHROOM() { | |
91 | return 'm'; | |
92 | } | |
93 | ||
94 | static get PIECES() { | |
95 | return ( | |
96 | ChessRules.PIECES.concat( | |
97 | Object.keys(V.IMMOBILIZE_DECODE)).concat( | |
98 | [V.BANANA, V.BOMB, V.EGG, V.MUSHROOM, V.INVISIBLE_QUEEN]) | |
99 | ); | |
100 | } | |
101 | ||
b9ce3d0f BA |
102 | getPpath(b) { |
103 | let prefix = ""; | |
104 | if ( | |
ad030c7d | 105 | b[0] == 'a' || |
b9ce3d0f BA |
106 | b[1] == V.INVISIBLE_QUEEN || |
107 | Object.keys(V.IMMOBILIZE_DECODE).includes(b[1]) | |
108 | ) { | |
109 | prefix = "Chakart/"; | |
110 | } | |
111 | return prefix + b; | |
112 | } | |
113 | ||
114 | static ParseFen(fen) { | |
115 | const fenParts = fen.split(" "); | |
116 | return Object.assign( | |
117 | ChessRules.ParseFen(fen), | |
5d75c82c | 118 | { captured: fenParts[4] } |
b9ce3d0f BA |
119 | ); |
120 | } | |
121 | ||
122 | // King can be l or L (immobilized) --> similar to Alice variant | |
90df90bc BA |
123 | static IsGoodPosition(position) { |
124 | if (position.length == 0) return false; | |
125 | const rows = position.split("/"); | |
126 | if (rows.length != V.size.x) return false; | |
b9ce3d0f | 127 | let kings = { "k": 0, "K": 0, 'l': 0, 'L': 0 }; |
90df90bc BA |
128 | for (let row of rows) { |
129 | let sumElts = 0; | |
130 | for (let i = 0; i < row.length; i++) { | |
b9ce3d0f | 131 | if (['K','k','L','l'].includes(row[i])) kings[row[i]]++; |
90df90bc BA |
132 | if (V.PIECES.includes(row[i].toLowerCase())) sumElts++; |
133 | else { | |
134 | const num = parseInt(row[i]); | |
135 | if (isNaN(num)) return false; | |
136 | sumElts += num; | |
137 | } | |
138 | } | |
139 | if (sumElts != V.size.y) return false; | |
140 | } | |
b9ce3d0f BA |
141 | if (kings['k'] + kings['l'] != 1 || kings['K'] + kings['L'] != 1) |
142 | return false; | |
90df90bc BA |
143 | return true; |
144 | } | |
145 | ||
b9ce3d0f | 146 | static IsGoodFlags(flags) { |
5d75c82c BA |
147 | // 4 for Peach + Mario w, b |
148 | return !!flags.match(/^[01]{4,4}$/); | |
b9ce3d0f BA |
149 | } |
150 | ||
151 | setFlags(fenflags) { | |
b9ce3d0f | 152 | this.powerFlags = { |
ad030c7d | 153 | w: [...Array(2)], //king can send shell? Queen can be invisible? |
b9ce3d0f BA |
154 | b: [...Array(2)] |
155 | }; | |
b9ce3d0f BA |
156 | for (let c of ["w", "b"]) { |
157 | for (let i = 0; i < 2; i++) | |
5d75c82c | 158 | this.pawnFlags[c][i] = fenFlags.charAt((c == "w" ? 0 : 2) + i) == "1"; |
b9ce3d0f BA |
159 | } |
160 | } | |
161 | ||
162 | aggregateFlags() { | |
5d75c82c | 163 | return this.powerFlags; |
b9ce3d0f BA |
164 | } |
165 | ||
166 | disaggregateFlags(flags) { | |
5d75c82c | 167 | this.powerFlags = flags; |
b9ce3d0f BA |
168 | } |
169 | ||
170 | getFen() { | |
171 | return super.getFen() + " " + this.getCapturedFen(); | |
172 | } | |
173 | ||
174 | getFenForRepeat() { | |
175 | return super.getFenForRepeat() + "_" + this.getCapturedFen(); | |
176 | } | |
177 | ||
178 | getCapturedFen() { | |
179 | let counts = [...Array(10).fill(0)]; | |
180 | let i = 0; | |
5333b500 | 181 | for (let p of [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.PAWN]) { |
b9ce3d0f BA |
182 | counts[i] = this.captured["w"][p]; |
183 | counts[5 + i] = this.captured["b"][p]; | |
184 | i++; | |
185 | } | |
186 | return counts.join(""); | |
187 | } | |
188 | ||
6c7cbfed | 189 | setOtherVariables(fen) { |
b9ce3d0f BA |
190 | const fenParsed = V.ParseFen(fen); |
191 | // Initialize captured pieces' counts from FEN | |
192 | this.captured = { | |
193 | w: { | |
194 | [V.ROOK]: parseInt(fenParsed.captured[0]), | |
195 | [V.KNIGHT]: parseInt(fenParsed.captured[1]), | |
196 | [V.BISHOP]: parseInt(fenParsed.captured[2]), | |
197 | [V.QUEEN]: parseInt(fenParsed.captured[3]), | |
198 | [V.PAWN]: parseInt(fenParsed.captured[4]), | |
199 | }, | |
200 | b: { | |
201 | [V.ROOK]: parseInt(fenParsed.captured[5]), | |
202 | [V.KNIGHT]: parseInt(fenParsed.captured[6]), | |
203 | [V.BISHOP]: parseInt(fenParsed.captured[7]), | |
204 | [V.QUEEN]: parseInt(fenParsed.captured[8]), | |
205 | [V.PAWN]: parseInt(fenParsed.captured[9]), | |
206 | } | |
207 | }; | |
6c7cbfed BA |
208 | this.subTurn = 1; |
209 | } | |
210 | ||
b9ce3d0f | 211 | getFlagsFen() { |
5d75c82c | 212 | let fen = ""; |
b9ce3d0f BA |
213 | // Add power flags |
214 | for (let c of ["w", "b"]) | |
215 | for (let i = 0; i < 2; i++) fen += (this.powerFlags[c][i] ? "1" : "0"); | |
216 | return fen; | |
217 | } | |
218 | ||
5d75c82c BA |
219 | getPotentialMovesFrom(sq) { |
220 | if (this.subTurn == 1) return super.getPotentialMovesFrom(sq); | |
6c7cbfed BA |
221 | if (this.subTurn == 2) { |
222 | // TODO: coup compatible avec firstMove | |
223 | } | |
224 | } | |
225 | ||
ad030c7d | 226 | getBasicMove([x1, y1], [x2, y2]) { |
5d75c82c BA |
227 | // Apply mushroom, bomb or banana effect (hidden to the player). |
228 | // Determine egg effect, too, and apply its first part if possible. | |
229 | // add egg + add mushroom for pawns. | |
230 | let move = super.getBasicMove([x1, y1], [x2, y2]); | |
231 | // TODO | |
232 | return move; | |
233 | // Infer move type based on its effects (used to decide subTurn 1 --> 2) | |
234 | // --> impossible étant donné juste first part (egg --> effect?) | |
235 | // => stocker l'effet (i, ii ou iii) dans le coup directement, | |
236 | // Pas terrible, mais y'aura pas 36 variantes comme ça. Disons end.effect == null, 0, 1, 2 ou 3 | |
237 | // 0 => tour ou fou, pose potentielle. | |
238 | // If queen can be invisible, add move same start + end but final type changes | |
239 | } | |
240 | ||
241 | getPotentialKingMoves([x, y]) { | |
242 | let moves = super.getPotentialKingMoves([x, y]); | |
243 | // TODO: if flags allows it, add 'remote shell captures' | |
244 | return moves; | |
245 | } | |
246 | ||
247 | getSlideNJumpMoves([x, y], steps, oneStep) { | |
248 | let moves = []; | |
249 | outerLoop: for (let step of steps) { | |
250 | let i = x + step[0]; | |
251 | let j = y + step[1]; | |
252 | while ( | |
253 | V.OnBoard(i, j) && | |
254 | ( | |
255 | this.board[i][j] == V.EMPTY || | |
256 | ( | |
257 | this.getColor(i, j) == 'a' && | |
258 | [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j)) | |
259 | ) | |
260 | ) | |
261 | ) { | |
262 | moves.push(this.getBasicMove([x, y], [i, j])); | |
263 | if (oneStep) continue outerLoop; | |
264 | i += step[0]; | |
265 | j += step[1]; | |
266 | } | |
267 | if (V.OnBoard(i, j) && this.canTake([x, y], [i, j])) | |
268 | moves.push(this.getBasicMove([x, y], [i, j])); | |
269 | } | |
270 | return moves; | |
90df90bc BA |
271 | } |
272 | ||
ad030c7d | 273 | getAllPotentialMoves() { |
5d75c82c BA |
274 | if (this.subTurn == 1) return super.getAllPotentialMoves(); |
275 | // TODO: subTurn == 2, switch on firstMove.end.effect --> lack firstMove, setOtherVariables, play/undo, see Dynamo | |
6c7cbfed BA |
276 | } |
277 | ||
1c15969e | 278 | doClick(square) { |
107dc1bd | 279 | if (isNaN(square[0])) return null; |
5333b500 | 280 | // TODO: If subTurn == 2: |
1c15969e | 281 | // if square is empty && firstMove is compatible, |
5333b500 | 282 | // complete the move (banana or bomb or piece exchange). |
1c15969e BA |
283 | // if square not empty, just complete with empty move |
284 | const Lf = this.firstMove.length; | |
285 | if (this.subTurn == 2) { | |
286 | if ( | |
287 | this.board[square[0]][square[1]] == V.EMPTY && | |
1c15969e BA |
288 | (La == 0 || !this.oppositeMoves(this.amoves[La-1], this.firstMove[Lf-1])) |
289 | ) { | |
290 | return { | |
291 | start: { x: -1, y: -1 }, | |
292 | end: { x: -1, y: -1 }, | |
293 | appear: [], | |
294 | vanish: [] | |
295 | }; | |
296 | } | |
297 | } | |
298 | return null; | |
299 | } | |
300 | ||
5d75c82c BA |
301 | play(move) { |
302 | // TODO | |
303 | // --> pour bonus toadette, passer "capture" temporairement en "reserve" pour permettre de jouer le coup. | |
304 | // il faut alors mettre à jour 'captured' | |
305 | // TODO: subTurn passe à 2 si arrivée sur bonus cavalier + effect == 1, 2 ou 3 ou si coup de tour ou fou (non cumulables) | |
1c15969e | 306 | } |
5d75c82c | 307 | |
b9ce3d0f | 308 | postPlay(move) { |
5d75c82c BA |
309 | // TODO: if effect = resurect a piece, then this.reserve = this.captured; |
310 | if (move.vanish.length == 2 && move.vanish[1].c != 'a') | |
b9ce3d0f BA |
311 | // Capture: update this.captured |
312 | this.captured[move.vanish[1].c][move.vanish[1].p]++; | |
5d75c82c BA |
313 | else if (move.vanish.length == 0) { |
314 | // A piece is back on board | |
315 | this.captured[move.vanish[1].c][move.vanish[1].p]++; | |
316 | this.reserve = null; | |
317 | } | |
318 | // si pièce immobilisée de ma couleur : elle redevient utilisable (changer status fin de play) | |
319 | // TODO: un-immobilize my formerly immobilized piece, if any. | |
320 | // Make invisible queen visible again, if any opponent invisible queen. | |
321 | } | |
322 | ||
323 | undo(move) { | |
324 | // TODO: should be easy once end.effect is set in getBasicMove() | |
b9ce3d0f BA |
325 | } |
326 | ||
327 | postUndo(move) { | |
5d75c82c | 328 | if (move.vanish.length == 2 && move.vanish[1].c != 'a') |
b9ce3d0f BA |
329 | this.captured[move.vanish[1].c][move.vanish[1].p]--; |
330 | } | |
1c15969e | 331 | |
5d75c82c BA |
332 | getCheckSquares() { |
333 | return []; | |
334 | } | |
335 | ||
6c7cbfed | 336 | getCurrentScore() { |
5d75c82c BA |
337 | // Find kings (not tracked in this variant) |
338 | let kingThere = { w: false, b: false }; | |
339 | for (let i=0; i<8; i++) { | |
340 | for (let j=0; j<8; j++) { | |
341 | if (this.board[i][j] != V.EMPTY && this.getPiece(i, j) == V.KING) | |
342 | kingThere[this.getColor(i, j)] = true; | |
343 | } | |
344 | } | |
345 | if (!kingThere['w']) return "0-1"; | |
346 | if (!kingThere['b']) return "1-0"; | |
347 | return "*"; | |
6c7cbfed BA |
348 | } |
349 | ||
b9ce3d0f BA |
350 | static GenRandInitFen(randomness) { |
351 | return ( | |
5d75c82c | 352 | SuicideRules.GenRandInitFen(randomness).slice(0, -1) + |
b9ce3d0f BA |
353 | // Add Peach + Mario flags, re-add en-passant + capture counts |
354 | "0000 - 0000000000" | |
355 | ); | |
356 | } | |
357 | ||
5d75c82c BA |
358 | filterValid(moves) { |
359 | return moves; | |
360 | } | |
361 | ||
1c15969e | 362 | getComputerMove() { |
5d75c82c | 363 | const moves = this.getAllValidMoves(); |
1c15969e | 364 | // TODO: random mover |
5d75c82c | 365 | return moves[0]; |
1c15969e | 366 | } |
b9ce3d0f BA |
367 | |
368 | getNotation(move) { | |
5d75c82c | 369 | // TODO |
b9ce3d0f | 370 | // invisibility used? --> move notation Q?? |
5d75c82c | 371 | return "?"; |
b9ce3d0f | 372 | } |
6c7cbfed | 373 | }; |