Fix Bario, write rules
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 16 Jan 2021 13:54:55 +0000 (14:54 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 16 Jan 2021 13:54:55 +0000 (14:54 +0100)
client/src/base_rules.js
client/src/translations/rules/Bario/en.pug
client/src/translations/rules/Bario/es.pug
client/src/translations/rules/Bario/fr.pug
client/src/translations/rules/Football/en.pug
client/src/translations/rules/Football/es.pug
client/src/translations/rules/Football/fr.pug
client/src/variants/Bario.js
client/src/variants/Otage.js
client/src/variants/Pacosako.js
client/src/variants/Refusal.js

index 4c90f1d..a9b6a6a 100644 (file)
@@ -22,8 +22,8 @@ export const Move = class Move {
   constructor(o) {
     this.appear = o.appear;
     this.vanish = o.vanish;
-    this.start = o.start ? o.start : { x: o.vanish[0].x, y: o.vanish[0].y };
-    this.end = o.end ? o.end : { x: o.appear[0].x, y: o.appear[0].y };
+    this.start = o.start || { x: o.vanish[0].x, y: o.vanish[0].y };
+    this.end = o.end || { x: o.appear[0].x, y: o.appear[0].y };
   }
 };
 
index df8d8b6..4dbf74c 100644 (file)
@@ -1,6 +1,61 @@
-p.boxed TODO
+p.boxed
+  | Your pieces remain in an undefined state until they are moved.
 
-p Some links - rules unwritten yet - variant slightly buggish for now.
-p https://www.chessvariants.com/incinf.dir/bario.html
-p https://le-cdn.website-editor.net/20ef5f800ea646c29f6852cfc5ceda07/dms3rep/multi/opt/BAR028-e15a849c-960w.jpg
-p https://www.bario-chess-checkers-chessphotography-spaceart.de/
+p.
+  Queens, rooks, bishops and knights begin the game in a reserve below
+  the board, because their location isn't determined yet.
+  At each turn, you can either move something already defined on the board, or
+ol
+  li move a piece from your reserve to any question mark, and then
+  li move the now defined piece on the board.
+
+p An undefined piece gives check if some specialization giving check exists.
+
+figure.diagram-container
+  .diagram
+    | fen:uuuuu1uk/p2p1qp1/1p6/7p/7P/5N2/PP1P1PP1/UUUKU1UU:
+  figcaption The white king cannot move, a rook might be on c8 or e8
+
+h4 Details, special cases
+
+p.
+  At the first turn, you must select a square for the king anywhere on
+  first rank. This deviates from the original intention of the author,
+  because castling rules would be rather tedious to implement.
+  Thus, you can instead "castle on move 1" :-)
+
+p.
+  If one of your undefined pieces is captured, you first have to choose which
+  one was captured from the reserve: place it at the capture location.
+
+p.
+  If after your move all your pieces are defined (and weren't before), then
+  all pieces on board revert to undefined state, unless:
+ul
+  li.
+    all your pieces (board + reserve) are now of the same type,
+    in which case none is put back in reserve, or
+  li.
+    all opponent's pieces are of the same type, in which case
+    they also remain defined.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:uuu1kuuu/2p5/2n5/1p1ppppp/p4PPP/3N3Q/PPPPP1BB/1RRUK3:
+  .diagram.diag22
+    | fen:uuu1kuuu/2p5/2u5/1p1ppppp/p4PPP/3UU2U/PPPPP1UU/1UU1K3:
+  figcaption.
+    Before and after knight definition on d1 (moving to e3)
+
+h3 More information
+
+p
+  | See the 
+  a(href="https://www.bario-chess-checkers-chessphotography-spaceart.de/")
+    | author's page
+  | &nbsp;with many examples and explanations, and the discussion 
+  a(href="https://www.chessvariants.com/index/listcomments.php?order=DESC&itemid=Bario")
+    | on chessvariants.com
+  | .
+
+p Inventor: Panos Louridas (1998)
index 21203ba..233a304 100644 (file)
@@ -1 +1,65 @@
-p.boxed TODO
+p.boxed
+  | Tus piezas permanecen en un estado indefinido hasta su primer movimiento.
+
+p.
+  Damas, torres, alfiles y caballos comienzan el juego en una reserva bajo
+  del tablero, porque su ubicación aún no está determinada.
+  En cada turno puedes mover algo ya en el tablero, ya sea
+ol
+  li traiga una pieza de su reserva a un signo de interrogación, luego
+  li mueva la pieza recién definida.
+
+p.
+  Una pieza indefinida puede dar jaque si
+  existe un especialización dando jaque.
+
+figure.diagram-container
+  .diagram
+    | fen:uuuuu1uk/p2p1qp1/1p6/7p/7P/5N2/PP1P1PP1/UUUKU1UU:
+  figcaption.
+    El rey blanco no puede moverse, una torre podría ser en c8 o e8
+
+h4 Detalles, casos especiales
+
+p.
+  En el primer turno, debes seleccionar una casilla para el rey en cualquier
+  lugar de la primera fila. Esto se desvía de la intención original del autor,
+  porque las reglas del enroque serían bastante tediosas de implementar.
+  Entonces, en cambio, "enroque en la primera jugada" :-)
+
+p.
+  Si se captura una de sus piezas indefinidas, primero debe elegir la que
+  fue capturado de la reserva: traer la pieza hacia el lugar de captura.
+
+p.
+  Si después de tu jugada todas tus piezas están definidas (y no fueron
+  antes), luego todas las piezas del tablero vuelven
+  en su estado indefinido, a menos que
+ul
+  li.
+    todas tus piezas (tablero + reserva) ahora son del mismo tipo,
+    en cuyo caso ninguno se devuelve a la reserva, o
+  li.
+    todas las piezas opuestas son del mismo tipo, en cuyo caso permanecen
+    también definido.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:uuu1kuuu/2p5/2n5/1p1ppppp/p4PPP/3N3Q/PPPPP1BB/1RRUK3:
+  .diagram.diag22
+    | fen:uuu1kuuu/2p5/2u5/1p1ppppp/p4PPP/3UU2U/PPPPP1UU/1UU1K3:
+  figcaption.
+    Antes y después de la definición del caballo en d1 (trasladado a e3)
+
+h3 Más información
+
+p
+  | Ver la 
+  a(href="https://www.bario-chess-checkers-chessphotography-spaceart.de/")
+    | página del autor
+  | &nbsp;con muchos ejemplos y explicaciones, así como debates 
+  a(href="https://www.chessvariants.com/index/listcomments.php?order=DESC&itemid=Bario")
+    | en chessvariants.com
+  | .
+
+p Inventor: Panos Louridas (1998)
index 21203ba..025f66b 100644 (file)
@@ -1 +1,67 @@
-p.boxed TODO
+p.boxed
+  | Vos pièces restent dans un état indéfini jusqu'à leur premier déplacement.
+
+p.
+  Dames, tours, fous et cavaliers commencent la partie dans une réserve sous
+  l'échiquier, car leur emplacement n'est pas encore déterminé.
+  À chaque tour, vous pouvez soit bouger quelque chose
+  déjà sur l'échiquier, soit
+ol
+  li amener une pièce depuis votre réserve vers un point d'interrogation, puis
+  li déplacer la pièce nouvellement définie.
+
+p.
+  Une pièce indéfinie peut donner échec si
+  une spécialisation donnant échec existe.
+
+figure.diagram-container
+  .diagram
+    | fen:uuuuu1uk/p2p1qp1/1p6/7p/7P/5N2/PP1P1PP1/UUUKU1UU:
+  figcaption.
+    Le roi blanc ne peut pas bouger, une tour pourrait être en c8 ou e8
+
+h4 Détails, cas particuliers
+
+p.
+  Au premier tour, vous devez sélectionner une case pour le roi n'importe où
+  sur la première rangée. Cela dévie de l'intention initiale de l'auteur,
+  car les règles du roque seraient plutîot fastidieuses à implémenter.
+  Ainsi, au lieu de cela vous "roquez au premier coup" :-)
+
+p.
+  Si une de vos pièces indéfinie est capturée, il faudra d'abord choisir
+  laquelle fut capturée depuis la réserve : placez la pièce à l'endroit
+  de la capture.
+
+p.
+  Si après votre coup toutes vos pièces sont définies (et ne l'étaient pas
+  avant), alors toutes les pièces sur l'échiquier reviennent
+  à leur état indéfini, sauf si
+ul
+  li.
+    toutes vos pièces (échiquier + réserve) sont désormais du même type,
+    auquel cas aucune ne revient en réserve, ou
+  li.
+    toutes les pièces adverses sont du même type, auquel cas elles restent
+    également définies.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:uuu1kuuu/2p5/2n5/1p1ppppp/p4PPP/3N3Q/PPPPP1BB/1RRUK3:
+  .diagram.diag22
+    | fen:uuu1kuuu/2p5/2u5/1p1ppppp/p4PPP/3UU2U/PPPPP1UU/1UU1K3:
+  figcaption.
+    Avant et après la définition du cavalier en d1 (déplacé vers e3)
+
+h3 Plus d'information
+
+p
+  | Voir la 
+  a(href="https://www.bario-chess-checkers-chessphotography-spaceart.de/")
+    | page de l'auteur
+  | &nbsp;avec beaucoup d'exemples et d'explications, ainsi que la discussion 
+  a(href="https://www.chessvariants.com/index/listcomments.php?order=DESC&itemid=Bario")
+    | sur chessvariants.com
+  | .
+
+p Inventeur : Panos Louridas (1998)
index 4f20b61..95f7a10 100644 (file)
@@ -6,8 +6,8 @@ p.
   At each turn, a player can first make a normal (non capturing) move.
   If none of his pieces are adjacent to the ball, such move is mandatory.
   But, if any of his pieces stand next to the ball he may then or right
