From 596e24d030f94682a31df74799c13eb792a63cdf Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
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
+  | &nbsp;:
+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