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