-  away kick it, if the step defined by the arrow piece --> ball is
-  compatible with the piece's movement.
+  away kick it, if the line piece <--> ball is compatible
+  with the piece's movement.
 
 p.
   To play a kick, click on the ball and then on the desired square.
@@ -33,6 +33,8 @@ p Pieces cannot stand in any goal square.
 
 p The ball may never reach or cross the goal horizontally.
 
+p No score at first move (possible in some 960 configurations).
+
 h4 Complete a move on the interface.
 
 p.
@@ -40,6 +42,10 @@ p.
   adjacent to the ball, you'll need to "capture" any of your pieces
   with the ball to indicate that the move is over.
 
+p.
+  Note: bishops stuck in the corner have the extra option
+  to jump over the obstacle. Useful in some randomized setups.
+
 h3 More information
 
 p
index 1f346b6..0ec76e3 100644 (file)
@@ -7,9 +7,8 @@ p.
   (sin captura). Si ninguna de sus piezas está al lado de la bola, tal
   movimiento es obligatorio.
   Pero, si una de sus piezas toque la pelota, entonces puede golpearla
-  después de un desplazamiento inicial o inmediatamente, siempre que el paso
-  definido por la flecha pieza --> globo es compatible con los
-  movimientos de este último.
+  después de un desplazamiento inicial o inmediatamente, siempre que la línea
+  pieza <--> globo sea compatible con los movimientos de la pieza.
 
 p.
   Para mover la bola, haga clic en ella y luego en la casilla de llegada.
