From eceb02f712e49d6c8b2fa90691c93161ca015248 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Mon, 4 Jul 2022 17:21:11 +0200 Subject: [PATCH] Add Atari-Go --- app.js | 6 +- pieces/Atarigo/CREDITS | 2 + pieces/Atarigo/black_stone.svg | 9 ++ pieces/Atarigo/white_stone.svg | 9 ++ variants.js | 2 +- variants/Align4/class.js | 4 +- variants/Atarigo/class.js | 170 +++++++++++++++++++++++++++++++++ variants/Atarigo/rules.html | 1 + variants/Atarigo/style.css | 11 +++ variants/Hex/class.js | 2 +- 10 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 pieces/Atarigo/CREDITS create mode 100644 pieces/Atarigo/black_stone.svg create mode 100644 pieces/Atarigo/white_stone.svg create mode 100644 variants/Atarigo/class.js create mode 100644 variants/Atarigo/rules.html create mode 100644 variants/Atarigo/style.css diff --git a/app.js b/app.js index 1090b30..12c2b7a 100644 --- 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 index 0000000..27e2cb1 --- /dev/null +++ b/pieces/Atarigo/CREDITS @@ -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 index 0000000..21bf0c5 --- /dev/null +++ b/pieces/Atarigo/black_stone.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pieces/Atarigo/white_stone.svg b/pieces/Atarigo/white_stone.svg new file mode 100644 index 0000000..21ce921 --- /dev/null +++ b/pieces/Atarigo/white_stone.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/variants.js b/variants.js index 1c4d4cb..b3cf120 100644 --- a/variants.js +++ b/variants.js @@ -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'}, diff --git a/variants/Align4/class.js b/variants/Align4/class.js index 4c191db..ce5700d 100644 --- a/variants/Align4/class.js +++ b/variants/Align4/class.js @@ -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 index 0000000..5337f71 --- /dev/null +++ b/variants/Atarigo/class.js @@ -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 = ` + `; + 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 += ` + `; + } + } + // 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 += ` + `; + } + for (let i = 0; i < this.size.x; i++) { + const x = i * 10 + 5, maxY = this.size.x * 10 - 5; + board += ` + `; + } + board += ""; + 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 index 0000000..c65158e --- /dev/null +++ b/variants/Atarigo/rules.html @@ -0,0 +1 @@ +

TODO

diff --git a/variants/Atarigo/style.css b/variants/Atarigo/style.css new file mode 100644 index 0000000..6ce83d8 --- /dev/null +++ b/variants/Atarigo/style.css @@ -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'); +} diff --git a/variants/Hex/class.js b/variants/Hex/class.js index 1919bc8..b82af41 100644 --- a/variants/Hex/class.js +++ b/variants/Hex/class.js @@ -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; -- 2.44.0