From 596e24d030f94682a31df74799c13eb792a63cdf Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Tue, 28 Apr 2020 06:10:26 +0200 Subject: [PATCH] Chakart beta :) --- client/src/components/BaseGame.vue | 7 +- client/src/components/Board.vue | 1 + client/src/translations/rules/Chakart/en.pug | 123 ++++- client/src/translations/rules/Chakart/es.pug | 129 ++++- client/src/translations/rules/Chakart/fr.pug | 183 +++++-- client/src/variants/Chakart.js | 544 +++++++++++-------- client/src/variants/Teleport.js | 24 +- 7 files changed, 710 insertions(+), 301 deletions(-) diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index 79f089e1..dc78b2af 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -288,9 +288,9 @@ export default { } if (this.mode != "analyze") { // Enter analyze mode: + this.gameMode = this.mode; //was not 'analyze' this.mode = "analyze"; if (this.inMultimove) this.cancelCurrentMultimove(); - this.gameMode = this.mode; //was not 'analyze' this.gameCursor = this.cursor; this.gameMoves = JSON.parse(JSON.stringify(this.moves)); document.getElementById("analyzeBtn").classList.add("active"); @@ -514,8 +514,9 @@ export default { (function executeMove() { const smove = move[moveIdx++]; // NOTE: condition "smove.start.x >= 0" required for Dynamo, - // because second move may be empty. - if (animate && smove.start.x >= 0) { + // because second move may be empty. noHighlight condition + // is used at least for Chakart. + if (animate && smove.start.x >= 0 && !smove.end.noHighlight) { self.animateMove(smove, () => { playSubmove(smove); if (moveIdx < move.length) setTimeout(executeMove, 500); diff --git a/client/src/components/Board.vue b/client/src/components/Board.vue index 0fd4e1f0..a83be0cc 100644 --- a/client/src/components/Board.vue +++ b/client/src/components/Board.vue @@ -638,6 +638,7 @@ export default { const color = this.analyze ? this.vr.turn : this.userColor; if (this.vr.canIplay(color, startSquare)) this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare); + else return; // For potential drag'n drop, remember start coordinates // (to center the piece on mouse cursor) const rect = parent.getBoundingClientRect(); diff --git a/client/src/translations/rules/Chakart/en.pug b/client/src/translations/rules/Chakart/en.pug index 9b5f9a4e..450c487a 100644 --- a/client/src/translations/rules/Chakart/en.pug +++ b/client/src/translations/rules/Chakart/en.pug @@ -1,7 +1,120 @@ p.boxed. - TODO + Some moves have random effects. The goal is to capture the enemy king. -// TODO: banane ou bombe redirige vers bonus, puis enchaînement -// (mushroom ou egg ou autre banane...etc) -// Example a1 to a5 banana => b5 mushroom => jump over c5, arrive on d5, egg -// Possible en sortant les fonctions applyEffect() de getBasicMove() ? +p + | Pieces move as usual, but they all hide a special "power" inspired by + a(href="https://fr.wikipedia.org/wiki/Mario_Kart") Mario Kart + | : +ul + li. + The pawn (Toad) leaves a "turbo" mushroom one square before its + destination. This corresponds to its initial square, except after the + potential initial two squares move. + li. + The knight (Yoshi) let an egg on the first intermediate square + orthogonally adjacent in the direction of the movement. If this is not + possible, then the egg stays on the initial square. + li. + The rook (Donkey) put a banana on a square diagonally adjacent to the + arrival one, if possible. In case of multiple options the choice is left + to the player (click on a square after your move). + li. + The bishop (Wario) put a bomb on a square orthogonally adjacent to the + arrival one, if possible. You can also choose the square. + li. + The queen (Mario) can play a stealth move once in the game: choose the + empty square when the queen is going to an empty square. After this move, + the opponent will know that a queen moved but not where. + A promoted queen also has this power if not already used. + li. + The king (Peach) can "throw a shell" on an enemy reachable by a queen, + once in the game. A promoted king also has this power, if not already used. + The capture is done remotely without moving. + +figure.diagram-container + .diagram + | fen:qbbrrnek/pppppppp/5n2/8/1BwP4/3m4/PPP1PPPP/QBNN1KRR: + figcaption After 1.d4 Nf6 2.Bb4 (put a bomb on c4). + +p. + So the goal is to capture Peach :) + If pawns promoted into king, then all of them must be captured. + Since it still looked too easy '^^ the 4 mentioned objects + alter the played move, generally at random: +ul + li. + A king or a pawn arriving on a mushroom advance one square further + (two if the pawn just moved two squares), while a knight jump another + time in the same direction, if possible. + Pawns can "eat" objects diagonally too. + li. + A rook, bishop or queen arriving on a mushroom can jump over the very next + piece in the movement direction, if the square right after isn't occupied + by a piece of its color. + If the opponent stands there, he is captured. + li. + A piece arriving on a banana (resp. bomb) is redirected at random by one + square in an orthogonal (resp. diagonal) direction, if possible. +p. + The effects can cumulate, as illustrated on the diagram: + the bishop "captures" the banana on e4, and is then redirected twoard e5: + mushroom, it jumps over the black pawn to ends on the bomb on e7, which + sends it on d6 (f6 and f8 were possible too). + A bomb is finally put on c6 which is the only eligible square. + A piece may ends on its initial square, move back, and so on. + That being said, a given object can only be used once on the path. + +figure.diagram-container + .diagram.diag12 + | fen:rn1b2qk/pbppwppp/1w2p3/1p1Rm3/3PdnPr/4P1d1/PPP1mP1P/NNB1KRQB: + .diagram.diag22 + | fen:rn1b2qk/pbpp1ppp/1wwBp3/1p1R4/3P1nPr/4P1d1/PPP1mP1P/NNB1KRQ1: + figcaption Left: before 1.Bxe4. Right: after the move, ending on d6. + +p. + The egg case is more complex: a move ending on an egg triggers an effect + chosen at random, positive or negative. + There are four bonus and four penalties. They are introduced in a dual form: + first the positive, then the negative. +ul + li. + King Boo (*B) let you exchange the position of the playing piece with any + of the pieces of the board. + li Koopa (*K) drives the piece back onto its initial square. + li. + Toadette (*T) allows to place a captured piece on the board (on an empty + square or an object; if it's a banana or a bomb, the effect is applied + and may propagate). + li. + Chomp (*C) eats the piece, which is thus captured. If it's Peach, + then you have lost :) + li Daisy (*D) allows to play again with the same piece. + li. + Bowser (*M) immobilizes the piece (which turns into yellow or red). + It won't be allowed to move on next turn. + li Luigi (*L) change the color of an enemy piece. + li Waluigi (*W) change the color of one of your pieces. + +figure.diagram-container + .diagram.diag12 + | fen:erk2rq1/1m2e1m1/mppmne1m/p1BpppPp/mm1P1QbP/1Nmnw1dm/e1mP2d1/1BwR1RKN: + .diagram.diag22 + | fen:erk3Q1/1m2e1d1/mppmnr1m/p1BpppPp/mm1P1QbP/1Nmnw1dm/e1mP2d1/1BwR1RKN: + figcaption. + 1...Rxf6*W: Waluigi turns the g8 queen into white. + She can capture the king. Bad luck :( + +p. + Note 1: some bonus might not be applicable. In particular, if no piece was + captured "Toadette" cannot be applied. + +p. + Note 2: + in the current implementation pieces are unaffected by the objects they let + on the board. This will probably change. + +h3 Source + +p. + Rules invented by Charlotte Blard, and developed by Benjamin Auder (2020). + These are likely to evolve :) diff --git a/client/src/translations/rules/Chakart/es.pug b/client/src/translations/rules/Chakart/es.pug index 859ebcf1..40532deb 100644 --- a/client/src/translations/rules/Chakart/es.pug +++ b/client/src/translations/rules/Chakart/es.pug @@ -1,2 +1,129 @@ p.boxed. - TODO + Algunos movimientos tienen efectos aleatorios. + El objetivo es capturar al rey contrario. + +p + | Las piezas se mueven como siempre, pero todas tienen un + | "poder" especial inspirado por + a(href="https://fr.wikipedia.org/wiki/Mario_Kart") Mario Kart + | : +ul + li. + El peón (Toad) deja un hongo "turbo" un espacio antes de su espacio + de llegada. Esto corresponde a su casilla inicial, excepto despues el + potencial desplazamiento inicial de dos casillas. + li. + El caballo (Yoshi) pone un huevo en el primer cuadro intermedio + adyacente ortogonalmente en la dirección de su movimiento. Si no + es posible, el huevo se deja en su casilla inicial. + li. + La torre (Donkey) coloca un plátano en un cuadrado diagonalmente adyacente + a la de llegada, si es posible. En caso de múltiples opciones, la elección + le queda el jugador (haga clic en una casilla después de su movimiento). + li. + El alfil (Wario) coloca una bomba en un cuadrado ortogonalmente adyacente + a la de llegada, si es posible. También puedes elegir la casilla. + li. + La dama (Mario) puede hacer un movimiento sigiloso una vez en el juego: + elige la caja vacía cuando la dama se mude a una caja vacía. + Después de este movimiento, el oponente sabrá que tu dama se ha movido + pero no a dónde. + Una dama promovida también tiene este poder si aún no se ha utilizado. + li. + El rey (Peach) puede "arrojar un proyectil" sobre un enemigo dentro del + alcance de una dama, una vez en el juego. + Un rey promovido también tiene este poder, si aún no se ha utilizado. + La captura se lleva a cabo de forma remota sin moverse. + +figure.diagram-container + .diagram + | fen:qbbrrnek/pppppppp/5n2/8/1BwP4/3m4/PPP1PPPP/QBNN1KRR: + figcaption Después de 1.d4 Nf6 2.Bb4 (coloca una bomba en c4). + +p. + Entonces el objetivo es capturar a Peach :) + Si los peones han sido promovidos a rey, entonces todos deben ser capturados + también. Como todavía parecía demasiado fácil '^^ + los 4 objetos mencionados alteran el movimiento, generalmente al azar: +ul + li. + Un rey o un peón que llega a un hongo se mueve una casilla más + (dos si un peón solo ha avanzado dos espacios), mientras que un caballo + haz una jugada adicional en la misma dirección, si es posible. + Los peones también pueden "comer" objetos en diagonal. + li. + Una torre, un alfil o una dama que llega en un hongo puede saltar + encima de una pieza inmediatamente adyacente en la misma dirección, + siempre que la casilla inmediatamente posterior no esté ocupada por una + pieza de su color. Si el oponente está allí, es capturado. + li. + Una pieza que llega en un plátano (resp. bomba) se redirige + aleatoriamente de una casilla en una dirección ortogonal (resp. diagonal), + si es posible. +p. + Los efectos pueden sumar, como se muestra en el diagrama: + el alfil "captura" el plátano en e4, luego es redirigido a e5: hongo, + así que salta sobre el peón negro para encontrarse en la bomba en e7, + que lo envía a d6 (f6 y f8 también son posible). Una bomba es finalmente + presentado en c6, que es la única casilla elegible. + Una pieza puede volver a su espacio original, retroceder, etc. + Dicho esto, un objeto dado solo se puede usar una vez en el camino. + +figure.diagram-container + .diagram.diag12 + | fen:rn1b2qk/pbppwppp/1w2p3/1p1Rm3/3PdnPr/4P1d1/PPP1mP1P/NNB1KRQB: + .diagram.diag22 + | fen:rn1b2qk/pbpp1ppp/1wwBp3/1p1R4/3P1nPr/4P1d1/PPP1mP1P/NNB1KRQ1: + figcaption. + Izquierda: antes de 1.Bxe4. + Derecha: después del movimiento, terminando en d6. + +p. + El caso del huevo es más complejo: un desplazamiento que termina en un huevo + causa un efecto elegido al azar, positivo o negativo. + Hay cuatro bonos y cuatro penalizaciones, de diversa importancia. + Se presentan en forma dual: primero el efecto positivo, + entonces lo negativo. +ul + li. + Rey Boo (*B) te permite cambiar la posición de la pieza que jugó con + cualquier otro en el tablero. + li Koopa (*K) devuelve la pieza a su casilla original. + li. + Toadette (*T) le permite reemplazar una pieza capturada en cualquier lugar + del tablero (en un espacio vacío o un objeto; si este último es un plátano + o una bomba, el efecto se aplica y finalmente continúa). + li. + Chomp (*C) se come la pieza, que termina capturada. Si es Peach, + pues perdiste :) + li Daisy (*D) te permite reproducir un movimiento con la misma pieza. + li. + Bowser (*M) inmoviliza la pieza (que se vuelve amarilla o roja). + Ella no podrá jugar la próxima ronda. + li Luigi (*L) hace que una pieza aleatoria del oponente cambie de bando. + li Waluigi (*W) cambia el color de una de tus piezas. + +figure.diagram-container + .diagram.diag12 + | fen:erk2rq1/1m2e1m1/mppmne1m/p1BpppPp/mm1P1QbP/1Nmnw1dm/e1mP2d1/1BwR1RKN: + .diagram.diag22 + | fen:erk3Q1/1m2e1d1/mppmnr1m/p1BpppPp/mm1P1QbP/1Nmnw1dm/e1mP2d1/1BwR1RKN: + figcaption. + 1...Rxf6*W: Waluigi cambia el color de la dama en g8. + Este puede capturar al rey: sin suerte :( + +p. + Nota 1: algunos bonos pueden no ser aplicables. Especialmente si + ninguna pieza fue capturada, "Toadette" no se aplica. + +p. + Nota 2: + en la implementación actual, las piezas no se ven afectadas por + objetos que dejan caer. Esto sin duda cambiará. + +h3 Fuente + +p. + Reglas inventadas por Charlotte Blard, + y desarrollado por Benjamin Auder (2020). + Estos están sujetos a cambios :) diff --git a/client/src/translations/rules/Chakart/fr.pug b/client/src/translations/rules/Chakart/fr.pug index f9b09312..d966a406 100644 --- a/client/src/translations/rules/Chakart/fr.pug +++ b/client/src/translations/rules/Chakart/fr.pug @@ -1,58 +1,127 @@ p.boxed. - TODO - - //Détails : - //Si une pièce pose quelque chose sur une case ça remplace ce qui y était déjà. - - //Toad: pion - // laisse sur sa case d'arrivée -1 un champi turbo permettant à Peach et cavalier et autres pions d'aller - // un dep plus loin (evt 2 cases si pion saut initial), et aux pièces arrivant sur cette case de sauter par - // dessus une pièce immédiatement adjacente dans leur trajectoire (en atterissant juste derrière, si case vide? Ou avec capture aussi?). - - //Donkey : tour - // pose une banane (optionnel) sur une case adjacente (diagonale) à celle d'arrivée - // Si une pièce arrive sur la peau de banane, alors elle effectue un déplacement - // aléatoire d'une (2?) case (vertical ou horizontal) depuis sa position finale. - - //Wario: fou - // pose une bombe (optionnel) sur une case orthogonalement adjacente à la case d'arrivée - // Si une pièce arrive sur une bombe, alors elle effectue un déplacement diagonal - // aléatoire d'une (2?) case depuis sa position finale (juste une case si impossible). - - //Yoshi: cavalier - // laisse sur sa case de départ un bonus aléatoire - // (NOTE: certains bonus pourraient ne pas être applicables ==> pion bloqué par exemple) - // - i) roi boo(*E*) : échange avec n'importe quelle pièce (choix du joueur, type et/ou couleur différents) - B - // - i*) koopa(*B*) : ramène sur la case initiale - K - // - ii) toadette(*R*) : permet de poser une pièce capturée sur le plateau - T - // (n'importe où sauf 8eme rangée pour les pions) - // - ii*) chomp(*W*) : mange la pièce ; si c'est Peach, c'est perdu - C - // - iii) daisy(*T*) : permet de rejouer un coup avec la même pièce --> cumulable si ensuite coup sur bonus Daisy. - D - // - iii*) bowser(*M*) : immobilise la pièce (marquée jaune/rouge), qui ne pourra pas jouer au tour suivant - I - // - iv) luigi(*L*) : fait changer de camp une pièce adverse (aléatoire) (sauf le roi) - L - // - iv*) waluigi(*D*) : fait changer de camp une de nos pièces (aléatoire, sauf le roi) - W - // --> i, ii, iii en deux temps (subTurn 1 & 2) - - //Mario: dame - // pouvoir "fantôme" : peut effectuer une fois dans la partie un coup non-capturant invisible (=> choix à chaque coup, getPPpath(m) teste m.nvisible...) - //wg bg ghost once in the game the queen can make an invisible move --> printed as "?" - - //Peach: roi - // Carapace rouge (disons ^^) jouable une seule fois dans la partie, - // au lieu de se déplacer. Capture un ennemi au choix parmi les plus proches, - // à condition qu'ils soient visibles (suivant les directions de déplacement d'une dame). - // Profite des accélérateurs posés par les pions (+ 1 case : obligatoire). - - // Promotion pion: n'importe quelle pièce y compris roi => Si plusieurs rois, il faut tous les capturer. - - p. - Règles imaginées par Charlotte Blard - et développées par Benjamin Auder (2020). + Certains coups ont des effets aléatoires. + Le but est de capturer le roi adverse. + +p + | Les pièces se déplacent comme habituellement, mais elles ont toutes un + | "pouvoir" spécial inspiré de + a(href="https://fr.wikipedia.org/wiki/Mario_Kart") Mario Kart + |  : +ul + li. + Le pion (Toad) laisse un champignon "turbo" une case avant sa case + d'arrivée. Cela correspond à sa case initiale, sauf après le potentiel + déplacement initial de deux cases. + li. + Le cavalier (Yoshi) dépose un oeuf sur la première case intermédiaire + othogonalement adjacente dans la direction de son mouvement. Si ce n'est + pas possible, l'oeuf est laissé sur sa case de départ. + li. + La tour (Donkey) pose une banane sur une case diagonalement adjacente à + celle d'arrivée, si possible. En cas d'options multiples le choix est + laissé au joueur (cliquez sur une case après votre coup). + li. + Le fou (Wario) pose une bombe sur une case orthogonalement adjacente à + celle d'arrivée, si possible. + Vous pouvez aussi choisir la case. + li. + La dame (Mario) peut effectuer un coup furtif une fois dans la partie : + choisissez la case vide lorsque la dame se déplace vers une case vide. + Après ce coup, l'adversaire saura que votre dame a bougé mais pas où. + Une dame promue dispose aussi de ce pouvoir s'il n'a pas déjà été utilisé. + li. + Le roi (Peach) peut "lancer une carapace" sur un ennemi à portée de dame, + une fois dans la partie. + Un roi promu dispose également de ce pouvoir, s'il n'a pas encore été + utilisé. La capture s'effectue alors à distance sans se déplacer. + +figure.diagram-container + .diagram + | fen:qbbrrnek/pppppppp/5n2/8/1BwP4/3m4/PPP1PPPP/QBNN1KRR: + figcaption Après 1.d4 Nf6 2.Bb4 (pose une bombe en c4). + +p. + Le but est donc de capturer Peach :) + Si des pions se sont promus en roi, alors il faut tous les capturer aussi. + Comme ça avait encore l'air trop facile '^^ + les 4 objets précités altèrent le coup joué, en général aléatoirement : +ul + li. + Un roi ou un pion arrivant sur un champignon avance une case plus loin + (deux si un pion vient d'avancer de deux cases), tandis qu'un cavalier + effectue un coup supplémentaire dans la même direction, si possible. + Les pions peuvent "manger" les objets en diagonale aussi. + li. + Une tour, un fou ou une dame arrivant sur un champignon peut sauter par + dessus une pièce immédiatement adjacente dans la même direction, à + condition que la case située juste après ne soit pas occupée par une + pièce de sa couleur. Si l'adversaire s'y trouve, il est capturé. + li. + Une pièce arrivant sur une banane (resp. bombe) est redirigée + aléatoirement d'une case dans une direction orthogonale (resp. diagonale), + si possible. +p. + Les effets peuvent se cumuler, comme illustré sur le diagramme : + le fou "capture" la banane en e4, puis est redirigé vers e5 : champignon, + il saute donc par dessus le pion noir pour se retrouver sur la bombe en e7, + qui l'envoie en d6 (f6 et f8 possibles aussi). Une bombe est finalement + posée en c6 qui est la seule case éligible. + Il se peut qu'une pièce revienne sur sa case initiale, recule, etc. + Ceci dit, un objet donné ne peut être utilisé qu'une fois sur le chemin. + +figure.diagram-container + .diagram.diag12 + | fen:rn1b2qk/pbppwppp/1w2p3/1p1Rm3/3PdnPr/4P1d1/PPP1mP1P/NNB1KRQB: + .diagram.diag22 + | fen:rn1b2qk/pbpp1ppp/1wwBp3/1p1R4/3P1nPr/4P1d1/PPP1mP1P/NNB1KRQ1: + figcaption Gauche : avant 1.Bxe4. Droite : après le coup, terminant en d6. + +p. + Le cas de l'oeuf est plus complexe : un déplacement se terminant sur un oeuf + provoque un effet choisi aléatoirement, positif ou négatif. + Il y a quatre bonus et quatre malus, d'importance variable. + Ils sont présentés sous forme duale : d'abord l'effet positif, + puis le négatif. +ul + li. + Le Roi Boo (*B) permet d'échanger la position de la pièce ayant joué avec + n'importe quelle autre sur le plateau. + li Koopa (*K) ramène la pièce sur sa case initiale. + li. + Toadette (*T) permet de replacer une pièce capturée n'importe où sur le + plateau (sur une case vide ou un objet ; si ce dernier est une banane ou + une bombe, l'effet s'applique puis continue éventuellement). + li. + Chomp (*C) mange la pièce, qui se retrouve capturée. Si c'est Peach, + et bien vous avez perdu :) + li Daisy (*D) permet de rejouer un coup avec la même pièce. + li. + Bowser (*M) immobilise la pièce (qui passe en jaune ou rouge). + Celle-ci ne pourra pas jouer au tour suivant. + li Luigi (*L) fait changer de camp une pièce adverse aléatoire. + li Waluigi (*W) fait changer de camp une de vos pièces. + +figure.diagram-container + .diagram.diag12 + | fen:erk2rq1/1m2e1m1/mppmne1m/p1BpppPp/mm1P1QbP/1Nmnw1dm/e1mP2d1/1BwR1RKN: + .diagram.diag22 + | fen:erk3Q1/1m2e1d1/mppmnr1m/p1BpppPp/mm1P1QbP/1Nmnw1dm/e1mP2d1/1BwR1RKN: + figcaption. + 1...Rxf6*W : Waluigi change de couleur la dame en g8. + Celle-ci peut capturer le roi : pas de chance :( + +p. + Note 1 : certains bonus pourraient ne pas être applicables. En particulier si + aucune pièce n'a été capturée "Toadette" ne s'applique pas. + +p. + Note 2 : + dans l'implémentation actuelle les pièces ne sont pas affectées par les + objets qu'elles déposent. Cela changera sans doute. + +h3 Source + +p. + Règles inventées par Charlotte Blard, + et développées par Benjamin Auder (2020). + Celles-ci sont susceptibles d'évoluer :) diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js index 9a026630..45230a14 100644 --- a/client/src/variants/Chakart.js +++ b/client/src/variants/Chakart.js @@ -12,6 +12,10 @@ export class ChakartRules extends ChessRules { return false; } + static get HasEnpassant() { + return false; + } + static get CorrConfirm() { // Because of bonus effects return false; @@ -33,13 +37,10 @@ export class ChakartRules extends ChessRules { const deltaX = Math.abs(fm.appear[0].x - x); const deltaY = Math.abs(fm.appear[0].y - y); return ( - (deltaX == 0 && deltaY == 0) || + (this.board[x][y] == V.EMPTY || this.getColor(x, y) == 'a') && ( - this.board[x][y] == V.EMPTY && - ( - (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) || - (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1) - ) + (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) || + (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1) ) ); } @@ -123,10 +124,17 @@ export class ChakartRules extends ChessRules { const fenParts = fen.split(" "); return Object.assign( ChessRules.ParseFen(fen), - { captured: fenParts[5] } + { captured: fenParts[4] } ); } + static IsGoodFen(fen) { + if (!ChessRules.IsGoodFen(fen)) return false; + const captured = V.ParseFen(fen).captured; + if (!captured || !captured.match(/^[0-9]{12,12}$/)) return false; + return true; + } + // King can be l or L (immobilized) --> similar to Alice variant static IsGoodPosition(position) { if (position.length == 0) return false; @@ -187,11 +195,11 @@ export class ChakartRules extends ChessRules { } getCapturedFen() { - let counts = [...Array(10).fill(0)]; + let counts = [...Array(12).fill(0)]; let i = 0; - for (let p of [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.PAWN]) { + for (let p of V.RESERVE_PIECES) { counts[i] = this.captured["w"][p]; - counts[5 + i] = this.captured["b"][p]; + counts[6 + i] = this.captured["b"][p]; i++; } return counts.join(""); @@ -209,14 +217,16 @@ export class ChakartRules extends ChessRules { [V.KNIGHT]: parseInt(fenParsed.captured[1]), [V.BISHOP]: parseInt(fenParsed.captured[2]), [V.QUEEN]: parseInt(fenParsed.captured[3]), - [V.PAWN]: parseInt(fenParsed.captured[4]), + [V.KING]: parseInt(fenParsed.captured[4]), + [V.PAWN]: parseInt(fenParsed.captured[5]), }, b: { - [V.ROOK]: parseInt(fenParsed.captured[5]), - [V.KNIGHT]: parseInt(fenParsed.captured[6]), - [V.BISHOP]: parseInt(fenParsed.captured[7]), - [V.QUEEN]: parseInt(fenParsed.captured[8]), - [V.PAWN]: parseInt(fenParsed.captured[9]), + [V.ROOK]: parseInt(fenParsed.captured[6]), + [V.KNIGHT]: parseInt(fenParsed.captured[7]), + [V.BISHOP]: parseInt(fenParsed.captured[8]), + [V.QUEEN]: parseInt(fenParsed.captured[9]), + [V.KING]: parseInt(fenParsed.captured[10]), + [V.PAWN]: parseInt(fenParsed.captured[11]), } }; this.firstMove = []; @@ -231,12 +241,22 @@ export class ChakartRules extends ChessRules { return fen; } + getColor(i, j) { + if (i >= V.size.x) return i == V.size.x ? "w" : "b"; + return this.board[i][j].charAt(0); + } + + getPiece(i, j) { + if (i >= V.size.x) return V.RESERVE_PIECES[j]; + return this.board[i][j].charAt(1); + } + getReservePpath(index, color) { return color + V.RESERVE_PIECES[index]; } static get RESERVE_PIECES() { - return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]; + return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING]; } getReserveMoves([x, y]) { @@ -248,16 +268,8 @@ export class ChakartRules extends ChessRules { const end = (color == 'b' && p == V.PAWN ? 7 : 8); for (let i = start; i < end; i++) { for (let j = 0; j < V.size.y; j++) { - // TODO: allow also to drop on bonus square? - // No effect if mushroom, but normal (recursive) effect otherwise. - // - //Koopa --> shift() - // - //Egg: no effect on subTurn == 2 => OK - //Juste bootstrap banan or bomb effect => getBasicMove otherwise - // - // - if (this.board[i][j] == V.EMPTY) { + const colIJ = this.getColor(i, j); + if (this.board[i][j] == V.EMPTY || colIJ == 'a') { let mv = new Move({ appear: [ new PiPo({ @@ -271,6 +283,34 @@ export class ChakartRules extends ChessRules { start: { x: x, y: y }, //a bit artificial... end: { x: i, y: j } }); + if (colIJ == 'a') { + const pieceIJ = this.getPiece(i, j); + mv.vanish.push( + new PiPo({ + x: i, + y: j, + c: colIJ, + p: pieceIJ + }) + ); + if ([V.BANANA, V.BOMB].includes(pieceIJ)) { + // Apply first effect, remove bonus from board, and then + // relay to getBasicMove. Finally merge mv.appear/vanish and + // put back the bonus on the board: + const bSteps = V.steps[pieceIJ == V.BANANA ? V.ROOK : V.BISHOP]; + const sqEffect = this.getRandomSquare([i, j], bSteps); + if (sqEffect[0] != i || sqEffect[1] != j) { + this.board[i][j] = color + p; + const bMove = + this.getBasicMove([i, j], [sqEffect[0], sqEffect[1]]); + this.board[i][j] = 'a' + pieceIJ; + mv.appear[0].x = bMove.appear[0].x; + mv.appear[0].y = bMove.appear[0].y; + for (let k = 1; k < bMove.vanish.length; k++) + mv.vanish.push(bMove.vanish[k]); + } + } + } moves.push(mv); } } @@ -379,17 +419,52 @@ export class ChakartRules extends ChessRules { return [x + step[0], y + step[1]]; } + canMove([x, y], piece) { + const color = this.getColor(x, y); + const oppCol = V.GetOppCol(color); + piece = piece || this.getPiece(x, y); + if (piece == V.PAWN) { + const forward = (color == 'w' ? -1 : 1); + return ( + this.board[x + forward][y] != oppCol || + ( + V.OnBoard(x + forward, y + 1) && + this.board[x + forward][y + 1] != V.EMPTY && + this.getColor[x + forward, y + 1] == oppCol + ) || + ( + V.OnBoard(x + forward, y - 1) && + this.board[x + forward][y - 1] != V.EMPTY && + this.getColor[x + forward, y - 1] == oppCol + ) + ); + } + // Checking one step is enough: + const steps = + [V.KING, V.QUEEN].includes(piece) + ? V.steps[V.ROOK].concat(V.steps[V.BISHOP]) + : V.steps[piece]; + if (!Array.isArray(steps)) debugger; + for (let step of steps) { + const [i, j] = [x + step[0], y + step[1]]; + if ( + V.OnBoard(i, j) && + (this.board[i][j] == V.EMPTY || this.getColor(i, j) != color) + ) { + return true; + } + } + return false; + } + getBasicMove([x1, y1], [x2, y2], tr) { // Apply mushroom, bomb or banana effect (hidden to the player). // Determine egg effect, too, and apply its first part if possible. let move = super.getBasicMove([x1, y1], [x2, y2], tr); const color1 = this.getColor(x1, y1); - const color2 = this.getColor(x2, y2); const piece1 = this.getPiece(x1, y1); - const piece2 = this.getPiece(x2, y2); const oppCol = V.GetOppCol(color1); if ([V.PAWN, V.KNIGHT].includes(piece1)) { - //&& (color2 != 'a' || !([V.BANANA, V.BOMB].includes(piece2))) switch (piece1) { case V.PAWN: { const twoSquaresMove = (Math.abs(x2 - x1) == 2); @@ -463,11 +538,19 @@ export class ChakartRules extends ChessRules { this.board[moveTo[0]][moveTo[1]] != V.EMPTY && this.getColor(moveTo[0], moveTo[1]) == 'a' ) { + move.vanish.push( + new PiPo({ + x: moveTo[0], + y: moveTo[1], + c: 'a', + p: this.getPiece(moveTo[0], moveTo[1]) + }) + ); // Artificially change direction, before applying new effects: x1 = x; y1 = y; x2 = moveTo[0]; - x2 = moveTo[1]; + y2 = moveTo[1]; switch (this.getPiece(moveTo[0], moveTo[1])) { case V.BANANA: applyBeffect(V.steps[V.ROOK]); @@ -490,25 +573,28 @@ export class ChakartRules extends ChessRules { const oppLastRank = (color == 'w' ? 7 : 0); for (let i=0; i<8; i++) { for (let j=0; j<8; j++) { - if ( - this.board[i][j] != V.EMPTY && - this.getColor(i, j) == color - ) { + if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color) { const piece = this.getPiece(i, j); if (piece != V.KING && (piece != V.PAWN || i != oppLastRank)) pieces.push({ x: i, y: j, p: piece }); } } } + // Special case of the current piece (temporarily off board): + if (color == color1) + pieces.push({ x: move.appear[0].x, y: move.appear[0].y, p: piece1 }); const cp = pieces[randInt(pieces.length)]; - move.vanish.push( - new PiPo({ - x: cp.x, - y: cp.y, - c: color, - p: cp.p - }) - ); + if (move.appear[0].x != cp.x || move.appear[0].y != cp.y) { + move.vanish.push( + new PiPo({ + x: cp.x, + y: cp.y, + c: color, + p: cp.p + }) + ); + } + else move.appear.pop(); move.appear.push( new PiPo({ x: cp.x, @@ -536,10 +622,13 @@ export class ChakartRules extends ChessRules { canPlayAgain = true; } else { - move.end.effect = "toadette"; - this.play(move); - canPlayAgain = this.getPotentialMovesFrom([x2, y2]).length > 0; - this.undo(move); + move.end.effect = "daisy"; + this.board[move.start.x][move.start.y] = saveCurrent; + V.PlayOnBoard(this.board, move); + const square = [move.appear[0].x, move.appear[0].y]; + canPlayAgain = this.canMove(square, piece1); + V.UndoOnBoard(this.board, move); + this.board[move.start.x][move.start.y] = V.EMPTY; delete move.end["effect"]; } if (canPlayAgain) effects.push("daisy"); @@ -549,7 +638,7 @@ export class ChakartRules extends ChessRules { return ( cell[0] == oppCol && cell[1] != V.KING && - (cell[1] != V.PAWN || i != lastRank[oppCol]) + (cell[1] != V.PAWN || i != lastRank[color1]) ); }) ) @@ -557,12 +646,16 @@ export class ChakartRules extends ChessRules { effects.push("luigi"); } if ( + ( + piece1 != V.KING && + (piece1 != V.PAWN || move.appear[0].x != lastRank[oppCol]) + ) || this.board.some((b,i) => b.some(cell => { return ( cell[0] == color1 && cell[1] != V.KING && - (cell[1] != V.PAWN || i != lastRank[color1]) + (cell[1] != V.PAWN || i != lastRank[oppCol]) ); }) ) @@ -650,44 +743,52 @@ export class ChakartRules extends ChessRules { this.getColor(next[0], next[1]) != 'a' ) { const afterNext = [next[0] + step[0], next[1] + step[1]]; - if ( - V.OnBoard(afterNext[0], afterNext[1]) && - ( + if (V.OnBoard(afterNext[0], afterNext[1])) { + const afterColor = this.getColor(afterNext[0], afterNext[1]) + if ( this.board[afterNext[0]][afterNext[1]] == V.EMPTY || - this.getColor(afterNext[0], afterNext[1]) == 'a' - ) - ) { - move.appear[0].x = afterNext[0]; - move.appear[0].y = afterNext[1]; - if (this.board[afterNext[0]][afterNext[1]] != V.EMPTY) { - const object = this.getPiece(afterNext[0], afterNext[1]); - move.vanish.push( - new PiPo({ - x: afterNext[0], - y: afterNext[0], - c: 'a', - p: object - }) - ); - switch (object) { - case V.BANANA: - applyBeffect(V.steps[V.ROOK]); - break; - case V.BOMB: - applyBeffect(V.steps[V.BISHOP]); - break; - case V.EGG: - applyEggEffect(); - break; - case V.MUSHROOM: - applyMushroomEffect(); - break; + afterColor != color1 + ) { + move.appear[0].x = afterNext[0]; + move.appear[0].y = afterNext[1]; + if (this.board[afterNext[0]][afterNext[1]] != V.EMPTY) { + // The "object" could also be an opponent's piece + const object = this.getPiece(afterNext[0], afterNext[1]); + move.vanish.push( + new PiPo({ + x: afterNext[0], + y: afterNext[1], + c: afterColor, + p: object + }) + ); + switch (object) { + case V.BANANA: + applyBeffect(V.steps[V.ROOK]); + break; + case V.BOMB: + applyBeffect(V.steps[V.BISHOP]); + break; + case V.EGG: + applyEggEffect(); + break; + case V.MUSHROOM: + applyMushroomEffect(); + break; + } } } } } } }; + const color2 = this.getColor(x2, y2); + const piece2 = this.getPiece(x2, y2); + // In case of (bonus effects might go through initial square): + // TODO: push the idea further, objects left initially should alter the + // trajectory or move as well (mushroom or egg). + const saveCurrent = this.board[move.start.x][move.start.y]; + this.board[move.start.x][move.start.y] = V.EMPTY; if (color2 == 'a') { switch (piece2) { case V.BANANA: @@ -700,57 +801,63 @@ export class ChakartRules extends ChessRules { applyMushroomEffect(); break; case V.EGG: - if (this.subTurn == 1) { + if (this.subTurn == 1) // No egg effect at subTurn 2 - if ([V.ROOK, V.BISHOP].includes(piece1)) { - // Drop a bomb or banana at random, because even if bonus is - // "play again" nothing can be done after next move. - const steps = V.steps[piece1 == V.ROOK ? V.BISHOP : V.ROOK]; - const object = (piece1 == V.ROOK ? V.BANANA : V.BOMB); - const dropOn = this.getRandomSquare([x2, y2], steps); - move.appear.push( - new PiPo({ - x: dropOn[0], - y: dropOn[1], - c: 'a', - p: object - }) - ); - } applyEggEffect(); - } break; } } if ( this.subTurn == 1 && - (color2 != 'a' || piece2 != V.EGG) && + move.appear.length > 0 && [V.ROOK, V.BISHOP].includes(piece1) ) { - move.end.effect = 0; + const finalSquare = [move.appear[0].x, move.appear[0].y]; + if ( + color2 != 'a' || + this.getColor(finalSquare[0], finalSquare[1]) != 'a' || + this.getPiece(finalSquare[0], finalSquare[1]) != V.EGG + ) { + const validSteps = + V.steps[piece1 == V.ROOK ? V.BISHOP : V.ROOK].filter(s => { + const [i, j] = [finalSquare[0] + s[0], finalSquare[1] + s[1]]; + return ( + V.OnBoard(i, j) && + (this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a') + ); + }); + if (validSteps.length >= 2) move.end.effect = 0; + else if (validSteps.length == 1) { + const [x, y] = [ + finalSquare[0] + validSteps[0][0], + finalSquare[1] + validSteps[0][1] + ]; + move.appear.push( + new PiPo({ + x: x, + y: y, + c: 'a', + p: (piece1 == V.ROOK ? V.BANANA : V.BOMB) + }) + ); + if (this.board[x][y] != V.EMPTY) { + move.vanish.push( + new PiPo({ x: x, y: y, c: 'a', p: this.getPiece(x, y) })); + } + } + } } - return move; - } - - getEnpassantCaptures([x, y], shiftX) { - const Lep = this.epSquares.length; - const epSquare = this.epSquares[Lep - 1]; //always at least one element - let enpassantMove = null; + this.board[move.start.x][move.start.y] = saveCurrent; if ( - !!epSquare && - epSquare.x == x + shiftX && - Math.abs(epSquare.y - y) == 1 + move.appear.length == 2 && + move.appear[0].x == move.appear[1].x && + move.appear[0].y == move.appear[1].y ) { - // Not using this.getBasicMove() because the mushroom has no effect - enpassantMove = super.getBasicMove([x, y], [epSquare.x, epSquare.y]); - enpassantMove.vanish.push({ - x: x, - y: epSquare.y, - p: V.PAWN, - c: this.getColor(x, epSquare.y) - }); + // Arrive on bonus left initially: + move.appear.pop(); } - return !!enpassantMove ? [enpassantMove] : []; + return move; + // TODO: if loopback to initial square, simplify move. } getPotentialPawnMoves([x, y]) { @@ -780,15 +887,11 @@ export class ChakartRules extends ChessRules { y + shiftY >= 0 && y + shiftY < sizeY && this.board[x + shiftX][y + shiftY] != V.EMPTY && - this.getColor(x + shiftX, y + shiftY) == oppCol + ['a', oppCol].includes(this.getColor(x + shiftX, y + shiftY)) ) { this.addPawnMoves([x, y], [x + shiftX, y + shiftY], moves); } } - Array.prototype.push.apply( - moves, - this.getEnpassantCaptures([x, y], shiftX) - ); return moves; } @@ -798,7 +901,12 @@ export class ChakartRules extends ChessRules { let invisibleMoves = []; if (this.powerFlags[this.turn][V.QUEEN]) { normalMoves.forEach(m => { - if (m.vanish.length == 1) { + if ( + m.appear.length == 1 && + m.vanish.length == 1 && + // Only simple non-capturing moves: + m.vanish[0].c != 'a' + ) { let im = JSON.parse(JSON.stringify(m)); im.appear[0].p = V.INVISIBLE_QUEEN; im.end.noHighlight = true; @@ -815,9 +923,16 @@ export class ChakartRules extends ChessRules { // If flag allows it, add 'remote shell captures' if (this.powerFlags[this.turn][V.KING]) { V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(step => { + const [nextX, nextY] = [x + step[0], y + step[1]]; if ( - V.OnBoard(x + step[0], y + step[1]) && - this.board[x + step[0]][y + step[1]] == V.EMPTY + V.OnBoard(nextX, nextY) && + ( + this.board[nextX][nextY] == V.EMPTY || + ( + this.getColor(nextX, nextY) == 'a' && + [V.EGG, V.MUSHROOM].includes(this.getPiece(nextX, nextY)) + ) + ) ) { let [i, j] = [x + 2 * step[0], y + 2 * step[1]]; while ( @@ -965,24 +1080,20 @@ export class ChakartRules extends ChessRules { } doClick(square) { - if (isNaN(square[0])) return null; - if (this.subTurn == 1) return null; const L = this.firstMove.length; - const fm = this.firstMove[L-1]; - if (fm.end.effect != 0) return null; + const fm = (L > 0 ? this.firstMove[L-1] : null); + if ( + isNaN(square[0]) || + this.subTurn == 1 || + !([0, "daisy"].includes(fm.end.effect)) + ) { + return null; + } const [x, y] = [square[0], square[1]]; const deltaX = Math.abs(fm.appear[0].x - x); const deltaY = Math.abs(fm.appear[0].y - y); - if (deltaX == 0 && deltaY == 0) { - // Empty move: - return { - start: { x: -1, y: -1 }, - end: { x: -1, y: -1 }, - appear: [], - vanish: [] - }; - } if ( + fm.end.effect == 0 && (this.board[x][y] == V.EMPTY || this.getColor(x, y) == 'a') && ( (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) || @@ -1008,12 +1119,28 @@ export class ChakartRules extends ChessRules { } return m; } + else if ( + fm.end.effect == "daisy" && + deltaX == 0 && deltaY == 0 && + !this.canMove([x, y]) + ) { + // No possible move: return empty move + return { + start: { x: -1, y: -1 }, + end: { x: -1, y: -1 }, + appear: [], + vanish: [] + }; + } return null; } play(move) { +// if (!this.states) this.states = []; +// const stateFen = this.getFen(); +// this.states.push(stateFen); + move.flags = JSON.stringify(this.aggregateFlags()); - this.epSquares.push(this.getEpSquare(move)); V.PlayOnBoard(this.board, move); move.turn = [this.turn, this.subTurn]; if ([0, "kingboo", "toadette", "daisy"].includes(move.end.effect)) { @@ -1031,6 +1158,7 @@ export class ChakartRules extends ChessRules { postPlay(move) { if (move.end.effect == "toadette") this.reserve = this.captured; else this.reserve = undefined; + const color = move.turn[0]; if (move.vanish.length == 2 && move.vanish[1].c != 'a') // Capture: update this.captured this.captured[move.vanish[1].c][move.vanish[1].p]++; @@ -1039,57 +1167,42 @@ export class ChakartRules extends ChessRules { // A piece is back on board this.captured[move.appear[0].c][move.appear[0].p]--; } - - - - -// TODO: simplify and fix this - - - - if (this.subTurn == 1) { - // Update flags: - if ( - this.board[move.start.x][move.start.y] != V.EMPTY && - this.getPiece(move.start.x, move.start.y) == V.KING && - ( - Math.abs(move.end.x - move.start.x) >= 2 || - Math.abs(move.end.y - move.start.y) >= 2 - ) - ) { - const myColor = this.getColor(move.start.x, move.start.y); - this.powerFlags[myColor][V.KING] = false; - } - else if ( - move.vanish[0].p == V.QUEEN && - this.getPiece(move.end.x, move.end.y) == V.INVISIBLE_QUEEN - ) { - this.powerFlags[move.vanish[0].c][V.QUEEN] = false; - } - const color = move.vanish[0].c; + if (move.appear.length == 0) { + // Three cases: king "shell capture", Chomp or Koopa + if (this.getPiece(move.start.x, move.start.y) == V.KING) + // King remote capture: + this.powerFlags[color][V.KING] = false; + else if (move.end.effect == "chomp") + this.captured[color][move.vanish[0].p]++; + } + else if (move.appear[0].p == V.INVISIBLE_QUEEN) + this.powerFlags[move.appear[0].c][V.QUEEN] = false; + if (move.turn[1] == 2) return; + if ( + move.appear.length == 0 || + !(Object.keys(V.IMMOBILIZE_DECODE).includes(move.appear[0].p)) + ) { + // Look for an immobilized piece of my color: it can now move + // Also make opponent invisible queen visible again, if any const oppCol = V.GetOppCol(color); - if (!(Object.keys(V.IMMOBILIZE_DECODE).includes(move.appear[0].p))) { - // Look for an immobilized piece of my color: it can now move - // Also make opponent invisible queen visible again, if any - for (let i=0; i<8; i++) { - for (let j=0; j<8; j++) { - if (this.board[i][j] != V.EMPTY) { - const colIJ = this.getColor(i, j); - const piece = this.getPiece(i, j); - if ( - colIJ == color && - Object.keys(V.IMMOBILIZE_DECODE).includes(piece) - ) { - this.board[i][j] = color + V.IMMOBILIZE_DECODE[piece]; - move.wasImmobilized = [i, j]; - } - else if ( - colIJ == oppCol && - piece == V.INVISIBLE_QUEEN - ) { - this.board[i][j] = oppCol + V.QUEEN; - move.wasInvisible = [i, j]; - } + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + if (this.board[i][j] != V.EMPTY) { + const colIJ = this.getColor(i, j); + const piece = this.getPiece(i, j); + if ( + colIJ == color && + Object.keys(V.IMMOBILIZE_DECODE).includes(piece) + ) { + this.board[i][j] = color + V.IMMOBILIZE_DECODE[piece]; + move.wasImmobilized = [i, j]; + } + else if ( + colIJ == oppCol && + piece == V.INVISIBLE_QUEEN + ) { + this.board[i][j] = oppCol + V.QUEEN; + move.wasInvisible = [i, j]; } } } @@ -1098,7 +1211,6 @@ export class ChakartRules extends ChessRules { } undo(move) { - this.epSquares.pop(); this.disaggregateFlags(JSON.parse(move.flags)); V.UndoOnBoard(this.board, move); if ([0, "kingboo", "toadette", "daisy"].includes(move.end.effect)) @@ -1107,6 +1219,10 @@ export class ChakartRules extends ChessRules { this.turn = move.turn[0]; this.subTurn = move.turn[1]; this.postUndo(move); + +// const stateFen = this.getFen(); +// if (stateFen != this.states[this.states.length-1]) debugger; +// this.states.pop(); } postUndo(move) { @@ -1117,8 +1233,7 @@ export class ChakartRules extends ChessRules { } if (!!move.wasInvisible) { const [i, j] = move.wasInvisible; - this.board[i][j] = - this.getColor(i, j) + V.INVISIBLE_QUEEN; + this.board[i][j] = this.getColor(i, j) + V.INVISIBLE_QUEEN; } if (move.vanish.length == 2 && move.vanish[1].c != 'a') this.captured[move.vanish[1].c][move.vanish[1].p]--; @@ -1127,6 +1242,8 @@ export class ChakartRules extends ChessRules { // A piece was back on board this.captured[move.appear[0].c][move.appear[0].p]++; } + else if (move.appear.length == 0 && move.end.effect == "chomp") + this.captured[move.vanish[0].c][move.vanish[0].p]--; if (move.vanish.length == 0) this.reserve = this.captured; else this.reserve = undefined; } @@ -1157,8 +1274,8 @@ export class ChakartRules extends ChessRules { static GenRandInitFen(randomness) { return ( SuicideRules.GenRandInitFen(randomness).slice(0, -1) + - // Add Peach + Mario flags, re-add en-passant + capture counts - "1111 - 0000000000" + // Add Peach + Mario flags + capture counts + "1111 000000000000" ); } @@ -1175,14 +1292,15 @@ export class ChakartRules extends ChessRules { if (this.subTurn == 2) { const moves2 = this.getAllValidMoves(); move2 = moves2[randInt(moves2.length)]; -console.log(move2); - } this.undo(move1); if (!move2) return move1; return [move1, move2]; } + // TODO: king chomped, king koopa: notation is incomplete. + // Also, king boo effect should be better written like "e4Sg1". + // Toadette placements on bonus square are badly written as well. getNotation(move) { if (move.vanish.length == 0) { if (move.appear.length == 0) return "-"; @@ -1198,14 +1316,27 @@ console.log(move2); return "Q??"; } const finalSquare = V.CoordsToSquare(move.end); - const piece = move.vanish[0].p; + let piece = undefined; if (move.appear.length == 0) { - // Koopa or Chomp + piece = this.getPiece(move.start.x, move.start.y); + if (piece == V.KING) return "Kx" + finalSquare; + // Koopa or Chomp: return ( piece.toUpperCase() + "x" + finalSquare + "*" + (move.end.effect == "koopa" ? "K" : "C") ); } + else if ( + move.appear.length == 1 && + move.vanish.length == 1 && + move.appear[0].c == 'a' && + move.vanish[0].c == 'a' + ) { + // Bonus replacement: + piece = move.appear[0].p.toUpperCase(); + return piece + "@" + finalSquare; + } + piece = move.vanish[0].p; let notation = undefined; if (piece == V.PAWN) { // Pawn move @@ -1247,21 +1378,6 @@ console.log(move2); break; } } - else if (move.vanish.length >= 2 && move.vanish[1].c == 'a') { - let symbol = ""; - switch (move.vanish[1].p) { - case V.MUSHROOM: - symbol = 'M'; - break; - case V.BANANA: - symbol = 'B'; - break; - case V.BOMB: - symbol = 'X'; - break; - } - notation.replace('x', 'x' + symbol); - } return notation; } }; diff --git a/client/src/variants/Teleport.js b/client/src/variants/Teleport.js index f5422c73..7704184f 100644 --- a/client/src/variants/Teleport.js +++ b/client/src/variants/Teleport.js @@ -3,27 +3,9 @@ import { randInt } from "@/utils/alea"; export class TeleportRules extends ChessRules { hoverHighlight(x, y) { - if (this.subTurn == 1 || this.board[x][y] != V.EMPTY) - return false; - // Only highlight if the move is legal - const color = this.turn; - const tMove = new Move({ - appear: [ - new PiPo({ - x: x, - y: y, - c: color, - // The dropped piece nature has no importance: - p: V.KNIGHT - }) - ], - vanish: [], - start: { x: -1, y: -1 } - }); - this.play(tMove); - const moveOk = !this.underCheck(color); - this.undo(tMove); - return moveOk; + // Testing move validity results in an infinite update loop. + // TODO: find a way to test validity anyway. + return (this.subTurn == 2 && this.board[x][y] == V.EMPTY); } setOtherVariables(fen) { -- 2.44.0