@@ -37,6 +36,8 @@ p Ninguna pieza puede estar en una casilla portería.
 
 p El balón nunca puede alcanzar o cruzar la portería horizontalmente.
 
+p Ningún gol en la primera jugada (posible en algunas configuraciones 960).
+
 h4 Completa una jugada en la interfaz.
 
 p.
@@ -44,6 +45,11 @@ p.
   lado de la bola, entonces tienes que "capturar" una de tus piezas con la
   bola para indicar el final de la ronda.
 
+p.
+  Nota: un alfil atrapado en una esquina tiene una opción adicional,
+  para saltar el obstáculo. Requerido para algunas posiciones iniciales
+  aleatorias.
+
 h3 Más información
 
 p
index f06bc90..1f09a53 100644 (file)
@@ -6,9 +6,8 @@ p.
   À chaque tour, un jour peut d'abord effectuer un coup normal (non capturant).
   Si aucune de ses pièces n'est à côté du ballon, un tel coup est obligatoire.
   Mais, si une de ses pièces touche le ballon, alors il peut le frapper soit
-  après un déplacement initial soit tout de suite, à condition que le pas
-  défini par la flèche pièce --> ballon soit compatible avec les
-  mouvements de cette dernière.
+  après un déplacement initial soit tout de suite, à condition que la ligne
+  pièce <--> ballon soit compatible avec les mouvements de la pièce.
 
 p.
   Pour déplacer la balle, cliquez dessus puis sur la case d'arrivée.
