From 6b9378a6b88b81f71ce5ad5d332ab6accfe1994b Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 24 Mar 2020 04:11:36 +0100
Subject: [PATCH] Fix Synchrone variant

---
 client/src/base_rules.js           |   9 ---
 client/src/components/BaseGame.vue |  40 +++++----
 client/src/variants/Synchrone.js   | 126 ++++++++++++++++++-----------
 3 files changed, 105 insertions(+), 70 deletions(-)

diff --git a/client/src/base_rules.js b/client/src/base_rules.js
index 448604a4..6da3212f 100644
--- a/client/src/base_rules.js
+++ b/client/src/base_rules.js
@@ -85,15 +85,6 @@ export const ChessRules = class ChessRules {
     return V.CanFlip;
   }
 
-  // Some variants require turn indicator
-  // (generally when analysis or flip is diabled)
-  static get ShowTurn() {
-    return !V.CanAnalyze || V.ShowMoves != "all" || !V.CanFlip;
-  }
-  get showTurn() {
-    return V.ShowTurn;
-  }
-
   static get IMAGE_EXTENSION() {
     // All pieces should be in the SVG format
     return ".svg";
diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue
index 2517c78a..4a451f4f 100644
--- a/client/src/components/BaseGame.vue
+++ b/client/src/components/BaseGame.vue
@@ -95,34 +95,46 @@ export default {
     };
   },
   computed: {
+    turn: function() {
+      if (!this.vr) return "";
+      if (this.vr.showMoves != "all") {
+        return this.st.tr[
+          (this.vr.turn == 'w' ? "White" : "Black") + " to move"];
+      }
+      // Cannot flip: racing king or circular chess
+      return (
+        this.vr.movesCount == 0 && this.game.mycolor == "w"
+          ? this.st.tr["It's your turn!"]
+          : ""
+      );
+    },
+    // TODO: is it OK to pass "computed" as propoerties?
+    // Also, some are seemingly not recomputed when vr is initialized.
     showMoves: function() {
       return this.game.score != "*"
         ? "all"
-        : (this.vr ? this.vr.showMoves : "none");
+        : (!!this.vr ? this.vr.showMoves : "none");
     },
     showTurn: function() {
       return (
         this.game.score == '*' &&
-        !!this.vr && this.vr.showTurn
+        !!this.vr && (this.vr.showMoves != "all" || !this.vr.canFlip)
       );
     },
-    turn: function() {
-      if (!this.vr) return "";
-      if (this.vr.showMoves != "all")
-        return this.st.tr[(this.vr.turn == 'w' ? "White" : "Black") + " to move"];
-      // Cannot flip: racing king or circular chess
-      return this.vr.movesCount == 0 && this.game.mycolor == "w"
-        ? this.st.tr["It's your turn!"]
-        : "";
-    },
     canAnalyze: function() {
-      return this.game.mode != "analyze" && this.vr && this.vr.canAnalyze;
+      return (
+        this.game.mode != "analyze" &&
+        !!this.vr && this.vr.canAnalyze
+      );
     },
     canFlip: function() {
-      return this.vr && this.vr.canFlip;
+      return !!this.vr && this.vr.canFlip;
     },
     allowDownloadPGN: function() {
-      return this.game.score != "*" || (this.vr && this.vr.showMoves == "all");
+      return (
+        this.game.score != "*" ||
+        (!!this.vr && this.vr.showMoves == "all")
+      );
     }
   },
   created: function() {
diff --git a/client/src/variants/Synchrone.js b/client/src/variants/Synchrone.js
index 372bc1ee..cee27c75 100644
--- a/client/src/variants/Synchrone.js
+++ b/client/src/variants/Synchrone.js
@@ -1,11 +1,9 @@
-// TODO: debug, and forbid self-capture of king.
-
 import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class SynchroneRules extends ChessRules {
   static get CanAnalyze() {
-    return true; //false;
+    return false;
   }
 
   static get ShowMoves() {
@@ -116,8 +114,8 @@ export class SynchroneRules extends ChessRules {
     });
   }
 
-  getCaptures(x, y) {
-    const color = this.turn;
+  // Aux function used to find opponent and self captures
+  getCaptures(x, y, color) {
     const sliderAttack = (xx, yy, allowedSteps) => {
       const deltaX = xx - x,
             absDeltaX = Math.abs(deltaX);
@@ -197,40 +195,41 @@ export class SynchroneRules extends ChessRules {
     const color = this.turn;
     // 0) Generate our possible moves
     let myMoves = super.getAllValidMoves();
+    // 1) Generate all opponent's capturing moves
+    let oppCaptureMoves = [];
+    const oppCol = V.GetOppCol(color);
+    for (let i=0; i<8; i++) {
+      for (let j=0; j<8; j++) {
+        if (
+          this.getColor(i, j) == color &&
+          // Do not consider king captures: self-captures of king are forbidden
+          this.getPiece(i, j) != V.KING
+        ) {
+          Array.prototype.push.apply(
+            oppCaptureMoves,
+            this.getCaptures(i, j, oppCol)
+          );
+        }
+      }
+    }
+    // 2) Play each opponent's capture, and see if back-captures are possible:
     // Lookup table to quickly decide if a move is already in list:
     let moveSet = {};
-    const getMoveHash = (move) => {
-      return (
-        "m" + move.start.x + move.start.y +
-              move.end.x + move.end.y +
-              // Also use m.appear[0].p for pawn promotions
-              move.appear[0].p
-      );
-    };
-    myMoves.forEach(m => moveSet[getMoveHash(m)] = true);
-    // 1) Generate all opponent's moves
-    this.turn = V.GetOppCol(color);
-    const oppMoves = super.getAllValidMoves();
-    this.turn = color;
-    // 2) Play each opponent's move, and see if captures are possible:
-    // --> capturing moving unit only (otherwise some issues)
-    oppMoves.forEach(m => {
-      V.PlayOnBoard(this.board, m);
-      // Can I take on [m.end.x, m.end.y] ?
-      // If yes and not already in list, add it (without the capturing part)
-      let capturingMoves = this.getCaptures(m.end.x, m.end.y);
-      capturingMoves.forEach(cm => {
-        const cmHash = getMoveHash(cm);
-        if (!moveSet[cmHash]) {
-          // The captured unit hasn't moved yet, so temporarily cancel capture
-          cm.vanish.pop();
-          // If m is itself a capturing move: then replace by self-capture
-          if (m.vanish.length == 2) cm.vanish.push(m.vanish[1]);
-          myMoves.push(cm);
-          moveSet[cmHash] = true;
-        }
-      });
-      V.UndoOnBoard(this.board, m);
+    oppCaptureMoves.forEach(m => {
+      // If another opponent capture with same endpoint already processed, skip:
+      const mHash = "m" + m.end.x + m.end.y;
+      if (!moveSet[mHash]) {
+        moveSet[mHash] = true;
+        // Just make enemy piece disappear, to clear potential path:
+        const justDisappear = {
+          appear: [],
+          vanish: [m.vanish[0]]
+        };
+        V.PlayOnBoard(this.board, justDisappear);
+        // Can I take on [m.end.x, m.end.y] ? If yes, add to list:
+        this.getCaptures(m.end.x, m.end.y, color).forEach(cm => myMoves.push(cm));
+        V.UndoOnBoard(this.board, justDisappear);
+      }
     });
     return myMoves;
   }
@@ -278,20 +277,28 @@ export class SynchroneRules extends ChessRules {
       smove.appear.push(m1.appear[0]);
       smove.appear.push(m2.appear[0]);
       // "Captured" pieces may have moved:
-      if (
+      if (m1.appear.length == 2) {
+        // Castle
+        smove.appear.push(m1.appear[1]);
+        smove.vanish.push(m1.vanish[1]);
+      } else if (
         m1.vanish.length == 2 &&
         (
-          m2.end.x != m1.vanish[1].x ||
-          m2.end.y != m1.vanish[1].y
+          m1.vanish[1].x != m2.start.x ||
+          m1.vanish[1].y != m2.start.y
         )
       ) {
         smove.vanish.push(m1.vanish[1]);
       }
-      if (
+      if (m2.appear.length == 2) {
+        // Castle
+        smove.appear.push(m2.appear[1]);
+        smove.vanish.push(m2.vanish[1]);
+      } else if (
         m2.vanish.length == 2 &&
         (
-          m1.end.x != m2.vanish[1].x ||
-          m1.end.y != m2.vanish[1].y
+          m2.vanish[1].x != m1.start.x ||
+          m2.vanish[1].y != m1.start.y
         )
       ) {
         smove.vanish.push(m2.vanish[1]);
@@ -313,8 +320,6 @@ export class SynchroneRules extends ChessRules {
       } else {
         // One move is a self-capture and the other a normal capture:
         // only the self-capture appears
-        console.log(m1);
-        console.log(m2);
         const selfCaptureMove =
           m1.vanish[1].c == m1.vanish[0].c
             ? m1
@@ -325,6 +330,12 @@ export class SynchroneRules extends ChessRules {
           p: selfCaptureMove.appear[0].p,
           c: selfCaptureMove.vanish[0].c
         });
+        smove.vanish.push({
+          x: m1.end.x,
+          y: m1.end.y,
+          p: selfCaptureMove.vanish[1].p,
+          c: selfCaptureMove.vanish[0].c
+        });
       }
     }
     return smove;
@@ -361,6 +372,7 @@ export class SynchroneRules extends ChessRules {
     // A full turn just ended:
     const smove = this.resolveSynchroneMove(move);
     V.PlayOnBoard(this.board, smove);
+    move.whiteMove = this.whiteMove; //for undo
     this.whiteMove = null;
 
     // Update king position + flags
@@ -399,18 +411,26 @@ export class SynchroneRules extends ChessRules {
   }
 
   postUndo(move) {
-    if (this.turn == 'w')
+    if (this.turn == 'w') {
       // Reset king positions: scan board
       this.scanKings();
+      // Also reset whiteMove
+      this.whiteMove = null;
+    } else this.whiteMove = move.whiteMove;
   }
 
   getCheckSquares(color) {
-    if (color == 'b') return [];
+    if (color == 'b') {
+      // kingPos must be reset for appropriate highlighting:
+      var lastMove = JSON.parse(JSON.stringify(this.whiteMove));
+      this.undo(lastMove); //will erase whiteMove, thus saved above
+    }
     let res = [];
     if (this.underCheck('w'))
       res.push(JSON.parse(JSON.stringify(this.kingPos['w'])));
     if (this.underCheck('b'))
       res.push(JSON.parse(JSON.stringify(this.kingPos['b'])));
+    if (color == 'b') this.play(lastMove);
     return res;
   }
 
@@ -465,4 +485,16 @@ export class SynchroneRules extends ChessRules {
       candidates.push(i);
     return moves[candidates[randInt(candidates.length)]];
   }
+
+  getNotation(move) {
+    if (move.appear.length == 2 && move.appear[0].p == V.KING)
+      // Castle
+      return move.end.y < move.start.y ? "0-0-0" : "0-0";
+    // Basic system: piece + init + dest square
+    return (
+      move.vanish[0].p.toUpperCase() +
+      V.CoordsToSquare(move.start) +
+      V.CoordsToSquare(move.end)
+    );
+  }
 };
-- 
2.44.0