From 4cec374b0172e0888aa2fa33283ad72210be6e56 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Wed, 22 Jun 2022 16:22:13 +0200
Subject: [PATCH] Fix memory leak of moves hash on server side. Draft Align4

---
 server.js                  | 11 +++---
 variants.js                |  2 +-
 variants/Align4/class.js   | 81 ++++++++++++++++++++++++++++++++++++++
 variants/Align4/rules.html |  6 +++
 variants/Align4/style.css  |  1 +
 5 files changed, 94 insertions(+), 7 deletions(-)
 create mode 100644 variants/Align4/class.js
 create mode 100644 variants/Align4/rules.html
 create mode 100644 variants/Align4/style.css

diff --git a/server.js b/server.js
index dc8bd97..e08d13b 100644
--- a/server.js
+++ b/server.js
@@ -7,7 +7,6 @@ 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");
@@ -28,14 +27,14 @@ function initializeGame(vname, players, options) {
     vname: vname,
     players: players,
     options: options,
-    time: Date.now()
+    time: Date.now(),
+    moveHash: {} //set of moves hashes seen so far
   };
   return gid;
 }
 
 // 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() * 19840), gid: gid},
     games[gid]
@@ -195,11 +194,11 @@ wss.on("connection", (socket, req) => {
         const hash = Crypto.createHash("md5")
                      .update(JSON.stringify(obj.fen))
                      .digest("hex");
-        if (moveHash[hash])
+        if (games[obj.gid].moveHash[hash])
           break;
-        moveHash[hash] = true;
+        games[obj.gid].moveHash[hash] = true;
         games[obj.gid].fen = obj.fen;
-        games[obj.gid].time = Date.now(); //update timestamp in case of
+        games[obj.gid].time = Date.now(); //update useful if verrry slow game
         const playingWhite = (games[obj.gid].players[0].sid == sid);
         const oppSid = games[obj.gid].players[playingWhite ? 1 : 0].sid;
         send(oppSid, "newmove", {moves: obj.moves});
diff --git a/variants.js b/variants.js
index 8918f47..5d04f45 100644
--- a/variants.js
+++ b/variants.js
@@ -2,7 +2,7 @@ const variants = [
   {name: 'Absorption', desc: 'Absorb powers'},
   {name: 'Alapo', desc: 'Geometric Chess'},
   {name: 'Alice', desc: 'Both sides of the mirror'},
-//  {name: 'Align4', desc: 'Align four pawns'},
+  {name: 'Align4', desc: 'Align four pawns'},
 //  {name: 'Allmate', desc: 'Mate any piece'},
   {name: 'Ambiguous', desc: "Play opponent's pieces"},
 //  {name: 'Antiking1', desc: 'Keep antiking in check', disp: 'Anti-King'},
diff --git a/variants/Align4/class.js b/variants/Align4/class.js
new file mode 100644
index 0000000..b7b0c4a
--- /dev/null
+++ b/variants/Align4/class.js
@@ -0,0 +1,81 @@
+import ChessRules from "/base_rules.js";
+
+export default class Align4Rules extends ChessRules {
+
+  static get Options() {
+    return {
+      select: [{
+        label: "Randomness",
+        variable: "randomness",
+        defaut: 0,
+        options: [
+          {label: "Deterministic", value: 0},
+          {label: "Random", value: 1}
+        ]
+      }],
+      styles: ["atomic", "capture", "cylinder"]
+    };
+  }
+
+  get hasReserve() {
+    return true;
+  }
+  get hasReserveFen() {
+    return false;
+  }
+
+  genRandInitFen(seed) {
+    const baseFen = super.genRandInitFen(seed);
+    return "4k3/8" + baseFen.substring(17, 50) + " -"; //TODO: + flags 1188
+  }
+
+  setOtherVariables(fenParsed) {
+    super.setOtherVariables(fenParsed);
+    this.reserve = { b: { p: 1 } };
+  }
+
+  // Just do not update any reserve (infinite supply)
+  updateReserve() {}
+
+  getCastleMoves([x, y]) {
+    if (this.GetColor(x, y) == 'b')
+      return [];
+    return super.getCastleMoves([x, y]);
+  }
+
+  getCurrentScore(move) {
+    const score = super.getCurrentScore(move);
+    if (score != "*")
+      return score;
+    // Check pawns connection:
+    for (let i = 0; i < this.size.x; i++) {
+      for (let j = 0; j < this.size.y; j++) {
+        if (
+          this.board[i][j] != "" &&
+          this.getColor(i, j) == 'b' &&
+          this.getPiece(i, j) == 'p'
+        ) {
+          // Exploration "rightward + downward" is enough
+          for (let step of [[1, 0], [0, 1], [1, 1], [-1, 1]]) {
+            let [ii, jj] = [i + step[0], j + step[1]];
+            let kounter = 1;
+            while (
+              this.onBoard(ii, jj) &&
+              this.board[ii][jj] != "" &&
+              this.getColor(ii, jj) == 'b' &&
+              this.getPiece(ii, jj) == 'p'
+            ) {
+              kounter++;
+              ii += step[0];
+              jj += step[1];
+            }
+            if (kounter == 4)
+              return "0-1";
+          }
+        }
+      }
+    }
+    return "*";
+  }
+
+};
diff --git a/variants/Align4/rules.html b/variants/Align4/rules.html
new file mode 100644
index 0000000..691a7a8
--- /dev/null
+++ b/variants/Align4/rules.html
@@ -0,0 +1,6 @@
+<p>
+  Black goal is to align 4 pawns, either orthogonaly or diagonaly.
+  White goal is to checkmate the black king.
+</p>
+
+<p class="author">Fynmorph [Discord] (2021).</p>
diff --git a/variants/Align4/style.css b/variants/Align4/style.css
new file mode 100644
index 0000000..a3550bc
--- /dev/null
+++ b/variants/Align4/style.css
@@ -0,0 +1 @@
+@import url("/base_pieces.css");
-- 
2.44.0