From: Benjamin Auder Date: Tue, 19 Jan 2021 20:35:55 +0000 (+0100) Subject: Fix Atarigo + Gomoku, prepare Emergo X-Git-Url: https://git.auder.net/js/%7B%7B%20asset%28%27mixstore/css/static/about.css%27%29%20%7D%7D?a=commitdiff_plain;h=e07981727a6d486886f01f8e0ddc548fd733764b;p=vchess.git Fix Atarigo + Gomoku, prepare Emergo --- diff --git a/client/README.md b/client/README.md index 6c657db5..2c1caabd 100644 --- a/client/README.md +++ b/client/README.md @@ -6,6 +6,9 @@ Rename and edit src/parameters.js.dist into parameters.js. Then, download the folders and files listed in download\_objects file. (This is optional, most variants will work without it). +For Emergo and Avalam, run generateSVG\*.py scripts in folder +public/images/pieces/Emergo and Avalam (pieces SVGs). + Finally install dependencies. ``` npm install diff --git a/client/public/images/pieces/Emergo/.gitignore b/client/public/images/pieces/Emergo/.gitignore new file mode 100644 index 00000000..5e3b89fc --- /dev/null +++ b/client/public/images/pieces/Emergo/.gitignore @@ -0,0 +1,3 @@ +* +!generate*.py +!.gitignore diff --git a/client/public/images/pieces/Emergo/generateSVG_composite.py b/client/public/images/pieces/Emergo/generateSVG_composite.py new file mode 100755 index 00000000..87eb55f6 --- /dev/null +++ b/client/public/images/pieces/Emergo/generateSVG_composite.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +# Compose each piece SVG with numbers +# https://travishorn.com/removing-parts-of-shapes-in-svg-b539a89e5649 +# https://developer.mozilla.org/fr/docs/Web/SVG/Tutoriel/Paths + +preamble = """ + + + + + + + +""" + +black_left = '' +white_right = '' +white_left = '' +black_right = '' + +digits = { + "left": [ + # 1 + '', + # 2 + '', + # 3 + '', + # 4 + '', + # 5 + '', + # 6 + '', + # 7 + '', + # 8 + '', + # 9 + '', + # 10 + '', + # 11 + '', + # 12 + '' + ], + "right": [ + # 1 + '', + # 2 + '', + # 3 + '', + # 4 + '', + # 5 + '', + # 6 + '', + # 7 + '', + # 8 + '', + # 9 + '', + # 10 + '', + # 11 + '', + # 12 + '' + ] +} + +final = "" + +for colorLeft in ["white", "black"]: + chrShift = 0 if colorLeft == "white" else 32 + for left in range(12): + for right in range(12): + filename = chr(65 + left + chrShift) + chr(65 + right + chrShift) + ".svg" + f = open(filename, "w") + f.write(preamble) + f.write("\n"); + f.write(black_right if colorLeft == "white" else white_right) + f.write("\n"); + f.write(white_left if colorLeft == "white" else black_left) + f.write("\n"); + f.write(digits["left"][left]) + f.write("\n"); + f.write(digits["right"][right]) + f.write("\n"); + f.write(final) + f.close() diff --git a/client/public/images/pieces/Emergo/generateSVG_simple.py b/client/public/images/pieces/Emergo/generateSVG_simple.py new file mode 100755 index 00000000..1437626c --- /dev/null +++ b/client/public/images/pieces/Emergo/generateSVG_simple.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +# Compose each piece SVG with numbers +# https://travishorn.com/removing-parts-of-shapes-in-svg-b539a89e5649 +# https://developer.mozilla.org/fr/docs/Web/SVG/Tutoriel/Paths + +preamble = """ + +""" + +black = '' +white = '' + +digits = [ + # 1 + '', + # 2 + '', + # 3 + '', + # 4 + '', + # 5 + '', + # 6 + '', + # 7 + '', + # 8 + '', + # 9 + '', + # 10 + '', + # 11 + '', + # 12 + '' +] + +final = "" + +for color in ["white", "black"]: + chrShift = 0 if color == "white" else 32 + for number in range(12): + filename = chr(65 + number + chrShift) + "_.svg" + f = open(filename, "w") + f.write(preamble) + f.write("\n"); + f.write(white if color == "white" else black) + f.write("\n"); + f.write(digits[number]) + f.write("\n"); + f.write(final) + f.close() diff --git a/client/src/variants/Atarigo.js b/client/src/variants/Atarigo.js index 2e50922d..f2c84a89 100644 --- a/client/src/variants/Atarigo.js +++ b/client/src/variants/Atarigo.js @@ -182,7 +182,7 @@ export class AtarigoRules extends ChessRules { for (let i = 0; i < V.size.x; i++) { for (let j=0; j < V.size.y; j++) { if (this.board[i][j] == V.EMPTY) { - const mv = this.doClick(i, j); + const mv = this.doClick([i, j]); if (!!mv) moves.push(mv); } } @@ -211,10 +211,73 @@ export class AtarigoRules extends ChessRules { return "*"; } + // Modified version to count liberties + find locations + countEmptySpaces([x, y], color, explored) { + if (explored[x][y]) return []; + explored[x][y] = true; + let res = []; + for (let s of V.steps[V.ROOK]) { + const [i, j] = [x + s[0], y + s[1]]; + if (V.OnBoard(i, j)) { + if (!explored[i][j] && this.board[i][j] == V.EMPTY) { + res.push([i, j]); + explored[i][j] = true; //not counting liberties twice! + } + else if (this.getColor(i, j) == color) + res = res.concat(this.countEmptySpaces([i, j], color, explored)); + } + } + return res; + } + getComputerMove() { const moves = super.getAllValidMoves(); if (moves.length == 0) return null; - // Just random mover for now... writing a good bot is far out of scope + // Any capture? + const captures = moves.filter(m => m.vanish.length >= 1); + if (captures.length > 0) return captures[randInt(captures.length)]; + // Any group in immediate danger? + const color = this.turn; + let explored = ArrayFun.init(V.size.x, V.size.y, false); + for (let i = 0; i < V.size.x; i++) { + for (let j = 0; j < V.size.y; j++) { + if ( + this.board[i][j] != V.EMPTY && + this.getColor(i, j) == color && + !explored[i][j] + ) { + // Before this search, reset liberties, + // because two groups might share them. + for (let ii = 0; ii < V.size.x; ii++) { + for (let jj = 0; jj < V.size.y; jj++) { + if (explored[ii][jj] && this.board[ii][jj] == V.EMPTY) + explored[ii][jj] = false; + } + } + const liberties = this.countEmptySpaces([i, j], color, explored); + if (liberties.length == 1) { + const L = liberties[0]; + const toPlay = moves.find(m => m.end.x == L[0] && m.end.y == L[1]); + if (!!toPlay) return toPlay; + } + } + } + } + // At this point, pick a random move not far from current stones (TODO) + const candidates = moves.filter(m => { + const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); + return ( + steps.some(s => { + const [i, j] = [m.end.x + s[0], m.end.y + s[1]]; + return ( + V.OnBoard(i, j) && + this.board[i][j] != V.EMPTY && + this.getColor(i, j) == color + ); + }) + ); + }); + if (candidates.length > 0) return candidates[randInt(candidates.length)]; return moves[randInt(moves.length)]; } diff --git a/client/src/variants/Gomoku.js b/client/src/variants/Gomoku.js index f0032945..51d0bed5 100644 --- a/client/src/variants/Gomoku.js +++ b/client/src/variants/Gomoku.js @@ -103,7 +103,7 @@ export class GomokuRules extends ChessRules { let moves = []; for (let i = 0; i < 19; i++) { for (let j=0; j < 19; j++) { - if (this.board[i][j] == V.EMPTY) moves.push(this.doClick(i, j)); + if (this.board[i][j] == V.EMPTY) moves.push(this.doClick([i, j])); } } return moves;