@@ -36,6 +35,8 @@ p Aucune pièce ne peut se trouver sur une case but.
 
 p La balle ne peut jamais atteindre ou traverser le but horizontalement.
 
+p Pas de but au premier coup (possible dans certaines configurations 960).
+
 h4 Compléter un coup sur l'interface.
 
 p.
@@ -43,6 +44,11 @@ p.
   du ballon, alors vous devez "capturer" une de vos pièces avec la balle
   pour indiquer la fin du tour.
 
+p.
+  Note : un fou coincé dans un coin d'ispose d'une option supplémentaire,
+  pour sauter par dessus l'obstacle. Nécessaire pour certaines positions
+  de départ aléatoires.
+
 h3 Plus d'information
 
 p
index 5b5b33c..57dd18e 100644 (file)
@@ -2,13 +2,11 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
-// TODO: issue with undo of specialisation to cover check, subTurn decremented to 0
-
 export class BarioRules extends ChessRules {
 
   // Does not really seem necessary (although the author mention it)
   // Instead, first move = pick a square for the king.
-  static get HasCastle() {
+  static get HasFlags() {
     return false;
   }
 
@@ -111,8 +109,8 @@ export class BarioRules extends ChessRules {
   getReserveFen() {
     let counts = new Array(8);
     for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
-      counts[i] = this.reserve["w"][V.PIECES[i]];
-      counts[4 + i] = this.reserve["b"][V.PIECES[i]];
+      counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]];
+      counts[4 + i] = this.reserve["b"][V.RESERVE_PIECES[i]];
     }
     return counts.join("");
   }
