From de1421be3ee53cb4ea8f112834d3de7a863fdd40 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Fri, 17 Feb 2023 09:18:56 +0100
Subject: [PATCH] Draft Checkless. Add 'trackKingWrap' method, untested. Use
 'canSelfTake'

---
 README.md                   |  2 +-
 base_rules.js               | 63 ++++++++++++++++++++++---------------
 nodemon.json                | 26 +++++++++++++++
 package-lock.json           | 12 +++----
 package.json                |  6 ++--
 start.sh                    |  2 +-
 variants.js                 |  2 +-
 variants/Checkless/class.js | 46 +++++++++++++++++++++++++++
 variants/Teleport/class.js  | 12 +++++--
 9 files changed, 132 insertions(+), 39 deletions(-)
 create mode 100644 nodemon.json
 create mode 100644 variants/Checkless/class.js

diff --git a/README.md b/README.md
index 5f60c1d..b0517cd 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,6 @@ Rename parameters.js.dist &rarr; parameters.js, and edit file. <br/>
 ```npm i```
 
 Generate some pieces: <br/>
-```python generateSVG.py``` in variants/Avalam/pieces
+```python generateSVG.py``` in pieces/Avalam
 
 ```./start.sh``` (and later, ```./stop.sh```)
diff --git a/base_rules.js b/base_rules.js
index e127b74..0c316eb 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -1321,9 +1321,9 @@ export default class ChessRules {
     return this.getColor(x1, y1) !== this.getColor(x2, y2);
   }
 
-  // TODO: currently unused, but makes sense?
+  // Teleport & Recycle. Assumption: color(x1,y1) == color(x2,y2)
   canSelfTake([x1, y1], [x2, y2]) {
-    return true;
+    return !this.isKing(x2, y2);
   }
 
   canStepOver(i, j, p) {
@@ -1488,6 +1488,7 @@ export default class ChessRules {
   }
 
   // All possible moves from selected square
+  // TODO: generalize usage if arg "color" (e.g. Checkered)
   getPotentialMovesFrom([x, y], color) {
     if (this.subTurnTeleport == 2)
       return [];
@@ -1722,8 +1723,12 @@ export default class ChessRules {
           segments: this.options["cylinder"],
           stepSpec: stepSpec
         },
-        ([i1, j1], [i2, j2]) =>
-          this.getColor(i2, j2) == color && !this.isKing(i2, j2)
+        ([i1, j1], [i2, j2]) => {
+          return (
+            this.getColor(i2, j2) == color &&
+            this.canSelfTake([i1, j1], [i2, j2])
+          );
+        }
       );
       Array.prototype.push.apply(squares, selfCaptures);
     }
@@ -2164,6 +2169,31 @@ export default class ChessRules {
     return res;
   }
 
