Add Atari-Go
[xogo.git] / variants / Atarigo / class.js
1 import ChessRules from "/base_rules.js";
2 import Move from "/utils/Move.js";
3 import PiPo from "/utils/PiPo.js";
4 import {ArrayFun} from "/utils/array.js";
5
6 export default class AtarigoRules extends ChessRules {
7
8 static get Options() {
9 return {
10 input: [
11 {
12 label: "Board size",
13 variable: "bsize",
14 type: "number",
15 defaut: 11
16 }
17 ]
18 };
19 }
20
21 get hasFlags() {
22 return false;
23 }
24 get hasEnpassant() {
25 return false;
26 }
27 get clickOnly() {
28 return true;
29 }
30
31 getSvgChessboard() {
32 const flipped = (this.playerColor == 'b');
33 let board = `
34 <svg
35 viewBox="0 0 ${10*(this.size.y)} ${10*(this.size.x)}"
36 class="chessboard_SVG">`;
37 for (let i=0; i < this.size.x; i++) {
38 for (let j=0; j < this.size.y; j++) {
39 const ii = (flipped ? this.size.x - 1 - i : i);
40 const jj = (flipped ? this.size.y - 1 - j : j);
41 board += `
42 <rect
43 id="${this.coordsToId({x: ii, y: jj})}"
44 width="10"
45 height="10"
46 x="${10*j}"
47 y="${10*i}"
48 fill="transparent"
49 />`;
50 }
51 }
52 // Add lines to delimitate "squares"
53 for (let i = 0; i < this.size.x; i++) {
54 const y = i * 10 + 5, maxX = this.size.y * 10 - 5;
55 board += `
56 <line x1="5" y1="${y}" x2="${maxX}" y2="${y}"
57 stroke="black" stroke-width="0.2"/>`;
58 }
59 for (let i = 0; i < this.size.x; i++) {
60 const x = i * 10 + 5, maxY = this.size.x * 10 - 5;
61 board += `
62 <line x1="${x}" y1="5" x2="${x}" y2="${maxY}"
63 stroke="black" stroke-width="0.2"/>`;
64 }
65 board += "</svg>";
66 return board;
67 }
68
69 get size() {
70 return {
71 x: this.options["bsize"],
72 y: this.options["bsize"],
73 };
74 }
75
76 genRandInitBaseFen() {
77 const fenLine = C.FenEmptySquares(this.size.y);
78 return {
79 fen: (fenLine + '/').repeat(this.size.x - 1) + fenLine + " w 0",
80 o: {}
81 };
82 }
83
84 pieces(color, x, y) {
85 return {
86 's': {
87 "class": "stone",
88 moves: []
89 }
90 };
91 }
92
93 doClick(coords) {
94 const [x, y] = [coords.x, coords.y];
95 if (this.board[x][y] != "")
96 return null;
97 const color = this.turn;
98 const oppCol = C.GetOppCol(color);
99 let move = new Move({
100 appear: [ new PiPo({ x: x, y: y, c: color, p: 's' }) ],
101 vanish: [],
102 start: {x: x, y: y}
103 });
104 this.playOnBoard(move); //put the stone
105 let noSuicide = false;
106 let captures = [];
107 for (let s of [[0, 1], [1, 0], [0, -1], [-1, 0]]) {
108 const [i, j] = [x + s[0], y + s[1]];
109 if (this.onBoard(i, j)) {
110 if (this.board[i][j] == "")
111 noSuicide = true; //clearly
112 else if (this.getColor(i, j) == color) {
113 // Free space for us = not a suicide
114 if (!noSuicide) {
115 let explored = ArrayFun.init(this.size.x, this.size.y, false);
116 noSuicide = this.searchForEmptySpace([i, j], color, explored);
117 }
118 }
119 else {
120 // Free space for opponent = not a capture
121 let explored = ArrayFun.init(this.size.x, this.size.y, false);
122 const captureSomething =
123 !this.searchForEmptySpace([i, j], oppCol, explored);
124 if (captureSomething) {
125 for (let ii = 0; ii < this.size.x; ii++) {
126 for (let jj = 0; jj < this.size.y; jj++) {
127 if (explored[ii][jj])
128 captures.push(new PiPo({ x: ii, y: jj, c: oppCol, p: 's' }));
129 }
130 }
131 }
132 }
133 }
134 }
135 this.undoOnBoard(move); //remove the stone
136 if (!noSuicide && captures.length == 0)
137 return null;
138 Array.prototype.push.apply(move.vanish, captures);
139 return move;
140 }
141
142 searchForEmptySpace([x, y], color, explored) {
143 if (explored[x][y])
144 return false; //didn't find empty space
145 explored[x][y] = true;
146 let res = false;
147 for (let s of [[1, 0], [0, 1], [-1, 0], [0, -1]]) {
148 const [i, j] = [x + s[0], y + s[1]];
149 if (this.onBoard(i, j)) {
150 if (this.board[i][j] == "")
151 res = true;
152 else if (this.getColor(i, j) == color)
153 res = this.searchForEmptySpace([i, j], color, explored) || res;
154 }
155 }
156 return res;
157 }
158
159 filterValid(moves) {
160 // Suicide check not here, because side-computation of captures
161 return moves;
162 }
163
164 getCurrentScore(move_s) {
165 if (move_s[0].vanish.length > 0)
166 return (this.turn == 'w' ? "0-1" : "1-0");
167 return "*";
168 }
169
170 };