@@ -194,17 +192,15 @@ export class BarioRules extends ChessRules {
     if (this.subTurn == 0) {
       const L = this.captureUndefined.length;
       const cu = this.captureUndefined[L-1];
-      return (
+      return [
+        // Nothing changes on the board, just mark start.p for reserve update
         new Move({
-          appear: [
-            new PiPo({ x: cu.x, y: cu.y, c: color, p: p })
-          ],
-          vanish: [
-            new PiPo({ x: cu.x, y: cu.y, c: color, p: V.UNDEFINED })
-          ],
-          start: { x: x, y: y }
+          appear: [],
+          vanish: [],
+          start: { x: x, y: y, p: p },
+          end: { x: cu.x, y: cu.y }
         })
-      );
+      ];
     }
     // or, subTurn == 1 => target any undefined piece that we own.
     let moves = [];
@@ -252,10 +248,24 @@ export class BarioRules extends ChessRules {
     return super.getPotentialMovesFrom([x, y]);
   }
 
-  getAllValidMoves() {
+  getAllPotentialMoves() {
+    const color = this.turn;
+    if (this.movesCount <= 1) {
+      // Just put the king on the board
+      let moves = [];
+      const firstRank = (color == 'w' ? 7 : 0);
+      return [...Array(8)].map((x, j) => {
+        return new Move({
+          appear: [
+            new PiPo({ x: firstRank, y: j, c: color, p: V.KING })
+          ],
+          vanish: [],
+          start: { x: -1, y: -1 }
+        });
+      });
+    }
     const getAllReserveMoves = () => {
       let moves = [];
-      const color = this.turn;
       for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
         moves = moves.concat(
           this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
@@ -276,7 +286,21 @@ export class BarioRules extends ChessRules {
       if (m.vanish.length == 0) return true;
       const start = { x: m.vanish[0].x, y: m.vanish[0].y };
       const end = { x: m.appear[0].x, y: m.appear[0].y };
-      if (start.x == end.x && start.y == end.y) return true; //unfinished turn
+      if (start.x == end.x && start.y == end.y) {
+        // Unfinished turn: require careful check
+        this.play(m);
+        let res = false;
+        if (this.subTurn == 1)
+          // Can either play a move, or specialize a piece
+          res = this.filterValid(this.getAllPotentialMoves()).length > 0;
+        else {
+          // subTurn == 2: can only play a specialized piece
+          res = this.filterValid(
+            this.getPotentialMovesFrom([m.end.x, m.end.y])).length > 0;
+        }
+        this.undo(m);
+        return res;
+      }
       this.play(m);
       const res = !this.underCheck(color);
       this.undo(m);
@@ -356,6 +380,7 @@ export class BarioRules extends ChessRules {
   }
 
   play(move) {
+    move.turn = [this.turn, this.subTurn]; //easier undo (TODO?)
     const toNextPlayer = () => {
       V.PlayOnBoard(this.board, move);
       this.turn = V.GetOppCol(this.turn);
@@ -365,20 +390,21 @@ export class BarioRules extends ChessRules {
       this.postPlay(move);
     };
     if (move.vanish.length == 0) {
-      toNextPlayer();
+      if (move.appear.length == 1) toNextPlayer();
+      else {
+        // Removal (subTurn == 0 --> 1)
+        this.reserve[this.turn][move.start.p]--;
+        this.subTurn++;
+      }
       return;
     }
     const start = { x: move.vanish[0].x, y: move.vanish[0].y };
     const end = { x: move.appear[0].x, y: move.appear[0].y };
     if (start.x == end.x && start.y == end.y) {
-      // Specialisation (subTurn == 1 before 2), or Removal (subTurn == 0).
-      // In both cases, turn not over, and a piece removed from reserve
+      // Specialisation (subTurn == 1 before 2)
       this.reserve[this.turn][move.appear[0].p]--;
-      if (move.appear[0].c == move.vanish[0].c) {
-        // Specialisation: play "move" on board
-        V.PlayOnBoard(this.board, move);
-        this.definitions.push(move.end);
-      }
+      V.PlayOnBoard(this.board, move);
+      this.definitions.push(move.end);
       this.subTurn++;
     }
     else {
@@ -417,31 +443,55 @@ export class BarioRules extends ChessRules {
           })
         ) {
           const piecesList = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
-          let myPieces = {};
+          const oppCol = this.turn;
+          let definedPieces = { w: {}, b: {} };
           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) {
                 const p = this.getPiece(i, j);
-                if (piecesList.includes(p))
-                  myPieces[p] = (!myPieces[p] ? 1 : myPieces[p] + 1);
+                const c = this.getColor(i, j);
+                if (piecesList.includes(p)) {
+                  definedPieces[c][p] =
+                    (!definedPieces[c][p] ? 1 : definedPieces[c][p] + 1);
+                }
               }
             }
           }
-          const pk = Object.keys(myPieces);
-          if (pk.length >= 2) {
+          const my_pk = Object.keys(definedPieces[color]);
+          const opp_pk = Object.keys(definedPieces[oppCol]);
+          const oppRevert = (
+            opp_pk.length >= 2 ||
+            (
+              // Only one opponent's piece is defined, but
+              // at least a different piece wait in reserve:
+              opp_pk.length == 1 &&
+              Object.keys(this.reserve[oppCol]).some(k => {
+                return (k != opp_pk[0] && this.reserve[oppCol][k] >= 1);
+              })
+            )
+          );
+          if (my_pk.length >= 2 || oppRevert) {
+            // NOTE: necessary HACK... because the move is played already.
+            V.UndoOnBoard(this.board, move);
             move.position = this.getBaseFen();
-            for (let p of pk) this.reserve[color][p] = myPieces[p];
-            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 &&
-                  piecesList.includes(this.getPiece(i, j))
-                ) {
-                  this.board[i][j] = color + V.UNDEFINED;
+            move.reserve = JSON.parse(JSON.stringify(this.reserve));
+            V.PlayOnBoard(this.board, move);
+            for (
+              let cp of [{ c: color, pk: my_pk }, { c: oppCol, pk: opp_pk }]
+            ) {
+              if (cp.pk.length >= 2 || (cp.c == oppCol && oppRevert)) {
+                for (let p of cp.pk)
+                  this.reserve[cp.c][p] += definedPieces[cp.c][p];
+                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) == cp.c &&
+                      piecesList.includes(this.getPiece(i, j))
+                    ) {
+                      this.board[i][j] = cp.c + V.UNDEFINED;
+                    }
+                  }
                 }
               }
             }
@@ -454,23 +504,25 @@ export class BarioRules extends ChessRules {
   undo(move) {
     const toPrevPlayer = () => {
       V.UndoOnBoard(this.board, move);
-      this.turn = V.GetOppCol(this.turn);
+      [this.turn, this.subTurn] = move.turn;
       this.movesCount--;
       this.postUndo(move);
     };
     if (move.vanish.length == 0) {
-      toPrevPlayer();
+      if (move.appear.length == 1) toPrevPlayer();
+      else {
+        this.reserve[this.turn][move.start.p]++;
+        this.subTurn = move.turn[1];
+      }
       return;
     }
     const start = { x: move.vanish[0].x, y: move.vanish[0].y };
     const end = { x: move.appear[0].x, y: move.appear[0].y };
     if (start.x == end.x && start.y == end.y) {
       this.reserve[this.turn][move.appear[0].p]++;
-      if (move.appear[0].c == move.vanish[0].c) {
-        V.UndoOnBoard(this.board, move);
-        this.definitions.pop();
-      }
-      this.subTurn--;
+      V.UndoOnBoard(this.board, move);
+      this.definitions.pop();
+      this.subTurn = move.turn[1];
     }
     else {
       this.epSquares.pop();
@@ -492,29 +544,41 @@ export class BarioRules extends ChessRules {
       else {
         if (!!move.position) {
           this.board = V.GetBoard(move.position);
-          this.reserve[color] = {
-            [V.ROOK]: 0,
-            [V.KNIGHT]: 0,
-            [V.BISHOP]: 0,
-            [V.QUEEN]: 0
-          }
+          this.reserve = move.reserve;
         }
       }
     }
   }
 
   getComputerMove() {
+    let initMoves = this.getAllValidMoves();
+    if (initMoves.length == 0) return null;
+    // Loop until valid move is found (no un-specifiable piece...)
     const color = this.turn;
-    // Just play at random for now...
-    let mvArray = [];
-    while (this.turn == color) {
-      const moves = this.getAllValidMoves();
-      const choice = moves[randInt(moves.length)];
-      mvArray.push(choice);
-      this.play(choice);
+    while (true) {
+      let moves = JSON.parse(JSON.stringify(initMoves));
+      let mvArray = [];
+      let mv = null;
+      // Just play random moves (for now at least. TODO?)
+      while (moves.length > 0) {
+        mv = moves[randInt(moves.length)];
+        mvArray.push(mv);
+        this.play(mv);
+        if (this.turn == color) {
+          if (this.subTurn == 1) moves = this.getAllValidMoves();
+          else {
+            // subTurn == 2
+            moves = this.filterValid(
+              this.getPotentialMovesFrom([mv.end.x, mv.end.y]));
+          }
+        }
+        else break;
+      }
+      const thisIsTheEnd = (this.turn != color);
+      for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
+      if (thisIsTheEnd) return (mvArray.length > 1 ? mvArray : mvArray[0]);
     }
-    for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
-    return (mvArray.length == 1? mvArray[0] : mvArray);
+    return null; //never reached
   }
 
   static get VALUES() {
@@ -524,19 +588,16 @@ export class BarioRules extends ChessRules {
   // NOTE: evalPosition is wrong, but unused (random mover)
 
   getNotation(move) {
-    const end = { x: move.appear[0].x, y: move.appear[0].y };
+    const end = { x: move.end.x, y: move.end.y };
     const endSquare = V.CoordsToSquare(end);
+    if (move.appear.length == 0)
+      // Removal
+      return move.start.p.toUpperCase() + endSquare + "X";
     if (move.vanish.length == 0) return "K@" + endSquare;
     const start = { x: move.vanish[0].x, y: move.vanish[0].y };
-    if (start.x == end.x && start.y == end.y) {
-      // Something is specialized, or removed
-      const symbol = move.appear[0].p.toUpperCase();
-      if (move.appear[0].c == move.vanish[0].c)
-        // Specialisation
-        return symbol + "@" + endSquare;
-      // Removal:
-      return symbol + endSquare + "X";
-    }
+    if (start.x == end.x && start.y == end.y)
+      // Something is specialized
+      return move.appear[0].p.toUpperCase() + "@" + endSquare;
     // Normal move
     return super.getNotation(move);
   }
index 98b340e..4cfe1e7 100644 (file)
@@ -761,6 +761,7 @@ export class OtageRules extends ChessRules {
       for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
       if (!mv.end.released) return (mvArray.length > 1 ? mvArray : mvArray[0]);
     }
+    return null; //never reached
   }
 
   // NOTE: evalPosition() is wrong, but unused since bot plays at random
index cde5039..8fc5a86 100644 (file)
@@ -833,6 +833,7 @@ export class PacosakoRules extends ChessRules {
       for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
       if (!mv.end.released) return (mvArray.length > 1 ? mvArray : mvArray[0]);
     }
+    return null; //never reached
   }
 
   // NOTE: evalPosition() is wrong, but unused since bot plays at random
index e4ec04d..76d737d 100644 (file)
@@ -1,9 +1,6 @@
 import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
-// TODO: Two moves, both promoting the same pawn, but to a different type of piece, count as two different moves.
-// ==> need to accept temporarily pawn promotions even if on forbidden square, and check afterward if promoted type changed (info in lastMove)
-
 export class RefusalRules extends ChessRules {
 
   static IsGoodFen(fen) {
@@ -50,7 +47,11 @@ export class RefusalRules extends ChessRules {
     if (this.getColor(x, y) != this.turn) {
       const L = this.lastMove.length;
       const lm = this.lastMove[L-1];
-      if (!!lm && !lm.noRef && x == lm.end.x && y == lm.end.y) {
+      const beforeLastRank = (this.turn == 'w' ? 1 : 6);
+      if (
+        !!lm && !lm.noRef && x == lm.end.x && y == lm.end.y &&
+        (this.getPiece(x, y) != V.PAWN || x != beforeLastRank)
+      ) {
         let revLm = JSON.parse(JSON.stringify(lm));
         let tmp = revLm.appear;
         revLm.appear = revLm.vanish;
@@ -94,7 +95,8 @@ export class RefusalRules extends ChessRules {
       if (
         !!lm && !!lm.refusal &&
         m.start.x == lm.end.x && m.start.y == lm.end.y &&
-        m.end.x == lm.start.x && m.end.y == lm.start.y
+        m.end.x == lm.start.x && m.end.y == lm.start.y &&
+        (m.vanish[0].p != V.PAWN || m.appear[0].p == lm.vanish[0].p)
       ) {
         return false;
       }