Commit | Line | Data |
---|---|---|
7c05a5f2 BA |
1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
2 | import { randInt } from "@/utils/alea"; | |
3 | import { ArrayFun } from "@/utils/array"; | |
4 | ||
5 | export class AtarigoRules extends ChessRules { | |
6 | ||
7 | static get Monochrome() { | |
8 | return true; | |
9 | } | |
10 | ||
11 | static get Notoodark() { | |
12 | return true; | |
13 | } | |
14 | ||
15 | static get Lines() { | |
16 | let lines = []; | |
17 | // Draw all inter-squares lines, shifted: | |
18 | for (let i = 0; i < V.size.x; i++) | |
19 | lines.push([[i+0.5, 0.5], [i+0.5, V.size.y-0.5]]); | |
20 | for (let j = 0; j < V.size.y; j++) | |
21 | lines.push([[0.5, j+0.5], [V.size.x-0.5, j+0.5]]); | |
22 | return lines; | |
23 | } | |
24 | ||
25 | static get HasFlags() { | |
26 | return false; | |
27 | } | |
28 | ||
29 | static get HasEnpassant() { | |
30 | return false; | |
31 | } | |
32 | ||
33 | static get ReverseColors() { | |
34 | return true; | |
35 | } | |
36 | ||
37 | static IsGoodPosition(position) { | |
38 | if (position.length == 0) return false; | |
39 | const rows = position.split("/"); | |
40 | if (rows.length != V.size.x) return false; | |
41 | for (let row of rows) { | |
42 | let sumElts = 0; | |
43 | for (let i = 0; i < row.length; i++) { | |
44 | if (row[i].toLowerCase() == V.PAWN) sumElts++; | |
45 | else { | |
46 | const num = parseInt(row[i], 10); | |
47 | if (isNaN(num) || num <= 0) return false; | |
48 | sumElts += num; | |
49 | } | |
50 | } | |
51 | if (sumElts != V.size.y) return false; | |
52 | } | |
53 | return true; | |
54 | } | |
55 | ||
56 | static IsGoodFen(fen) { | |
57 | if (!ChessRules.IsGoodFen(fen)) return false; | |
58 | const fenParsed = V.ParseFen(fen); | |
59 | // 3) Check capture "flag" | |
60 | if (!fenParsed.capture || !fenParsed.capture.match(/^[01]$/)) | |
61 | return false; | |
62 | return true; | |
63 | } | |
64 | ||
65 | static ParseFen(fen) { | |
66 | const fenParts = fen.split(" "); | |
67 | return Object.assign( | |
68 | ChessRules.ParseFen(fen), | |
69 | // Capture field allows to compute the score cleanly. | |
70 | { capture: fenParts[3] } | |
71 | ); | |
72 | } | |
73 | ||
74 | static get size() { | |
75 | return { x: 12, y: 12 }; | |
76 | } | |
77 | ||
78 | static GenRandInitFen() { | |
79 | return "93/93/93/93/93/5Pp5/5pP5/93/93/93/93/93 w 0 0"; | |
80 | } | |
81 | ||
82 | getFen() { | |
83 | return super.getFen() + " " + (this.capture ? 1 : 0); | |
84 | } | |
85 | ||
86 | setOtherVariables(fen) { | |
87 | this.capture = parseInt(V.ParseFen(fen).capture, 10); | |
88 | } | |
89 | ||
90 | getPiece() { | |
91 | return V.PAWN; | |
92 | } | |
93 | ||
94 | getPpath(b) { | |
95 | return "Gomoku/" + b; | |
96 | } | |
97 | ||
98 | onlyClick() { | |
99 | return true; | |
100 | } | |
101 | ||
102 | canIplay(side, [x, y]) { | |
103 | return (side == this.turn && this.board[x][y] == V.EMPTY); | |
104 | } | |
105 | ||
106 | hoverHighlight([x, y], side) { | |
107 | if (!!side && side != this.turn) return false; | |
108 | return (this.board[x][y] == V.EMPTY); | |
109 | } | |
110 | ||
111 | searchForEmptySpace([x, y], color, explored) { | |
112 | if (explored[x][y]) return false; //didn't find empty space | |
113 | explored[x][y] = true; | |
114 | let res = false; | |
115 | for (let s of V.steps[V.ROOK]) { | |
116 | const [i, j] = [x + s[0], y + s[1]]; | |
117 | if (V.OnBoard(i, j)) { | |
118 | if (this.board[i][j] == V.EMPTY) res = true; | |
119 | else if (this.getColor(i, j) == color) | |
120 | res = this.searchForEmptySpace([i, j], color, explored) || res; | |
121 | } | |
122 | } | |
123 | return res; | |
124 | } | |
125 | ||
126 | doClick([x, y]) { | |
127 | const color = this.turn; | |
128 | const oppCol = V.GetOppCol(color); | |
129 | let move = new Move({ | |
130 | appear: [ | |
131 | new PiPo({ x: x, y: y, c: color, p: V.PAWN }) | |
132 | ], | |
133 | vanish: [], | |
134 | start: { x: -1, y: -1 } | |
135 | }); | |
136 | V.PlayOnBoard(this.board, move); //put the stone | |
137 | let noSuicide = false; | |
138 | let captures = []; | |
139 | for (let s of V.steps[V.ROOK]) { | |
140 | const [i, j] = [x + s[0], y + s[1]]; | |
141 | if (V.OnBoard(i, j)) { | |
142 | if (this.board[i][j] == V.EMPTY) noSuicide = true; //clearly | |
143 | else if (this.getColor(i, j) == color) { | |
144 | // Free space for us = not a suicide | |
145 | if (!noSuicide) { | |
146 | let explored = ArrayFun.init(V.size.x, V.size.y, false); | |
147 | noSuicide = this.searchForEmptySpace([i, j], color, explored); | |
148 | } | |
149 | } | |
150 | else { | |
151 | // Free space for opponent = not a capture | |
152 | let explored = ArrayFun.init(V.size.x, V.size.y, false); | |
153 | const captureSomething = | |
154 | !this.searchForEmptySpace([i, j], oppCol, explored); | |
155 | if (captureSomething) { | |
156 | for (let ii = 0; ii < V.size.x; ii++) { | |
157 | for (let jj = 0; jj < V.size.y; jj++) { | |
158 | if (explored[ii][jj]) { | |
159 | captures.push( | |
160 | new PiPo({ x: ii, y: jj, c: oppCol, p: V.PAWN }) | |
161 | ); | |
162 | } | |
163 | } | |
164 | } | |
165 | } | |
166 | } | |
167 | } | |
168 | } | |
169 | V.UndoOnBoard(this.board, move); //remove the stone | |
170 | if (!noSuicide && captures.length == 0) return null; | |
171 | Array.prototype.push.apply(move.vanish, captures); | |
172 | return move; | |
173 | } | |
174 | ||
175 | getPotentialMovesFrom([x, y]) { | |
176 | const move = this.doClick([x, y]); | |
177 | return (!move ? [] : [move]); | |
178 | } | |
179 | ||
180 | getAllPotentialMoves() { | |
181 | let moves = []; | |
182 | for (let i = 0; i < V.size.x; i++) { | |
183 | for (let j=0; j < V.size.y; j++) { | |
184 | if (this.board[i][j] == V.EMPTY) { | |
185 | const mv = this.doClick(i, j); | |
186 | if (!!mv) moves.push(mv); | |
187 | } | |
188 | } | |
189 | } | |
190 | return moves; | |
191 | } | |
192 | ||
193 | filterValid(moves) { | |
194 | return moves; | |
195 | } | |
196 | ||
197 | getCheckSquares() { | |
198 | return []; | |
199 | } | |
200 | ||
201 | postPlay(move) { | |
202 | if (move.vanish.length >= 1) this.capture = true; | |
203 | } | |
204 | ||
205 | postUndo() { | |
206 | this.capture = false; | |
207 | } | |
208 | ||
209 | getCurrentScore() { | |
210 | if (this.capture) return (this.turn == 'w' ? "0-1" : "1-0"); | |
211 | return "*"; | |
212 | } | |
213 | ||
214 | getComputerMove() { | |
215 | const moves = super.getAllValidMoves(); | |
216 | if (moves.length == 0) return null; | |
217 | // Just random mover for now... writing a good bot is far out of scope | |
218 | return moves[randInt(moves.length)]; | |
219 | } | |
220 | ||
221 | getNotation(move) { | |
222 | return V.CoordsToSquare(move.end); | |
223 | } | |
224 | ||
225 | }; |