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