From 68971030c62c551e2bb030260d2346157c5652a9 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Mon, 4 May 2020 00:19:22 +0200
Subject: [PATCH] Add simple Dice chess 8x8

---
 client/src/translations/en.js             |   1 +
 client/src/translations/es.js             |   1 +
 client/src/translations/fr.js             |   1 +
 client/src/translations/rules/Dice/en.pug |  18 +++
 client/src/translations/rules/Dice/es.pug |  19 +++
 client/src/translations/rules/Dice/fr.pug |  19 +++
 client/src/variants/Dice.js               | 140 ++++++++++++++++++++++
 server/db/populate.sql                    |   1 +
 8 files changed, 200 insertions(+)
 create mode 100644 client/src/translations/rules/Dice/en.pug
 create mode 100644 client/src/translations/rules/Dice/es.pug
 create mode 100644 client/src/translations/rules/Dice/fr.pug
 create mode 100644 client/src/variants/Dice.js

diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index f853e52e..59d409b9 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -232,6 +232,7 @@ export const translations = {
   "Reposition pieces": "Reposition pieces",
   "Reuse pieces": "Reuse pieces",
   "Reverse captures": "Reverse captures",
+  "Roll the dice": "Roll the dice",
   "Rotating board": "Rotating board",
   "Run forward": "Run forward",
   "Score a goal": "Score a goal",
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index 3243f92a..e702b325 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -232,6 +232,7 @@ export const translations = {
   "Reposition pieces": "Reposicionar las piezas",
   "Reuse pieces": "Reutilizar piezas",
   "Reverse captures": "Capturas invertidas",
+  "Roll the dice": "Tirar los dados",
   "Rotating board": "Tablero giratorio",
   "Run forward": "Correr hacia adelante",
   "Score a goal": "Marcar una meta",
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index 483f34c3..980742a3 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -232,6 +232,7 @@ export const translations = {
   "Reposition pieces": "Replacer les pièces",
   "Reuse pieces": "Réutiliser les pièces",
   "Reverse captures": "Captures inversées",
+  "Roll the dice": "Lancez le dé",
   "Rotating board": "Échiquier tournant",
   "Run forward": "Courir vers l'avant",
   "Score a goal": "Marquer un but",
diff --git a/client/src/translations/rules/Dice/en.pug b/client/src/translations/rules/Dice/en.pug
new file mode 100644
index 00000000..3fc2354a
--- /dev/null
+++ b/client/src/translations/rules/Dice/en.pug
@@ -0,0 +1,18 @@
+p.boxed.
+  Play the piece type determined by a dice roll.
+
+p.
+  At each turn, click on any empty square first: you will see a piece type
+  written in the moves list, and a piece of this nature will be highlighted on
+  the board. You then have to play a move with this piece's type.
+
+p There is no check or checkmate: the goal is to capture the king.
+
+figure.diagram-container
+  .diagram
+    | fen:r1b2b1r/pp2p3/2p2q2/3p1kpp/1P2Qp2/2K2P1P/P1PP2P1/RNB2BNR:
+  figcaption Both kings could be captured if the dice indicated a queen.
+
+p.
+  Games are likely to be very random with this constraint.
+  Play at your own risk :)
diff --git a/client/src/translations/rules/Dice/es.pug b/client/src/translations/rules/Dice/es.pug
new file mode 100644
index 00000000..4a08a928
--- /dev/null
+++ b/client/src/translations/rules/Dice/es.pug
@@ -0,0 +1,19 @@
+p.boxed.
+  Juega la pieza cuyo tipo está determinado por una tirada de dado.
+
+p.
+  En cada turno, haga clic en una casilla vacía: verá un tipo de pieza
+  escrito en la lista de jugadas, y se indicará una pieza de esta naturaleza
+  en el tablero. Luego debes jugar un movimiento con una pieza de este tipo.
+
+p No hay jaque ni jaque mate: el objetivo es capturar al rey.
+
+figure.diagram-container
+  .diagram
+    | fen:r1b2b1r/pp2p3/2p2q2/3p1kpp/1P2Qp2/2K2P1P/P1PP2P1/RNB2BNR:
+  figcaption.
+    Los dos reyes podrían ser capturados si el dado indica una dama.
+
+p.
+  Es probable que los juegos sean muy aleatorios con esta restricción.
+  Juega bajo tu propio riesgo :)
diff --git a/client/src/translations/rules/Dice/fr.pug b/client/src/translations/rules/Dice/fr.pug
new file mode 100644
index 00000000..42ee7ca1
--- /dev/null
+++ b/client/src/translations/rules/Dice/fr.pug
@@ -0,0 +1,19 @@
+p.boxed.
+  Jouez la pièce dont le type est déterminé par un lancer de dé.
+
+p.
+  À chaque tour, cliquez sur une case vide : vous verrez un type de pièce
+  écrit dans la liste des coups, et une pièce de cette nature sera indiquée
+  sur l'échiquier. Vous devez ensuite jouer un coup avec une pièce de ce type.
+
+p Il n'y a ni échec ni mat : l'objectif est de capturer le roi.
+
+figure.diagram-container
+  .diagram
+    | fen:r1b2b1r/pp2p3/2p2q2/3p1kpp/1P2Qp2/2K2P1P/P1PP2P1/RNB2BNR:
+  figcaption.
+    Les deux rois pourraient être capturés si le dé indiquait une dame.
+
+p.
+  Les parties sont susceptibles d'être très aléatoires avec cette contrainte.
+  Jouez à vos risques et périls :)
diff --git a/client/src/variants/Dice.js b/client/src/variants/Dice.js
new file mode 100644
index 00000000..a8ecf1b9
--- /dev/null
+++ b/client/src/variants/Dice.js
@@ -0,0 +1,140 @@
+import { ChessRules, Move } from "@/base_rules";
+import { randInt } from "@/utils/alea";
+
+export class DiceRules extends ChessRules {
+  static get CanAnalyze() {
+    return true;//false;
+  }
+
+  doClick(square) {
+    if (
+      this.subTurn == 2 ||
+      isNaN(square[0]) ||
+      this.board[square[0]][square[1]] != V.EMPTY
+    ) {
+      return null;
+    }
+    // Announce the piece' type to be played:
+    return this.getRandPieceMove();
+  }
+
+  getPotentialMovesFrom([x, y]) {
+    const L = this.p2play.length;
+    if (
+      this.subTurn == 1 ||
+      // The piece type must match last p2play
+      this.getPiece(x, y) != this.p2play[L-1]
+    ) {
+      return [];
+    }
+    return super.getPotentialMovesFrom([x, y]);
+  }
+
+  setOtherVariables(fen) {
+    super.setOtherVariables(fen);
+    this.p2play = [];
+    this.subTurn = 1;
+  }
+
+  filterValid(moves) {
+    return moves;
+  }
+
+  getCheckSquares() {
+    return [];
+  }
+
+  getCurrentScore() {
+    const color = this.turn;
+    if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0");
+    return "*";
+  }
+
+  play(move) {
+    if (this.subTurn == 1) {
+      this.subTurn = 2;
+      this.p2play.push(move.appear[0].p);
+      return;
+    }
+    // Subturn == 2 means the (dice-constrained) move is played
+    move.flags = JSON.stringify(this.aggregateFlags());
+    V.PlayOnBoard(this.board, move);
+    this.epSquares.push(this.getEpSquare(move));
+    this.movesCount++;
+    this.turn = V.GetOppCol(this.turn);
+    this.subTurn = 1;
+    this.postPlay(move);
+  }
+
+  postPlay(move) {
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      this.kingPos[move.vanish[1].c] = [-1, -1];
+    // Castle flags for captured king won't be updated (not important...)
+    super.postPlay(move);
+  }
+
+  undo(move) {
+    if (this.subTurn == 2) {
+      this.subTurn = 1;
+      this.p2play.pop();
+      return;
+    }
+    this.disaggregateFlags(JSON.parse(move.flags));
+    V.UndoOnBoard(this.board, move);
+    this.epSquares.pop();
+    this.movesCount--;
+    this.turn = V.GetOppCol(this.turn);
+    this.subTurn = 2;
+    this.postUndo(move);
+  }
+
+  postUndo(move) {
+    if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+      this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
+    super.postUndo(move);
+  }
+
+  getRandPieceMove() {
+    // For current turn, find pieces which can move and roll a dice
+    let canMove = {};
+    const color = this.turn;
+    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) {
+          const piece = this.getPiece(i, j);
+          if (
+            !canMove[piece] &&
+            super.getPotentialMovesFrom([i, j]).length > 0
+          ) {
+            canMove[piece] = [i, j];
+          }
+        }
+      }
+    }
+    const options = Object.keys(canMove);
+    const randPiece = options[randInt(options.length)];
+    return (
+      new Move({
+        appear: [{ p: randPiece }],
+        vanish: [],
+        start: { x: -1, y: -1 },
+        end: { x: canMove[randPiece][0], y: canMove[randPiece][1] }
+      })
+    );
+  }
+
+  // Random mover
+  getComputerMove() {
+    const toPlay = this.getRandPieceMove();
+    this.play(toPlay);
+    const moves = this.getAllValidMoves();
+    const choice = moves[randInt(moves.length)];
+    this.undo(toPlay);
+    return [toPlay, choice];
+  }
+
+  getNotation(move) {
+    if (this.subTurn == 1) return move.appear[0].p.toUpperCase();
+    return super.getNotation(move);
+  }
+};
diff --git a/server/db/populate.sql b/server/db/populate.sql
index 9b55c863..a4b2b9a5 100644
--- a/server/db/populate.sql
+++ b/server/db/populate.sql
@@ -4,6 +4,7 @@ insert or ignore into Variants (name, description, noProblems) values
   ('Apocalypse', 'The end of the world', true),
   ('Chakart', 'Capture the princess', true),
   ('Dark', 'In the shadow', true),
+  ('Dice', 'Roll the dice', true),
   ('Hidden', 'Unidentified pieces', true),
   ('Hiddenqueen', 'Queen disguised as a pawn', true),
   ('Synchrone', 'Play at the same time', true);
-- 
2.44.0