X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FAtarigo.js;fp=client%2Fsrc%2Fvariants%2FAtarigo.js;h=2e50922d0fdf67de031b3ce5782840ae1a3a03fa;hb=7c05a5f2297bea540c700ebceb0cc8b03a7f6775;hp=0000000000000000000000000000000000000000;hpb=cdab566355412821c9187078ee0864ceb30545de;p=vchess.git diff --git a/client/src/variants/Atarigo.js b/client/src/variants/Atarigo.js new file mode 100644 index 00000000..2e50922d --- /dev/null +++ b/client/src/variants/Atarigo.js @@ -0,0 +1,225 @@ +import { ChessRules, Move, PiPo } from "@/base_rules"; +import { randInt } from "@/utils/alea"; +import { ArrayFun } from "@/utils/array"; + +export class AtarigoRules extends ChessRules { + + static get Monochrome() { + return true; + } + + static get Notoodark() { + return true; + } + + static get Lines() { + let lines = []; + // Draw all inter-squares lines, shifted: + for (let i = 0; i < V.size.x; i++) + lines.push([[i+0.5, 0.5], [i+0.5, V.size.y-0.5]]); + for (let j = 0; j < V.size.y; j++) + lines.push([[0.5, j+0.5], [V.size.x-0.5, j+0.5]]); + return lines; + } + + static get HasFlags() { + return false; + } + + static get HasEnpassant() { + return false; + } + + static get ReverseColors() { + return true; + } + + static IsGoodPosition(position) { + if (position.length == 0) return false; + const rows = position.split("/"); + if (rows.length != V.size.x) return false; + for (let row of rows) { + let sumElts = 0; + for (let i = 0; i < row.length; i++) { + if (row[i].toLowerCase() == V.PAWN) sumElts++; + else { + const num = parseInt(row[i], 10); + if (isNaN(num) || num <= 0) return false; + sumElts += num; + } + } + if (sumElts != V.size.y) return false; + } + return true; + } + + static IsGoodFen(fen) { + if (!ChessRules.IsGoodFen(fen)) return false; + const fenParsed = V.ParseFen(fen); + // 3) Check capture "flag" + if (!fenParsed.capture || !fenParsed.capture.match(/^[01]$/)) + return false; + return true; + } + + static ParseFen(fen) { + const fenParts = fen.split(" "); + return Object.assign( + ChessRules.ParseFen(fen), + // Capture field allows to compute the score cleanly. + { capture: fenParts[3] } + ); + } + + static get size() { + return { x: 12, y: 12 }; + } + + static GenRandInitFen() { + return "93/93/93/93/93/5Pp5/5pP5/93/93/93/93/93 w 0 0"; + } + + getFen() { + return super.getFen() + " " + (this.capture ? 1 : 0); + } + + setOtherVariables(fen) { + this.capture = parseInt(V.ParseFen(fen).capture, 10); + } + + getPiece() { + return V.PAWN; + } + + getPpath(b) { + return "Gomoku/" + b; + } + + onlyClick() { + return true; + } + + canIplay(side, [x, y]) { + return (side == this.turn && this.board[x][y] == V.EMPTY); + } + + hoverHighlight([x, y], side) { + if (!!side && side != this.turn) return false; + return (this.board[x][y] == V.EMPTY); + } + + 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 V.steps[V.ROOK]) { + const [i, j] = [x + s[0], y + s[1]]; + if (V.OnBoard(i, j)) { + if (this.board[i][j] == V.EMPTY) res = true; + else if (this.getColor(i, j) == color) + res = this.searchForEmptySpace([i, j], color, explored) || res; + } + } + return res; + } + + doClick([x, y]) { + const color = this.turn; + const oppCol = V.GetOppCol(color); + let move = new Move({ + appear: [ + new PiPo({ x: x, y: y, c: color, p: V.PAWN }) + ], + vanish: [], + start: { x: -1, y: -1 } + }); + V.PlayOnBoard(this.board, move); //put the stone + let noSuicide = false; + let captures = []; + for (let s of V.steps[V.ROOK]) { + const [i, j] = [x + s[0], y + s[1]]; + if (V.OnBoard(i, j)) { + if (this.board[i][j] == V.EMPTY) noSuicide = true; //clearly + else if (this.getColor(i, j) == color) { + // Free space for us = not a suicide + if (!noSuicide) { + let explored = ArrayFun.init(V.size.x, V.size.y, false); + noSuicide = this.searchForEmptySpace([i, j], color, explored); + } + } + else { + // Free space for opponent = not a capture + let explored = ArrayFun.init(V.size.x, V.size.y, false); + const captureSomething = + !this.searchForEmptySpace([i, j], oppCol, explored); + if (captureSomething) { + for (let ii = 0; ii < V.size.x; ii++) { + for (let jj = 0; jj < V.size.y; jj++) { + if (explored[ii][jj]) { + captures.push( + new PiPo({ x: ii, y: jj, c: oppCol, p: V.PAWN }) + ); + } + } + } + } + } + } + } + V.UndoOnBoard(this.board, move); //remove the stone + if (!noSuicide && captures.length == 0) return null; + Array.prototype.push.apply(move.vanish, captures); + return move; + } + + getPotentialMovesFrom([x, y]) { + const move = this.doClick([x, y]); + return (!move ? [] : [move]); + } + + getAllPotentialMoves() { + let moves = []; + 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); + if (!!mv) moves.push(mv); + } + } + } + return moves; + } + + filterValid(moves) { + return moves; + } + + getCheckSquares() { + return []; + } + + postPlay(move) { + if (move.vanish.length >= 1) this.capture = true; + } + + postUndo() { + this.capture = false; + } + + getCurrentScore() { + if (this.capture) return (this.turn == 'w' ? "0-1" : "1-0"); + return "*"; + } + + 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 + return moves[randInt(moves.length)]; + } + + getNotation(move) { + return V.CoordsToSquare(move.end); + } + +};