Commit | Line | Data |
---|---|---|
bc2bc396 BA |
1 | import ChessRules from "/base_rules.js"; |
2 | import GiveawayRules from "/variants/Giveaway/class.js"; | |
8f57fbf2 BA |
3 | import { ArrayFun } from "/utils/array.js"; |
4 | import { Random } from "/utils/alea.js"; | |
5 | import PiPo from "/utils/PiPo.js"; | |
6 | import Move from "/utils/Move.js"; | |
f382c57b | 7 | |
bc2bc396 | 8 | export default class ChakartRules extends ChessRules { |
f8b43ef7 BA |
9 | |
10 | static get Options() { | |
11 | return { | |
12 | select: [ | |
13 | { | |
14 | label: "Randomness", | |
15 | variable: "randomness", | |
16 | defaut: 2, | |
17 | options: [ | |
37481d1e BA |
18 | {label: "Deterministic", value: 0}, |
19 | {label: "Symmetric random", value: 1}, | |
20 | {label: "Asymmetric random", value: 2} | |
f8b43ef7 BA |
21 | ] |
22 | } | |
3b641716 BA |
23 | ], |
24 | styles: ["cylinder"] | |
f8b43ef7 BA |
25 | }; |
26 | } | |
27 | ||
8f57fbf2 BA |
28 | get pawnPromotions() { |
29 | return ['q', 'r', 'n', 'b', 'k']; | |
f8b43ef7 | 30 | } |
8f57fbf2 | 31 | |
2b9b90da | 32 | get hasCastle() { |
f8b43ef7 BA |
33 | return false; |
34 | } | |
2b9b90da | 35 | get hasEnpassant() { |
f8b43ef7 BA |
36 | return false; |
37 | } | |
24872b22 BA |
38 | get hasReserve() { |
39 | return true; | |
40 | } | |
41 | get hasReserveFen() { | |
42 | return false; | |
43 | } | |
f8b43ef7 | 44 | |
f8b43ef7 BA |
45 | static get IMMOBILIZE_CODE() { |
46 | return { | |
47 | 'p': 's', | |
48 | 'r': 'u', | |
49 | 'n': 'o', | |
50 | 'b': 'c', | |
51 | 'q': 't', | |
52 | 'k': 'l' | |
53 | }; | |
54 | } | |
55 | ||
56 | static get IMMOBILIZE_DECODE() { | |
57 | return { | |
58 | 's': 'p', | |
59 | 'u': 'r', | |
60 | 'o': 'n', | |
61 | 'c': 'b', | |
62 | 't': 'q', | |
63 | 'l': 'k' | |
64 | }; | |
65 | } | |
66 | ||
67 | static get INVISIBLE_QUEEN() { | |
68 | return 'i'; | |
69 | } | |
70 | ||
71 | // Fictive color 'a', bomb banana mushroom egg | |
72 | static get BOMB() { | |
73 | return 'w'; //"Wario" | |
74 | } | |
75 | static get BANANA() { | |
76 | return 'd'; //"Donkey" | |
77 | } | |
78 | static get EGG() { | |
79 | return 'e'; | |
80 | } | |
81 | static get MUSHROOM() { | |
82 | return 'm'; | |
83 | } | |
84 | ||
24872b22 BA |
85 | static get EGG_SURPRISE() { |
86 | return [ | |
87 | "kingboo", "bowser", "daisy", "koopa", | |
88 | "luigi", "waluigi", "toadette", "chomp"]; | |
89 | } | |
90 | ||
3b641716 BA |
91 | canIplay(x, y) { |
92 | if ( | |
93 | this.playerColor != this.turn || | |
94 | Object.keys(V.IMMOBILIZE_DECODE).includes(this.getPiece(x, y)) | |
95 | ) { | |
96 | return false; | |
97 | } | |
98 | return this.egg == "kingboo" || this.getColor(x, y) == this.turn; | |
99 | } | |
100 | ||
24872b22 | 101 | pieces(color, x, y) { |
3b641716 | 102 | const specials = { |
24872b22 BA |
103 | 'i': {"class": "invisible"}, //queen |
104 | 'e': {"class": "egg"}, | |
105 | 'm': {"class": "mushroom"}, | |
106 | 'd': {"class": "banana"}, | |
3b641716 BA |
107 | 'w': {"class": "bomb"}, |
108 | 'z': {"class": "remote-capture"} | |
24872b22 | 109 | }; |
3b641716 BA |
110 | const bowsered = { |
111 | 's': {"class": ["immobilized", "pawn"]}, | |
112 | 'u': {"class": ["immobilized", "rook"]}, | |
113 | 'o': {"class": ["immobilized", "knight"]}, | |
114 | 'c': {"class": ["immobilized", "bishop"]}, | |
115 | 't': {"class": ["immobilized", "queen"]}, | |
116 | 'l': {"class": ["immobilized", "king"]} | |
117 | }; | |
118 | return Object.assign({}, specials, bowsered, super.pieces(color, x, y)); | |
24872b22 BA |
119 | } |
120 | ||
91339921 | 121 | genRandInitFen(seed) { |
bc2bc396 BA |
122 | const gr = new GiveawayRules( |
123 | {mode: "suicide", options: {}, genFenOnly: true}); | |
24872b22 BA |
124 | // Add Peach + mario flags |
125 | return gr.genRandInitFen(seed).slice(0, -17) + '{"flags":"1111"}'; | |
91339921 BA |
126 | } |
127 | ||
8f57fbf2 | 128 | fen2board(f) { |
f8b43ef7 BA |
129 | return ( |
130 | f.charCodeAt() <= 90 | |
131 | ? "w" + f.toLowerCase() | |
132 | : (['w', 'd', 'e', 'm'].includes(f) ? "a" : "b") + f | |
133 | ); | |
134 | } | |
135 | ||
f8b43ef7 BA |
136 | setFlags(fenflags) { |
137 | // King can send shell? Queen can be invisible? | |
138 | this.powerFlags = { | |
8f57fbf2 BA |
139 | w: {k: false, q: false}, |
140 | b: {k: false, q: false} | |
f8b43ef7 | 141 | }; |
8f57fbf2 | 142 | for (let c of ['w', 'b']) { |
f8b43ef7 BA |
143 | for (let p of ['k', 'q']) { |
144 | this.powerFlags[c][p] = | |
145 | fenflags.charAt((c == "w" ? 0 : 2) + (p == 'k' ? 0 : 1)) == "1"; | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
150 | aggregateFlags() { | |
151 | return this.powerFlags; | |
152 | } | |
153 | ||
154 | disaggregateFlags(flags) { | |
155 | this.powerFlags = flags; | |
156 | } | |
157 | ||
91339921 BA |
158 | getFlagsFen() { |
159 | return ['w', 'b'].map(c => { | |
160 | return ['k', 'q'].map(p => this.powerFlags[c][p] ? "1" : "0").join(""); | |
161 | }).join(""); | |
162 | } | |
163 | ||
8f57fbf2 | 164 | setOtherVariables(fenParsed) { |
24872b22 BA |
165 | this.setFlags(fenParsed.flags); |
166 | this.reserve = {}; //to be filled later | |
bc2bc396 | 167 | this.egg = null; |
24872b22 | 168 | this.moveStack = []; |
3b641716 BA |
169 | // Change seed (after FEN generation!!) |
170 | // so that further calls differ between players: | |
171 | Random.setSeed(Math.floor(10000 * Math.random())); | |
f8b43ef7 BA |
172 | } |
173 | ||
c7c2f41c | 174 | // For Toadette bonus |
91339921 | 175 | getDropMovesFrom([c, p]) { |
be3cb9d1 | 176 | if (typeof c != "string" || this.reserve[c][p] == 0) |
c7c2f41c | 177 | return []; |
f8b43ef7 | 178 | let moves = []; |
91339921 | 179 | const start = (c == 'w' && p == 'p' ? 1 : 0); |
3b641716 | 180 | const end = (c == 'b' && p == 'p' ? 7 : 8); |
f8b43ef7 | 181 | for (let i = start; i < end; i++) { |
91339921 BA |
182 | for (let j = 0; j < this.size.y; j++) { |
183 | const pieceIJ = this.getPiece(i, j); | |
3b641716 | 184 | const colIJ = this.getColor(i, j); |
f8b43ef7 | 185 | if ( |
91339921 | 186 | this.board[i][j] == "" || |
3b641716 | 187 | colIJ == 'a' || |
91339921 | 188 | pieceIJ == V.INVISIBLE_QUEEN |
f8b43ef7 | 189 | ) { |
91339921 BA |
190 | let m = new Move({ |
191 | start: {x: c, y: p}, | |
91339921 BA |
192 | appear: [new PiPo({x: i, y: j, c: c, p: p})], |
193 | vanish: [] | |
194 | }); | |
195 | // A drop move may remove a bonus (or hidden queen!) | |
196 | if (this.board[i][j] != "") | |
3b641716 | 197 | m.vanish.push(new PiPo({x: i, y: j, c: colIJ, p: pieceIJ})); |
f8b43ef7 BA |
198 | moves.push(m); |
199 | } | |
200 | } | |
201 | } | |
202 | return moves; | |
203 | } | |
204 | ||
bc2bc396 | 205 | getPotentialMovesFrom([x, y]) { |
f8b43ef7 | 206 | let moves = []; |
3b641716 | 207 | const piece = this.getPiece(x, y); |
bc2bc396 | 208 | if (this.egg == "toadette") |
24872b22 BA |
209 | moves = this.getDropMovesFrom([x, y]); |
210 | else if (this.egg == "kingboo") { | |
3b641716 | 211 | const color = this.turn; |
37481d1e | 212 | const oppCol = C.GetOppCol(color); |
3b641716 | 213 | // Only allow to swap (non-immobilized!) pieces |
37481d1e BA |
214 | for (let i=0; i<this.size.x; i++) { |
215 | for (let j=0; j<this.size.y; j++) { | |
bc2bc396 BA |
216 | const colIJ = this.getColor(i, j); |
217 | const pieceIJ = this.getPiece(i, j); | |
218 | if ( | |
219 | (i != x || j != y) && | |
220 | ['w', 'b'].includes(colIJ) && | |
3b641716 | 221 | !Object.keys(V.IMMOBILIZE_DECODE).includes(pieceIJ) && |
bc2bc396 BA |
222 | // Next conditions = no pawn on last rank |
223 | ( | |
3b641716 | 224 | piece != 'p' || |
bc2bc396 BA |
225 | ( |
226 | (color != 'w' || i != 0) && | |
227 | (color != 'b' || i != this.size.x - 1) | |
228 | ) | |
229 | ) | |
230 | && | |
231 | ( | |
232 | pieceIJ != 'p' || | |
233 | ( | |
234 | (colIJ != 'w' || x != 0) && | |
235 | (colIJ != 'b' || x != this.size.x - 1) | |
236 | ) | |
237 | ) | |
238 | ) { | |
37481d1e | 239 | let m = this.getBasicMove([x, y], [i, j]); |
3b641716 BA |
240 | m.appear.push(new PiPo({x: x, y: y, p: pieceIJ, c: colIJ})); |
241 | m.kingboo = true; //avoid some side effects (bananas/bombs) | |
37481d1e BA |
242 | moves.push(m); |
243 | } | |
244 | } | |
245 | } | |
be3cb9d1 | 246 | } |
24872b22 BA |
247 | else { |
248 | // Normal case (including bonus daisy) | |
24872b22 BA |
249 | switch (piece) { |
250 | case 'p': | |
251 | moves = this.getPawnMovesFrom([x, y]); //apply promotions | |
252 | break; | |
253 | case 'q': | |
254 | moves = this.getQueenMovesFrom([x, y]); | |
255 | break; | |
256 | case 'k': | |
257 | moves = this.getKingMovesFrom([x, y]); | |
258 | break; | |
259 | case 'n': | |
260 | moves = this.getKnightMovesFrom([x, y]); | |
261 | break; | |
262 | case 'b': | |
263 | case 'r': | |
3b641716 BA |
264 | // Explicitely listing types to avoid moving immobilized piece |
265 | moves = super.getPotentialMovesOf(piece, [x, y]); | |
24872b22 BA |
266 | break; |
267 | } | |
268 | } | |
24872b22 BA |
269 | return moves; |
270 | } | |
271 | ||
3b641716 BA |
272 | canStepOver(i, j) { |
273 | return ( | |
274 | this.board[i][j] == "" || | |
275 | [V.MUSHROOM, V.EGG].includes(this.getPiece(i, j))); | |
b0cf998b BA |
276 | } |
277 | ||
37481d1e | 278 | getPawnMovesFrom([x, y]) { |
f8b43ef7 | 279 | const color = this.turn; |
be3cb9d1 BA |
280 | const oppCol = C.GetOppCol(color); |
281 | const shiftX = (color == 'w' ? -1 : 1); | |
282 | const firstRank = (color == "w" ? this.size.x - 1 : 0); | |
f8b43ef7 BA |
283 | let moves = []; |
284 | if ( | |
be3cb9d1 | 285 | this.board[x + shiftX][y] == "" || |
f8b43ef7 BA |
286 | this.getColor(x + shiftX, y) == 'a' || |
287 | this.getPiece(x + shiftX, y) == V.INVISIBLE_QUEEN | |
288 | ) { | |
7562d2c2 | 289 | moves.push(this.getBasicMove([x, y], [x + shiftX, y])); |
f8b43ef7 BA |
290 | if ( |
291 | [firstRank, firstRank + shiftX].includes(x) && | |
292 | ( | |
7562d2c2 | 293 | this.board[x + 2 * shiftX][y] == "" || |
f8b43ef7 BA |
294 | this.getColor(x + 2 * shiftX, y) == 'a' || |
295 | this.getPiece(x + 2 * shiftX, y) == V.INVISIBLE_QUEEN | |
296 | ) | |
297 | ) { | |
7562d2c2 | 298 | moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y])); |
f8b43ef7 BA |
299 | } |
300 | } | |
301 | for (let shiftY of [-1, 1]) { | |
302 | if ( | |
303 | y + shiftY >= 0 && | |
bc2bc396 | 304 | y + shiftY < this.size.y && |
7562d2c2 | 305 | this.board[x + shiftX][y + shiftY] != "" && |
f8b43ef7 BA |
306 | // Pawns cannot capture invisible queen this way! |
307 | this.getPiece(x + shiftX, y + shiftY) != V.INVISIBLE_QUEEN && | |
308 | ['a', oppCol].includes(this.getColor(x + shiftX, y + shiftY)) | |
309 | ) { | |
7562d2c2 | 310 | moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY])); |
f8b43ef7 BA |
311 | } |
312 | } | |
24872b22 | 313 | this.pawnPostProcess(moves, color, oppCol); |
3b641716 | 314 | // Add mushroom on before-last square |
24872b22 | 315 | moves.forEach(m => { |
3b641716 BA |
316 | let revStep = [m.start.x - m.end.x, m.start.y - m.end.y]; |
317 | for (let i of [0, 1]) | |
318 | revStep[i] = revStep[i] / Math.abs(revStep[i]) || 0; | |
319 | const [blx, bly] = [m.end.x + revStep[0], m.end.y + revStep[1]]; | |
320 | m.appear.push(new PiPo({x: blx, y: bly, c: 'a', p: 'm'})); | |
321 | if (blx != x && this.board[blx][bly] != "") { | |
322 | m.vanish.push(new PiPo({ | |
323 | x: blx, | |
324 | y: bly, | |
325 | c: this.getColor(blx, bly), | |
326 | p: this.getPiece(blx, bly) | |
327 | })); | |
24872b22 | 328 | } |
24872b22 | 329 | }); |
3b641716 | 330 | return moves; |
24872b22 BA |
331 | } |
332 | ||
333 | getKnightMovesFrom([x, y]) { | |
334 | // Add egg on initial square: | |
335 | return this.getPotentialMovesOf('n', [x, y]).map(m => { | |
336 | m.appear.push(new PiPo({p: "e", c: "a", x: x, y: y})); | |
337 | return m; | |
338 | }); | |
339 | } | |
340 | ||
37481d1e | 341 | getQueenMovesFrom(sq) { |
24872b22 | 342 | const normalMoves = this.getPotentialMovesOf('q', sq); |
f8b43ef7 BA |
343 | // If flag allows it, add 'invisible movements' |
344 | let invisibleMoves = []; | |
7562d2c2 | 345 | if (this.powerFlags[this.turn]['q']) { |
f8b43ef7 BA |
346 | normalMoves.forEach(m => { |
347 | if ( | |
348 | m.appear.length == 1 && | |
349 | m.vanish.length == 1 && | |
350 | // Only simple non-capturing moves: | |
351 | m.vanish[0].c != 'a' | |
352 | ) { | |
353 | let im = JSON.parse(JSON.stringify(m)); | |
354 | im.appear[0].p = V.INVISIBLE_QUEEN; | |
24872b22 | 355 | im.noAnimate = true; |
f8b43ef7 BA |
356 | invisibleMoves.push(im); |
357 | } | |
358 | }); | |
359 | } | |
360 | return normalMoves.concat(invisibleMoves); | |
361 | } | |
362 | ||
37481d1e | 363 | getKingMovesFrom([x, y]) { |
24872b22 | 364 | let moves = this.getPotentialMovesOf('k', [x, y]); |
f8b43ef7 | 365 | // If flag allows it, add 'remote shell captures' |
7562d2c2 BA |
366 | if (this.powerFlags[this.turn]['k']) { |
367 | super.pieces()['k'].moves[0].steps.forEach(step => { | |
f8b43ef7 BA |
368 | let [i, j] = [x + step[0], y + step[1]]; |
369 | while ( | |
7562d2c2 | 370 | this.onBoard(i, j) && |
f8b43ef7 | 371 | ( |
7562d2c2 | 372 | this.board[i][j] == "" || |
f8b43ef7 BA |
373 | this.getPiece(i, j) == V.INVISIBLE_QUEEN || |
374 | ( | |
375 | this.getColor(i, j) == 'a' && | |
376 | [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j)) | |
377 | ) | |
378 | ) | |
379 | ) { | |
380 | i += step[0]; | |
381 | j += step[1]; | |
382 | } | |
7562d2c2 | 383 | if (this.onBoard(i, j)) { |
f8b43ef7 | 384 | const colIJ = this.getColor(i, j); |
7562d2c2 | 385 | if (colIJ != this.turn) { |
f8b43ef7 | 386 | // May just destroy a bomb or banana: |
24872b22 BA |
387 | let shellCapture = new Move({ |
388 | start: {x: x, y: y}, | |
389 | end: {x: i, y: j}, | |
390 | appear: [], | |
391 | vanish: [ | |
392 | new PiPo({x: i, y: j, c: colIJ, p: this.getPiece(i, j)}) | |
393 | ] | |
394 | }); | |
395 | shellCapture.shell = true; //easier play() | |
3b641716 | 396 | shellCapture.choice = 'z'; //to display in showChoices() |
24872b22 | 397 | moves.push(shellCapture); |
f8b43ef7 BA |
398 | } |
399 | } | |
400 | }); | |
401 | } | |
402 | return moves; | |
403 | } | |
404 | ||
7562d2c2 | 405 | play(move) { |
3b641716 BA |
406 | if (!move.nextComputed) { |
407 | // Set potential random effects, so that play() is deterministic | |
408 | // from opponent viewpoint: | |
409 | const endPiece = this.getPiece(move.end.x, move.end.y); | |
410 | switch (endPiece) { | |
411 | case V.EGG: | |
412 | move.egg = Random.sample(V.EGG_SURPRISE); | |
413 | move.next = this.getEggEffect(move); | |
414 | break; | |
415 | case V.MUSHROOM: | |
416 | move.next = this.getMushroomEffect(move); | |
417 | break; | |
418 | case V.BANANA: | |
419 | case V.BOMB: | |
420 | move.next = this.getBombBananaEffect(move, endPiece); | |
421 | break; | |
422 | } | |
423 | if (!move.next && move.appear.length > 0 && !move.kingboo) { | |
424 | const movingPiece = move.appear[0].p; | |
425 | if (['b', 'r'].includes(movingPiece)) { | |
426 | // Drop a banana or bomb: | |
427 | const bs = | |
428 | this.getRandomSquare([move.end.x, move.end.y], | |
429 | movingPiece == 'r' | |
430 | ? [[1, 1], [1, -1], [-1, 1], [-1, -1]] | |
431 | : [[1, 0], [-1, 0], [0, 1], [0, -1]], | |
432 | "freeSquare"); | |
433 | if (bs) { | |
434 | move.appear.push( | |
435 | new PiPo({ | |
436 | x: bs[0], | |
437 | y: bs[1], | |
438 | c: 'a', | |
439 | p: movingPiece == 'r' ? 'd' : 'w' | |
440 | }) | |
441 | ); | |
442 | if (this.board[bs[0]][bs[1]] != "") { | |
443 | move.vanish.push( | |
444 | new PiPo({ | |
445 | x: bs[0], | |
446 | y: bs[1], | |
447 | c: this.getColor(bs[0], bs[1]), | |
448 | p: this.getPiece(bs[0], bs[1]) | |
449 | }) | |
450 | ); | |
451 | } | |
452 | } | |
453 | } | |
454 | } | |
455 | move.nextComputed = true; | |
456 | } | |
24872b22 | 457 | this.egg = move.egg; |
91339921 | 458 | const color = this.turn; |
3b641716 | 459 | const oppCol = C.GetOppCol(color); |
24872b22 BA |
460 | if (move.egg == "toadette") { |
461 | this.reserve = { w: {}, b: {} }; | |
462 | // Randomly select a piece in pawnPromotions | |
3b641716 BA |
463 | if (!move.toadette) |
464 | move.toadette = Random.sample(this.pawnPromotions); | |
465 | this.reserve[color][move.toadette] = 1; | |
24872b22 | 466 | this.re_drawReserve([color]); |
f8b43ef7 | 467 | } |
24872b22 BA |
468 | else if (Object.keys(this.reserve).length > 0) { |
469 | this.reserve = {}; | |
470 | this.re_drawReserve([color]); | |
f8b43ef7 | 471 | } |
24872b22 BA |
472 | if (move.shell) |
473 | this.powerFlags[color]['k'] = false; | |
3b641716 | 474 | else if (move.appear.length > 0 && move.appear[0].p == V.INVISIBLE_QUEEN) { |
24872b22 | 475 | this.powerFlags[move.appear[0].c]['q'] = false; |
3b641716 BA |
476 | if (color != this.playerColor) |
477 | alert("Invisible queen!"); | |
478 | } | |
479 | if (color == this.playerColor) { | |
480 | // Look for an immobilized piece of my color: it can now move | |
481 | for (let i=0; i<8; i++) { | |
482 | for (let j=0; j<8; j++) { | |
483 | if ((i != move.end.x || j != move.end.y) && this.board[i][j] != "") { | |
484 | const piece = this.getPiece(i, j); | |
485 | if ( | |
486 | this.getColor(i, j) == color && | |
487 | Object.keys(V.IMMOBILIZE_DECODE).includes(piece) | |
488 | ) { | |
489 | move.vanish.push(new PiPo({ | |
490 | x: i, y: j, c: color, p: piece | |
491 | })); | |
492 | move.appear.push(new PiPo({ | |
493 | x: i, y: j, c: color, p: V.IMMOBILIZE_DECODE[piece] | |
494 | })); | |
495 | } | |
f8b43ef7 BA |
496 | } |
497 | } | |
498 | } | |
3b641716 BA |
499 | // Also make opponent invisible queen visible again, if any |
500 | for (let i=0; i<8; i++) { | |
501 | for (let j=0; j<8; j++) { | |
502 | if ( | |
503 | this.board[i][j] != "" && | |
504 | this.getColor(i, j) == oppCol && | |
505 | this.getPiece(i, j) == V.INVISIBLE_QUEEN | |
506 | ) { | |
507 | move.vanish.push(new PiPo({ | |
508 | x: i, y: j, c: oppCol, p: V.INVISIBLE_QUEEN | |
509 | })); | |
510 | move.appear.push(new PiPo({ | |
511 | x: i, y: j, c: oppCol, p: 'q' | |
512 | })); | |
513 | } | |
f8b43ef7 BA |
514 | } |
515 | } | |
516 | } | |
24872b22 BA |
517 | if (!move.next && !["daisy", "toadette", "kingboo"].includes(move.egg)) { |
518 | this.turn = oppCol; | |
91339921 | 519 | this.movesCount++; |
f8b43ef7 | 520 | } |
bc2bc396 BA |
521 | if (move.egg) |
522 | this.displayBonus(move.egg); | |
3b641716 | 523 | this.playOnBoard(move); |
24872b22 | 524 | this.nextMove = move.next; |
bc2bc396 BA |
525 | } |
526 | ||
3b641716 BA |
527 | // Helper to set and apply banana/bomb effect |
528 | getRandomSquare([x, y], steps, freeSquare) { | |
529 | let validSteps = steps.filter(s => this.onBoard(x + s[0], y + s[1])); | |
530 | if (freeSquare) { | |
531 | // Square to put banana/bomb cannot be occupied by a piece | |
532 | validSteps = validSteps.filter(s => { | |
533 | return ["", 'a'].includes(this.getColor(x + s[0], y + s[1])) | |
534 | }); | |
535 | } | |
536 | if (validSteps.length == 0) | |
537 | return null; | |
538 | const step = validSteps[Random.randInt(validSteps.length)]; | |
539 | return [x + step[0], y + step[1]]; | |
f8b43ef7 BA |
540 | } |
541 | ||
3b641716 BA |
542 | getEggEffect(move) { |
543 | const getRandomPiece = (c) => { | |
544 | let bagOfPieces = []; | |
545 | for (let i=0; i<this.size.x; i++) { | |
546 | for (let j=0; j<this.size.y; j++) { | |
547 | if (this.getColor(i, j) == c && this.getPiece(i, j) != 'k') | |
548 | bagOfPieces.push([i, j]); | |
549 | } | |
550 | } | |
551 | if (bagOfPieces.length >= 1) | |
552 | return Random.sample(bagOfPieces); | |
553 | return null; | |
554 | }; | |
555 | const color = this.turn; | |
556 | let em = null; | |
557 | switch (move.egg) { | |
558 | case "luigi": | |
559 | case "waluigi": | |
560 | // Change color of friendly or enemy piece, king excepted | |
561 | const oldColor = (move.egg == "waluigi" ? color : C.GetOppCol(color)); | |
562 | const newColor = C.GetOppCol(oldColor); | |
563 | const coords = getRandomPiece(oldColor); | |
564 | if (coords) { | |
565 | const piece = this.getPiece(coords[0], coords[1]); | |
566 | em = new Move({ | |
567 | appear: [ | |
568 | new PiPo({x: coords[0], y: coords[1], c: newColor, p: piece}) | |
569 | ], | |
570 | vanish: [ | |
571 | new PiPo({x: coords[0], y: coords[1], c: oldColor, p: piece}) | |
572 | ] | |
573 | }); | |
574 | } | |
575 | break; | |
576 | case "bowser": | |
577 | em = new Move({ | |
578 | appear: [ | |
579 | new PiPo({ | |
580 | x: move.end.x, | |
581 | y: move.end.y, | |
582 | c: color, | |
583 | p: V.IMMOBILIZE_CODE[move.appear[0].p] | |
584 | }) | |
585 | ], | |
586 | vanish: [ | |
587 | new PiPo({ | |
588 | x: move.end.x, | |
589 | y: move.end.y, | |
590 | c: color, | |
591 | p: move.appear[0].p | |
592 | }) | |
593 | ] | |
594 | }); | |
595 | break; | |
596 | case "koopa": | |
597 | // Reverse move | |
598 | em = new Move({ | |
599 | appear: [ | |
600 | new PiPo({ | |
601 | x: move.start.x, y: move.start.y, c: color, p: move.appear[0].p | |
602 | }) | |
603 | ], | |
604 | vanish: [ | |
605 | new PiPo({ | |
606 | x: move.end.x, y: move.end.y, c: color, p: move.appear[0].p | |
607 | }) | |
608 | ] | |
609 | }); | |
610 | if (this.board[move.start.x][move.start.y] != "") { | |
611 | // Pawn or knight let something on init square | |
612 | em.vanish.push(new PiPo({ | |
613 | x: move.start.x, | |
614 | y: move.start.y, | |
615 | c: 'a', | |
616 | p: this.getPiece(move.start.x, move.start.y) | |
617 | })); | |
618 | } | |
619 | break; | |
620 | case "chomp": | |
621 | // Eat piece | |
622 | em = new Move({ | |
623 | appear: [], | |
624 | vanish: [ | |
625 | new PiPo({ | |
626 | x: move.end.x, y: move.end.y, c: color, p: move.appear[0].p | |
627 | }) | |
628 | ], | |
629 | end: {x: move.end.x, y: move.end.y} | |
630 | }); | |
631 | break; | |
632 | } | |
633 | if (em && move.egg != "koopa") | |
634 | em.noAnimate = true; //static move | |
635 | return em; | |
f8b43ef7 BA |
636 | } |
637 | ||
24872b22 BA |
638 | getMushroomEffect(move) { |
639 | let step = [move.end.x - move.start.x, move.end.y - move.start.y]; | |
3b641716 | 640 | if ([0, 1].some(i => Math.abs(step[i]) >= 2 && Math.abs(step[1-i]) != 1)) { |
24872b22 BA |
641 | // Slider, multi-squares: normalize step |
642 | for (let j of [0, 1]) | |
643 | step[j] = step[j] / Math.abs(step[j]) || 0; | |
37481d1e | 644 | } |
24872b22 BA |
645 | const nextSquare = [move.end.x + step[0], move.end.y + step[1]]; |
646 | const afterSquare = | |
647 | [nextSquare[0] + step[0], nextSquare[1] + step[1]]; | |
648 | let nextMove = null; | |
3b641716 | 649 | this.playOnBoard(move); //HACK for getBasicMove() below |
24872b22 BA |
650 | if ( |
651 | this.onBoard(nextSquare[0], nextSquare[1]) && | |
3b641716 BA |
652 | ['k', 'p', 'n'].includes(move.vanish[0].p) && |
653 | !['w', 'b'].includes(this.getColor(nextSquare[0], nextSquare[1])) | |
24872b22 BA |
654 | ) { |
655 | // Speed up non-sliders | |
656 | nextMove = this.getBasicMove([move.end.x, move.end.y], nextSquare); | |
657 | } | |
658 | else if ( | |
659 | this.onBoard(afterSquare[0], afterSquare[1]) && | |
660 | this.board[nextSquare[0]][nextSquare[1]] != "" && | |
661 | this.getColor(nextSquare[0], nextSquare[1]) != 'a' && | |
662 | this.getColor(afterSquare[0], afterSquare[1]) != this.turn | |
663 | ) { | |
664 | nextMove = this.getBasicMove([move.end.x, move.end.y], afterSquare); | |
665 | } | |
3b641716 | 666 | this.undoOnBoard(move); |
24872b22 | 667 | return nextMove; |
37481d1e BA |
668 | } |
669 | ||
3b641716 BA |
670 | getBombBananaEffect(move, item) { |
671 | const steps = item == V.BANANA | |
672 | ? [[1, 0], [-1, 0], [0, 1], [0, -1]] | |
673 | : [[1, 1], [1, -1], [-1, 1], [-1, -1]]; | |
674 | const nextSquare = this.getRandomSquare([move.end.x, move.end.y], steps); | |
675 | this.playOnBoard(move); //HACK for getBasicMove() | |
676 | const res = this.getBasicMove([move.end.x, move.end.y], nextSquare); | |
677 | this.undoOnBoard(move); | |
678 | return res; | |
679 | } | |
680 | ||
681 | displayBonus(egg) { | |
682 | alert(egg); //TODO: nicer display | |
683 | } | |
684 | ||
685 | atLeastOneMove() { | |
686 | return true; | |
687 | } | |
688 | ||
689 | filterValid(moves) { | |
690 | return moves; | |
691 | } | |
692 | ||
be3cb9d1 | 693 | playPlusVisual(move, r) { |
37481d1e | 694 | this.moveStack.push(move); |
3b641716 BA |
695 | const nextLines = () => { |
696 | this.play(move); | |
697 | this.playVisual(move, r); | |
698 | if (this.nextMove) | |
699 | this.playPlusVisual(this.nextMove, r); | |
700 | else { | |
701 | this.afterPlay(this.moveStack); | |
702 | this.moveStack = []; | |
703 | } | |
704 | }; | |
705 | if (this.moveStack.length == 1) | |
706 | nextLines(); | |
24872b22 | 707 | else |
3b641716 | 708 | this.animate(move, nextLines); |
be3cb9d1 | 709 | } |
f8b43ef7 BA |
710 | |
711 | }; |