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