Add Atari-Go
authorBenjamin Auder <benjamin.auder@somewhere>
Mon, 4 Jul 2022 15:21:11 +0000 (17:21 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Mon, 4 Jul 2022 15:21:11 +0000 (17:21 +0200)
app.js
pieces/Atarigo/CREDITS [new file with mode: 0644]
pieces/Atarigo/black_stone.svg [new file with mode: 0644]
pieces/Atarigo/white_stone.svg [new file with mode: 0644]
variants.js
variants/Align4/class.js
variants/Atarigo/class.js [new file with mode: 0644]
variants/Atarigo/rules.html [new file with mode: 0644]
variants/Atarigo/style.css [new file with mode: 0644]
variants/Hex/class.js

diff --git a/app.js b/app.js
index 1090b30..12c2b7a 100644 (file)
--- a/app.js
+++ b/app.js
@@ -373,7 +373,7 @@ const messageCenter = (msg) => {
       if (document.hidden)
         notifyMe("move");
       vr.playReceivedMove(obj.moves, () => {
-        if (vr.getCurrentScore(obj.moves[obj.moves.length-1]) != "*") {
+        if (vr.getCurrentScore(obj.moves) != "*") {
           localStorage.removeItem("gid");
           setTimeout( () => toggleVisible("gameStopped"), 2000 );
         }
@@ -470,7 +470,6 @@ const afterPlay = (move_s, newTurn, ops) => {
            {gid: gid, moves: curMoves, fen: vr.getFen()},
            {
              retry: true,
-             success: () => curMoves = [],
              error: () => alert("Move not sent: reload page")
            }
       );
@@ -478,7 +477,8 @@ const afterPlay = (move_s, newTurn, ops) => {
   }
   if (ops.res && newTurn != playerColor) {
     toggleTurnIndicator(false); //now all moves are sent and animated
-    const result = vr.getCurrentScore(move_s);
+    const result = vr.getCurrentScore(curMoves);
+    curMoves = [];
     if (result != "*") {
       setTimeout(() => {
         toggleVisible("gameStopped");
diff --git a/pieces/Atarigo/CREDITS b/pieces/Atarigo/CREDITS
new file mode 100644 (file)
index 0000000..27e2cb1
--- /dev/null
@@ -0,0 +1,2 @@
+https://commons.wikimedia.org/wiki/File:Go_b.svg?uselang=fr
+https://commons.wikimedia.org/wiki/File:Go_w.svg?uselang=fr
diff --git a/pieces/Atarigo/black_stone.svg b/pieces/Atarigo/black_stone.svg
new file mode 100644 (file)
index 0000000..21bf0c5
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<defs><radialGradient id="rg" cx=".3" cy=".3" r=".8">
+<stop offset="0" stop-color="#777"/>
+<stop offset=".3" stop-color="#222"/>
+<stop offset="1" stop-color="#000"/>
+</radialGradient></defs>
+<circle cx="250" cy="250" r="245" fill="url(#rg)"/>
+</svg>
diff --git a/pieces/Atarigo/white_stone.svg b/pieces/Atarigo/white_stone.svg
new file mode 100644 (file)
index 0000000..21ce921
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<defs><radialGradient id="rg" cx=".47" cy=".49" r=".48">
+<stop offset=".7" stop-color="#FFF"/>
+<stop offset=".9" stop-color="#DDD"/>
+<stop offset="1" stop-color="#777"/>
+</radialGradient></defs>
+<circle cx="250" cy="250" r="245" fill="url(#rg)"/>
+</svg>
index 1c4d4cb..b3cf120 100644 (file)
@@ -10,7 +10,7 @@ const variants = [
   {name: 'Antimatter', desc: 'Dangerous collisions'},
   {name: 'Apocalypse', desc: 'The end of the world'},
   {name: 'Arena', desc: 'Middle battle'},
-//  {name: 'Atarigo', desc: 'First capture wins', disp: 'Atari-Go'},
+  {name: 'Atarigo', desc: 'First capture wins', disp: 'Atari-Go'},
   {name: 'Atomic', desc: 'Explosive captures'},
 //  {name: 'Avalam', desc: 'Build towers'},
 //  {name: 'Avalanche', desc: 'Pawnfalls'},
index 4c191db..ce5700d 100644 (file)
@@ -42,8 +42,8 @@ export default class Align4Rules extends ChessRules {
   // Just do not update any reserve (infinite supply)
   updateReserve() {}
 
-  getCurrentScore(move) {
-    const score = super.getCurrentScore(move);
+  getCurrentScore(move_s) {
+    const score = super.getCurrentScore(move_s);
     if (score != "*")
       return score;
     // Check pawns connection:
diff --git a/variants/Atarigo/class.js b/variants/Atarigo/class.js
new file mode 100644 (file)
index 0000000..5337f71
--- /dev/null
@@ -0,0 +1,170 @@
+import ChessRules from "/base_rules.js";
+import Move from "/utils/Move.js";
+import PiPo from "/utils/PiPo.js";
+import {ArrayFun} from "/utils/array.js";
+
+export default class AtarigoRules extends ChessRules {
+
+  static get Options() {
+    return {
+      input: [
+        {
+          label: "Board size",
+          variable: "bsize",
+          type: "number",
+          defaut: 11
+        }
+      ]
+    };
+  }
+
+  get hasFlags() {
+    return false;
+  }
+  get hasEnpassant() {
+    return false;
+  }
+  get clickOnly() {
+    return true;
+  }
+
+  getSvgChessboard() {
+    const flipped = (this.playerColor == 'b');
+    let board = `
+      <svg
+        viewBox="0 0 ${10*(this.size.y)} ${10*(this.size.x)}"
+        class="chessboard_SVG">`;
+    for (let i=0; i < this.size.x; i++) {
+      for (let j=0; j < this.size.y; j++) {
+        const ii = (flipped ? this.size.x - 1 - i : i);
+        const jj = (flipped ? this.size.y - 1 - j : j);
+        board += `
+          <rect
+            id="${this.coordsToId({x: ii, y: jj})}"
+            width="10"
+            height="10"
+            x="${10*j}"
+            y="${10*i}"
+            fill="transparent"
+          />`;
+      }
+    }
+    // Add lines to delimitate "squares"
+    for (let i = 0; i < this.size.x; i++) {
+      const y = i * 10 + 5, maxX = this.size.y * 10 - 5;
+      board += `
+        <line x1="5" y1="${y}" x2="${maxX}" y2="${y}"
+              stroke="black" stroke-width="0.2"/>`;
+    }
+    for (let i = 0; i < this.size.x; i++) {
+      const x = i * 10 + 5, maxY = this.size.x * 10 - 5;
+      board += `
+        <line x1="${x}" y1="5" x2="${x}" y2="${maxY}"
+              stroke="black" stroke-width="0.2"/>`;
+    }
+    board += "</svg>";
+    return board;
+  }
+
+  get size() {
+    return {
+      x: this.options["bsize"],
+      y: this.options["bsize"],
+    };
+  }
+
+  genRandInitBaseFen() {
+    const fenLine = C.FenEmptySquares(this.size.y);
+    return {
+      fen: (fenLine + '/').repeat(this.size.x - 1) + fenLine + " w 0",
+      o: {}
+    };
+  }
+
+  pieces(color, x, y) {
+    return {
+      's': {
+        "class": "stone",
+        moves: []
+      }
+    };
+  }
+
+  doClick(coords) {
+    const [x, y] = [coords.x, coords.y];
+    if (this.board[x][y] != "")
+      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' }) ],
+      vanish: [],
+      start: {x: x, y: y}
+    });
+    this.playOnBoard(move); //put the stone
+    let noSuicide = false;
+    let captures = [];
+    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);
+          const captureSomething =
+            !this.searchForEmptySpace([i, j], oppCol, explored);
+          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' }));
+              }
+            }
+          }
+        }
+      }
+    }
+    this.undoOnBoard(move); //remove the stone
+    if (!noSuicide && 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;
+      }
+    }
+    return res;
+  }
+
+  filterValid(moves) {
+    // Suicide check not here, because side-computation of captures
+    return moves;
+  }
+
+  getCurrentScore(move_s) {
+    if (move_s[0].vanish.length > 0)
+      return (this.turn == 'w' ? "0-1" : "1-0");
+    return "*";
+  }
+
+};
diff --git a/variants/Atarigo/rules.html b/variants/Atarigo/rules.html
new file mode 100644 (file)
index 0000000..c65158e
--- /dev/null
@@ -0,0 +1 @@
+<p>TODO</p>
diff --git a/variants/Atarigo/style.css b/variants/Atarigo/style.css
new file mode 100644 (file)
index 0000000..6ce83d8
--- /dev/null
@@ -0,0 +1,11 @@
+.chessboard_SVG {
+  background-color: #BA8C63;
+}
+
+piece.white.stone {
+  background-image: url('/pieces/Atarigo/black_stone.svg');
+}
+
+piece.black.stone {
+  background-image: url('/pieces/Atarigo/white_stone.svg');
+}
index 1919bc8..b82af41 100644 (file)
@@ -171,7 +171,7 @@ export default class HexRules extends AbstractClickFillRules {
     this.turn = C.GetOppCol(this.turn);
   }
 
-  getCurrentScore(move) {
+  getCurrentScore() {
     const oppCol = C.GetOppCol(this.turn);
     // Search for connecting path of opp color:
     let explored = {}, component;