From c4e9bb928964d723ee624a449c3342e2ef9140f8 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Thu, 2 Jun 2022 20:55:02 +0200
Subject: [PATCH] Bugs fixing attempt

---
 TODO          |  6 ------
 app.js        |  2 +-
 base_rules.js | 56 +++++++++++++++++++++++++--------------------------
 common.css    |  6 ------
 server.js     |  9 +++++++++
 5 files changed, 38 insertions(+), 41 deletions(-)

diff --git a/TODO b/TODO
index 09af34f..0545a54 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,3 @@
-Fix rescaling by adopting a different strategy: use mouse scroll instead, +/- 5% each time ?
-Seems easier to control width/height accurately and safely in this way...
-
-Impossible to play / triple move in Doublemove : turn issues, maybe because a move is sent twice to the server...
-=> solution = sign (hash?) moves, and if an already received signature received again, ignore?
-
 add variants : Chakart first (dark,cylinder)
 Dark Racing Kings ?
 Checkered-Teleport ?
diff --git a/app.js b/app.js
index c0c74c0..79aa6e0 100644
--- a/app.js
+++ b/app.js
@@ -516,7 +516,7 @@ function initializeGame(obj) {
           </g>
         </svg>
       </div>
-      <div class="resizeable chessboard"></div>`;
+      <div class="chessboard"></div>`;
     vr = new V({
       seed: obj.seed, //may be null if FEN already exists (running game)
       fen: obj.fen,
diff --git a/base_rules.js b/base_rules.js
index 3117506..f73700b 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -533,8 +533,6 @@ export default class ChessRules {
     this.initMouseEvents();
     const chessboard =
       document.getElementById(this.containerId).querySelector(".chessboard");
-    // TODO: calling with "this" seems required by Hex. Understand why...
-    new ResizeObserver(() => this.rescale(this)).observe(chessboard);
   }
 
   re_drawBoardElements() {
@@ -773,25 +771,22 @@ export default class ChessRules {
     }
   }
 
-  // After resize event: no need to destroy/recreate pieces
-  rescale(self) {
-    const container = document.getElementById(self.containerId);
-    if (!container)
-      return; //useful at initial loading
-    let chessboard = container.querySelector(".chessboard");
-    let r = chessboard.getBoundingClientRect();
-    let [newWidth, newHeight] = [r.width, r.height];
+  // Resize board: no need to destroy/recreate pieces
+  rescale(mode) {
+    let chessboard =
+      document.getElementById(this.containerId).querySelector(".chessboard");
+    const r = chessboard.getBoundingClientRect();
+    const multFact = (mode == "up" ? 1.05 : 0.95);
+    let [newWidth, newHeight] = [multFact * r.width, multFact * r.height];
     // Stay in window:
-    if (newWidth > window.innerWidth)
+    if (newWidth > window.innerWidth) {
       newWidth = window.innerWidth;
-    if (newHeight > window.innerHeight)
+      newHeight = newWidth / this.size.ratio;
+    }
+    if (newHeight > window.innerHeight) {
       newHeight = window.innerHeight;
-    const newRatio = newWidth / newHeight;
-    const epsilon = 1e-4; //arbitrary small value to avoid instabilities
-    if (newRatio - self.size.ratio > epsilon)
-      newWidth = newHeight * self.size.ratio;
-    else if (newRatio - self.size.ratio < -epsilon)
-      newHeight = newWidth / self.size.ratio;
+      newWidth = newHeight * this.size.ratio;
+    }
     chessboard.style.width = newWidth + "px";
     chessboard.style.height = newHeight + "px";
     const newX = (window.innerWidth - newWidth) / 2;
@@ -799,26 +794,26 @@ export default class ChessRules {
     const newY = (window.innerHeight - newHeight) / 2;
     chessboard.style.top = newY + "px";
     const newR = {x: newX, y: newY, width: newWidth, height: newHeight};
-    const pieceWidth = self.getPieceWidth(newWidth);
+    const pieceWidth = this.getPieceWidth(newWidth);
     // NOTE: next "if" for variants which use squares filling
     // instead of "physical", moving pieces
     if (this.g_pieces) {
-      for (let i=0; i < self.size.x; i++) {
-        for (let j=0; j < self.size.y; j++) {
-          if (self.g_pieces[i][j]) {
+      for (let i=0; i < this.size.x; i++) {
+        for (let j=0; j < this.size.y; j++) {
+          if (this.g_pieces[i][j]) {
             // NOTE: could also use CSS transform "scale"
-            self.g_pieces[i][j].style.width = pieceWidth + "px";
-            self.g_pieces[i][j].style.height = pieceWidth + "px";
-            const [ip, jp] = self.getPixelPosition(i, j, newR);
+            this.g_pieces[i][j].style.width = pieceWidth + "px";
+            this.g_pieces[i][j].style.height = pieceWidth + "px";
+            const [ip, jp] = this.getPixelPosition(i, j, newR);
             // Translate coordinates to use chessboard as reference:
-            self.g_pieces[i][j].style.transform =
+            this.g_pieces[i][j].style.transform =
               `translate(${ip - newX}px,${jp - newY}px)`;
           }
         }
       }
     }
-    if (self.hasReserve)
-      self.rescaleReserve(newR);
+    if (this.hasReserve)
+      this.rescaleReserve(newR);
   }
 
   rescaleReserve(r) {
@@ -977,10 +972,15 @@ export default class ChessRules {
       curPiece.remove();
     };
 
+    const wheelResize = (e) => {
+      this.rescale(e.deltaY < 0 ? "up" : "down");
+    };
+
     if ('onmousedown' in window) {
       document.addEventListener("mousedown", mousedown);
       document.addEventListener("mousemove", mousemove);
       document.addEventListener("mouseup", mouseup);
+      document.addEventListener("wheel", wheelResize);
     }
     if ('ontouchstart' in window) {
       // https://stackoverflow.com/a/42509310/12660887
diff --git a/common.css b/common.css
index e359955..c1ba77c 100644
--- a/common.css
+++ b/common.css
@@ -277,12 +277,6 @@ a {
 .chessboard {
   position: absolute;
   cursor: pointer;
-}
-
-/* Board container can be resized */
-.resizeable {
-  resize: both;
-  overflow: hidden;
   min-width: 200px;
   min-height: 200px;
 }
diff --git a/server.js b/server.js
index 0382e37..7e1a99b 100644
--- a/server.js
+++ b/server.js
@@ -7,6 +7,7 @@ const wss = new WebSocket.Server({
 
 let challenges = {}; //variantName --> socketId, name
 let games = {}; //gameId --> gameInfo (vname, fen, players, options, time)
+let moveHash = {}; //gameId --> set of hashes seen so far
 let sockets = {}; //socketId --> socket
 const variants = require("./variants.js");
 const Crypto = require("crypto");
@@ -34,6 +35,7 @@ function initializeGame(vname, players, options) {
 
 // Provide seed in case of, so that both players initialize with same FEN
 function launchGame(gid) {
+  moveHash[gid] = {};
   const gameInfo = Object.assign(
     {seed: Math.floor(Math.random() * 1984), gid: gid},
     games[gid]
@@ -193,6 +195,13 @@ wss.on("connection", (socket, req) => {
         break;
       // Relay a move + update games object
       case "newmove":
+        // NOTE: still potential racing issues, but... fingers crossed
+        const hash = Crypto.createHash("md5")
+                     .update(JSON.stringify(obj.fen))
+                     .digest("hex");
+        if (moveHash[hash])
+          break;
+        moveHash[hash] = true;
         games[obj.gid].fen = obj.fen;
         games[obj.gid].time = Date.now(); //update timestamp in case of
         const playingWhite = (games[obj.gid].players[0].sid == sid);
-- 
2.44.0