+  // cb: callback returning a boolean (false if king missing)
+  trackKingWrap(move, kingPos, cb) {
+    let newKingPP = null,
+        sqIdx = 0,
+        res = true; //a priori valid
+    const oldKingPP =
+      move.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
+    if (oldKingPP) {
+      // Search king in appear array:
+      newKingPP =
+        move.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
+      if (newKingPP) {
+        sqIdx = kingPos.findIndex(kp =>
+          kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
+        kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
+      }
+      else
+        res = false; //king vanished
+    }
+    res &&= cb(kingPos);
+    if (oldKingPP && newKingPP)
+      kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
+    return res;
+  }
+
   // 'color' arg because some variants (e.g. Refusal) check opponent moves
   filterValid(moves, color) {
     if (!color)
@@ -2172,26 +2202,9 @@ export default class ChessRules {
     let kingPos = this.searchKingPos(color);
     return moves.filter(m => {
       this.playOnBoard(m);
-      let newKingPP = null,
-          sqIdx = 0,
-          res = true; //a priori valid
-      const oldKingPP =
-        m.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
-      if (oldKingPP) {
-        // Search king in appear array:
-        newKingPP =
-          m.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
-        if (newKingPP) {
-          sqIdx = kingPos.findIndex(kp =>
-            kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
-          kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
-        }
-        else
-          res = false; //king vanished
-      }
-      res &&= !this.underCheck(kingPos, oppCols);
-      if (oldKingPP && newKingPP)
-        kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
+      const res = this.trackKingWrap(m, kingPos, (kp) => {
+        return !this.underCheck(kp, oppCols);
+      });
       this.undoOnBoard(m);
       return res;
     });
@@ -2367,7 +2380,7 @@ export default class ChessRules {
         if (this.board[i][j] != "" && this.getColor(i, j) == color) {
           // NOTE: in fact searching for all potential moves from i,j.
           //       I don't believe this is an issue, for now at least.
-          const moves = this.getPotentialMovesFrom([i, j]);
+          const moves = this.getPotentialMovesFrom([i, j], color);
           if (moves.some(m => this.filterValid([m]).length >= 1))
             return true;
         }
diff --git a/nodemon.json b/nodemon.json
new file mode 100644
index 0000000..d9ffb96
--- /dev/null
+++ b/nodemon.json
@@ -0,0 +1,26 @@
+{
+  "ignore": [
+    "app.js",
+    "assets/",
+    "assets.zip","
+    "base_pieces.css",
+    "base_rules.js",
+    "common.css",
+    "favicon.ico",
+    "index.html",
+    "LICENSE",
+    "parameters.js",
+    "parameters.js.dist",
+    "pieces/",
+    "README.md",
+    "start.sh",
+    "stop.sh",
+    "utils/",
+    "TODO",
+    "variants/",
+    "variants.js"
+  ],
+  "watch": [
+    "server.js"
+  ]
+}
diff --git a/package-lock.json b/package-lock.json
index d553246..4f5febd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,9 +12,9 @@
       }
     },
     "node_modules/anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
       "dev": true,
       "dependencies": {
         "normalize-path": "^3.0.0",
@@ -220,9 +220,9 @@
   },
   "dependencies": {
     "anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
       "dev": true,
       "requires": {
         "normalize-path": "^3.0.0",
diff --git a/package.json b/package.json
index 79387df..e1c96d5 100644
--- a/package.json
+++ b/package.json
@@ -2,11 +2,11 @@
   "dependencies": {
     "ws": "^7.5.3"
   },
-  "devDependencies": {
-    "chokidar": "^3.5.3"
-  },
   "scripts": {
     "start": "./start.sh",
     "stop": "./stop.sh"
+  },
+  "devDependencies": {
+    "chokidar": "^3.5.3"
   }
 }
diff --git a/start.sh b/start.sh
index 8434097..35e6b41 100755
--- a/start.sh
+++ b/start.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
 
-nodemon -i app.js -i base_rules.js ./server.js &
+nodemon ./server.js &
 echo $! > .pid
 php -S localhost:8000 &
diff --git a/variants.js b/variants.js
index db500f5..8353853 100644
--- a/variants.js
+++ b/variants.js
@@ -27,7 +27,7 @@ const variants = [
   {name: 'Capture', desc: 'Mandatory captures'},
   {name: 'Chakart', desc: 'Capture the princess'},
   {name: 'Checkered', desc: 'Shared pieces'},
-//  {name: 'Checkless', desc: 'No-check mode'},
+  {name: 'Checkless', desc: 'No-check mode'},
   {name: 'Chess960', disp: "Chess 960", desc: "Standard rules"},
 //  {name: 'Circular', desc: 'Run forward'},
 //  {name: 'Clorange', desc: 'A Clockwork Orange', disp: 'Clockwork Orange'},
diff --git a/variants/Checkless/class.js b/variants/Checkless/class.js
new file mode 100644
index 0000000..bc85184
--- /dev/null
+++ b/variants/Checkless/class.js
@@ -0,0 +1,46 @@
+import ChessRules from "/base_rules.js";
+
+export default class ChecklessRules extends ChessRules {
+
+  // Cannot use super.atLeastOneMove: lead to infinite recursion
+  atLeastOneMove_aux(kingPos, oppKingPos, color, oppCol) {
+    for (let i = 0; i < this.size.x; i++) {
+      for (let j = 0; j < this.size.y; j++) {
+        if (this.getColor(i, j) == color) {
+          const moves = this.getPotentialMovesFrom([i, j]);
+          for (let m of moves) {
+            this.playOnBoard(m);
+            const res = this.trackKingWrap(m, kingPos, (kp) => {
+              return !this.underCheck(kp, [oppCol]);
+            });
+            this.undoOnBoard(m);
+            if (res)
+              return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  filterValid(moves) {
+    fmoves = super.filterValid(moves);
+    // Filter out moves giving check but not checkmate
+    const color = this.turn;
+    const oppCol = C.GetOppTurn(color);
+    let kingPos = this.searchKingPos(color),
+        oppKingPos = this.searchKingPos(oppCol);
+    return fmoves.filter(m => {
+      this.playOnBoard(m);
+      const res = this.trackKingWrap(m, oppKingPos, (oppKp) => {
+        return (
+          !this.underCheck(oppKp, [color]) ||
+          this.atLeastOneMove_aux(oppKp, kingPos, oppCol, color)
+        );
+      });
+      this.undoOnBoard(m);
+      return res;
+    });
+  }
+
+};
diff --git a/variants/Teleport/class.js b/variants/Teleport/class.js
index 2698eaf..7ac3b34 100644
--- a/variants/Teleport/class.js
+++ b/variants/Teleport/class.js
@@ -5,8 +5,12 @@ export default class TeleportRules extends ChessRules {
   static get Options() {
     return {
       select: C.Options.select,
-      // TODO? option "teleport king"?
-      input: C.Options.input,
+      input: C.Options.input.concat({
+        label: "Teleport king",
+        variable: "tpking",
+        type: "checkbox",
+        defaut: false
+      }),
       styles: C.Options.styles.filter(s => s != "teleport")
     };
   }
@@ -16,4 +20,8 @@ export default class TeleportRules extends ChessRules {
     super(o);
   }
 
+  canSelfTake([x1, y1], [x2, y2]) {
+    return (this.options["tpking"] || !this.isKing(x2, y2));
+  }
+
 };
-- 
2.44.0