Commit | Line | Data |
---|---|---|
d2af3400 | 1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
c11afcdf | 2 | import { randInt } from "@/utils/alea"; |
d2af3400 | 3 | |
d2af3400 BA |
4 | export class KonaneRules extends ChessRules { |
5 | ||
6 | static get HasFlags() { | |
7 | return false; | |
8 | } | |
9 | ||
10 | static get HasEnpassant() { | |
11 | return false; | |
12 | } | |
13 | ||
cbe95378 BA |
14 | static get ReverseColors() { |
15 | return true; | |
16 | } | |
17 | ||
cbe95378 BA |
18 | getPiece() { |
19 | return V.PAWN; | |
20 | } | |
21 | ||
d2af3400 BA |
22 | getPpath(b) { |
23 | return "Konane/" + b; | |
24 | } | |
25 | ||
26 | static IsGoodPosition(position) { | |
27 | if (position.length == 0) return false; | |
28 | const rows = position.split("/"); | |
29 | if (rows.length != V.size.x) return false; | |
30 | for (let row of rows) { | |
31 | let sumElts = 0; | |
32 | for (let i = 0; i < row.length; i++) { | |
cdab5663 | 33 | if (row[i].toLowerCase() == V.PAWN) sumElts++; |
d2af3400 BA |
34 | else { |
35 | const num = parseInt(row[i], 10); | |
36 | if (isNaN(num) || num <= 0) return false; | |
37 | sumElts += num; | |
38 | } | |
39 | } | |
40 | if (sumElts != V.size.y) return false; | |
41 | } | |
42 | return true; | |
43 | } | |
44 | ||
45 | static GenRandInitFen() { | |
46 | return ( | |
47 | "PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/" + | |
48 | "PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP w 0" | |
49 | ); | |
50 | } | |
51 | ||
52 | setOtherVariables(fen) { | |
53 | this.captures = []; //reinit for each move | |
54 | } | |
55 | ||
7c05a5f2 | 56 | hoverHighlight([x, y], side) { |
d2af3400 | 57 | const c = this.turn; |
7c05a5f2 | 58 | if (this.movesCount >= 2 || (!!side && side != c)) return false; |
d2af3400 BA |
59 | if (c == 'w') return (x == y && [0, 3, 4, 7].includes(x)); |
60 | // "Black": search for empty square and allow nearby | |
61 | for (let i of [0, 3, 4, 7]) { | |
62 | if (this.board[i][i] == V.EMPTY) | |
63 | return (Math.abs(x - i) + Math.abs(y - i) == 1) | |
64 | } | |
65 | } | |
66 | ||
67 | onlyClick([x, y]) { | |
68 | return ( | |
69 | this.movesCount <= 1 || | |
70 | // TODO: next line theoretically shouldn't be required... | |
71 | (this.movesCount == 2 && this.getColor(x, y) != this.turn) | |
72 | ); | |
73 | } | |
74 | ||
75 | doClick([x, y]) { | |
76 | if (this.movesCount >= 2) return null; | |
77 | const color = this.turn; | |
78 | if (color == 'w') { | |
79 | if (x != y || ![0, 3, 4, 7].includes(x)) return null; | |
80 | return new Move({ | |
81 | appear: [], | |
82 | vanish: [ new PiPo({ x: x, y: y, c: color, p: V.PAWN }) ], | |
83 | end: { x: x, y: y } | |
84 | }); | |
85 | } | |
86 | // "Black": search for empty square and allow nearby | |
87 | for (let i of [0, 3, 4, 7]) { | |
88 | if (this.board[i][i] == V.EMPTY) { | |
89 | if (Math.abs(x - i) + Math.abs(y - i) != 1) return null; | |
90 | return new Move({ | |
91 | appear: [], | |
92 | vanish: [ new PiPo({ x: x, y: y, c: color, p: V.PAWN }) ], | |
93 | end: { x: x, y: y } | |
94 | }); | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
99 | getPotentialMovesFrom([x, y]) { | |
100 | if (this.movesCount <= 1) { | |
101 | const mv = this.doClick([x, y]); | |
102 | return (!!mv ? [mv] : []); | |
103 | } | |
104 | const L = this.captures.length; | |
105 | const c = (L > 0 ? this.captures[L-1] : null); | |
106 | const color = this.turn; | |
107 | const oppCol = V.GetOppCol(color); | |
108 | let step = null; | |
109 | let moves = []; | |
110 | if (!!c) { | |
111 | if (x != c.end.x || y != c.end.y) return []; | |
112 | step = [(c.end.x - c.start.x) / 2, (c.end.y - c.start.y) / 2]; | |
113 | // Add move to adjacent empty square to mark "end of capture" | |
114 | moves.push( | |
115 | new Move({ | |
116 | appear: [], | |
117 | vanish: [], | |
118 | start: { x: x, y: y }, | |
119 | end: { x: x - step[0], y: y - step[1] } | |
120 | }) | |
121 | ); | |
122 | } | |
123 | // Examine captures from here | |
124 | for (let s of (!!step ? [step] : V.steps[V.ROOK])) { | |
125 | let [i, j] = [x + 2*s[0], y + 2*s[1]]; | |
126 | if ( | |
127 | !!c || //avoid redundant checks if continuation | |
128 | ( | |
129 | V.OnBoard(i, j) && | |
130 | this.board[i][j] == V.EMPTY && | |
131 | this.board[i - s[0]][j - s[1]] != V.EMPTY && | |
132 | this.getColor(i - s[0], j - s[1]) == oppCol | |
133 | ) | |
134 | ) { | |
135 | let mv = new Move({ | |
136 | appear: [ | |
137 | new PiPo({ x: i, y: j, c: color, p: V.PAWN }) | |
138 | ], | |
139 | vanish: [ | |
140 | new PiPo({ x: x, y: y, c: color, p: V.PAWN }), | |
141 | new PiPo({ x: i - s[0], y: j - s[1], c: oppCol, p: V.PAWN }) | |
142 | ] | |
143 | }); | |
144 | // Is there another capture possible then? | |
145 | [i, j] = [i + 2*s[0], j + 2*s[1]]; | |
146 | if ( | |
147 | V.OnBoard(i, j) && | |
148 | this.board[i][j] == V.EMPTY && | |
149 | this.board[i - s[0]][j - s[1]] != V.EMPTY && | |
150 | this.getColor(i - s[0], j - s[1]) == oppCol | |
151 | ) { | |
152 | mv.end.moreCapture = true; | |
153 | } | |
154 | moves.push(mv); | |
155 | } | |
156 | } | |
157 | return moves; | |
158 | } | |
159 | ||
160 | filterValid(moves) { | |
161 | return moves; | |
162 | } | |
163 | ||
164 | getCheckSquares() { | |
165 | return []; | |
166 | } | |
167 | ||
168 | getCurrentScore() { | |
169 | if (this.atLeastOneMove()) return "*"; | |
170 | return (this.turn == "w" ? "0-1" : "1-0"); | |
171 | } | |
172 | ||
173 | play(move) { | |
174 | V.PlayOnBoard(this.board, move); | |
175 | if (!move.end.moreCapture) { | |
176 | this.turn = V.GetOppCol(this.turn); | |
177 | this.movesCount++; | |
178 | this.captures = []; | |
179 | } | |
180 | else { | |
181 | this.captures.push( | |
182 | { | |
183 | start: move.start, | |
184 | end: { x: move.end.x, y: move.end.y } | |
185 | } | |
186 | ); | |
187 | } | |
188 | } | |
189 | ||
190 | undo(move) { | |
191 | V.UndoOnBoard(this.board, move); | |
192 | if (!move.end.moreCapture) { | |
193 | this.turn = V.GetOppCol(this.turn); | |
194 | this.movesCount--; | |
195 | } | |
196 | else this.captures.pop(); | |
197 | } | |
198 | ||
c11afcdf BA |
199 | getComputerMove() { |
200 | const color = this.turn; | |
201 | let mvArray = []; | |
202 | let mv = null; | |
203 | const undoAll = () => { | |
204 | for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); | |
205 | }; | |
206 | // Just play random moves (for now at least. TODO?) | |
207 | while (this.turn == color) { | |
208 | let moves = super.getAllValidMoves(); | |
209 | if (moves.length == 0) { | |
210 | // Shouldn't happen, but... | |
211 | undoAll(); | |
212 | return null; | |
213 | } | |
214 | mv = moves[randInt(moves.length)]; | |
215 | mvArray.push(mv); | |
216 | this.play(mv); | |
217 | } | |
218 | undoAll(); | |
219 | return (mvArray.length > 1 ? mvArray : mvArray[0]); | |
d2af3400 BA |
220 | } |
221 | ||
222 | getNotation(move) { | |
223 | if (this.movesCount <= 1) return V.CoordsToSquare(move.start) + "X"; | |
224 | if (move.vanish.length == 0) return "end"; | |
225 | return V.CoordsToSquare(move.start) + "x" + V.CoordsToSquare(move.end); | |
226 | } | |
227 | ||
228 | }; |