Fix Weiqi
[xogo.git] / variants / Weiqi / class.js
index e528719..714ada8 100644 (file)
@@ -1,8 +1,3 @@
-//TODO:
-// - pass btn on top + message if opponent just passed
-// - do not count points: rely on players' ability to do that
-// - implement Ko rule (need something in fen: lastMove)
-
 import ChessRules from "/base_rules.js";
 import Move from "/utils/Move.js";
 import PiPo from "/utils/PiPo.js";
@@ -10,7 +5,6 @@ import {ArrayFun} from "/utils/array.js";
 
 export default class WeiqiRules extends ChessRules {
 
-  // TODO: option oneColor (just alter pieces class of white stones)
   static get Options() {
     return {
       input: [
@@ -19,6 +13,12 @@ export default class WeiqiRules extends ChessRules {
           variable: "bsize",
           type: "number",
           defaut: 9
+        },
+        {
+          label: "One color",
+          variable: "onecolor",
+          type: "checkbox",
+          defaut: false
         }
       ]
     };
@@ -82,15 +82,51 @@ export default class WeiqiRules extends ChessRules {
   genRandInitBaseFen() {
     const fenLine = C.FenEmptySquares(this.size.y);
     return {
-      fen: (fenLine + '/').repeat(this.size.x - 1) + fenLine + " w 0",
+      fen: (fenLine + '/').repeat(this.size.x - 1) + fenLine,
       o: {}
     };
   }
 
+  constructor(o) {
+    super(o);
+    if (!o.genFenOnly && !o.diagram) {
+      this.passListener = () => {
+        if (this.turn == this.playerColor) {
+          let mv = {
+            // Need artificial start/end for animate (TODO?)
+            start: {x: -1, y: -1},
+            end: {x: -1, y: -1},
+            appear: [],
+            vanish: [],
+            pass: true
+          };
+          this.buildMoveStack(mv);
+        }
+      };
+      // Show pass btn
+      let passBtn = document.createElement("button");
+      C.AddClass_es(passBtn, "pass-btn");
+      passBtn.innerHTML = "pass";
+      passBtn.addEventListener("click", this.passListener);
+      let container = document.getElementById(this.containerId);
+      container.appendChild(passBtn);
+    }
+  }
+
+  removeListeners() {
+    super.removeListeners();
+    let passBtn_arr = document.getElementsByClassName("pass-btn");
+    if (passBtn_arr.length >= 1)
+      passBtn_arr[0].removeEventListener("click", this.passListener);
+  }
+
   pieces(color, x, y) {
+    let classe_s = ["stone"];
+    if (this.options["onecolor"] && color == 'w')
+      classe_s.push("one-color");
     return {
       's': {
-        "class": "stone",
+        "class": classe_s,
         moves: []
       }
     };
@@ -98,40 +134,35 @@ export default class WeiqiRules extends ChessRules {
 
   doClick(coords) {
     const [x, y] = [coords.x, coords.y];
-    if (this.board[x][y] != "")
+    if (this.board[x][y] != "" || this.turn != this.playerColor)
       return null;
     const color = this.turn;
     const oppCol = C.GetOppCol(color);
     let move = new Move({
-      appear: [ new PiPo({ x: x, y: y, c: color, p: 's' }) ],
+      appear: [new PiPo({x: x, y: y, c: color, p: 's'})],
       vanish: [],
       start: {x: x, y: y}
     });
     this.playOnBoard(move); //put the stone
-    let noSuicide = false;
     let captures = [];
+    let inCaptures = {};
+    let explored = ArrayFun.init(this.size.x, this.size.y, false);
+    const suicide = !this.searchForEmptySpace([x, y], color, explored);
     for (let s of [[0, 1], [1, 0], [0, -1], [-1, 0]]) {
       const [i, j] = [x + s[0], y + s[1]];
-      if (this.onBoard(i, j)) {
-        if (this.board[i][j] == "")
-          noSuicide = true; //clearly
-        else if (this.getColor(i, j) == color) {
-          // Free space for us = not a suicide
-          if (!noSuicide) {
-            let explored = ArrayFun.init(this.size.x, this.size.y, false);
-            noSuicide = this.searchForEmptySpace([i, j], color, explored);
-          }
-        }
-        else {
-          // Free space for opponent = not a capture
-          let explored = ArrayFun.init(this.size.x, this.size.y, false);
+      if (this.onBoard(i, j) && !inCaptures[i + "." + j]) {
+        if (this.getColor(i, j) == oppCol) {
+          // Free space for opponent => not a capture
+          let oppExplored = ArrayFun.init(this.size.x, this.size.y, false);
           const captureSomething =
-            !this.searchForEmptySpace([i, j], oppCol, explored);
+            !this.searchForEmptySpace([i, j], oppCol, oppExplored);
           if (captureSomething) {
             for (let ii = 0; ii < this.size.x; ii++) {
               for (let jj = 0; jj < this.size.y; jj++) {
-                if (explored[ii][jj])
-                  captures.push(new PiPo({ x: ii, y: jj, c: oppCol, p: 's' }));
+                if (oppExplored[ii][jj]) {
+                  captures.push(new PiPo({x: ii, y: jj, c: oppCol, p: 's'}));
+                  inCaptures[ii + "." + jj] = true;
+                }
               }
             }
           }
@@ -139,27 +170,41 @@ export default class WeiqiRules extends ChessRules {
       }
     }
     this.undoOnBoard(move); //remove the stone
-    if (!noSuicide && captures.length == 0)
+    if (suicide && captures.length == 0)
       return null;
     Array.prototype.push.apply(move.vanish, captures);
     return move;
   }
 
   searchForEmptySpace([x, y], color, explored) {
-    if (explored[x][y])
-      return false; //didn't find empty space
     explored[x][y] = true;
-    let res = false;
     for (let s of [[1, 0], [0, 1], [-1, 0], [0, -1]]) {
       const [i, j] = [x + s[0], y + s[1]];
-      if (this.onBoard(i, j)) {
-        if (this.board[i][j] == "")
-          res = true;
-        else if (this.getColor(i, j) == color)
-          res = this.searchForEmptySpace([i, j], color, explored) || res;
+      if (
+        this.onBoard(i, j) &&
+        !explored[i][j] &&
+        (
+          this.board[i][j] == "" ||
+          (
+            this.getColor(i, j) == color &&
+            this.searchForEmptySpace([i, j], color, explored)
+          )
+        )
+      ) {
+        return true;
       }
     }
-    return res;
+    return false;
+  }
+
+  play(move) {
+    if (move.pass) {
+      if (this.turn != this.playerColor)
+        super.displayMessage(null, "pass", "pass-text", 2000);
+      this.turn = C.GetOppCol(this.turn);
+    }
+    else
+      super.play(move);
   }
 
   filterValid(moves) {