Commit | Line | Data |
---|---|---|
0b8bd121 BA |
1 | import { ChessRules, PiPo, Move } from "@/base_rules"; |
2 | import { ArrayFun } from "@/utils/array"; | |
3 | ||
4 | export class ClorangeRules extends ChessRules { | |
7e8a7ea1 | 5 | |
0b8bd121 BA |
6 | static IsGoodFen(fen) { |
7 | if (!ChessRules.IsGoodFen(fen)) return false; | |
8 | const fenParsed = V.ParseFen(fen); | |
9 | // 5) Check reserves | |
10 | if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{20,20}$/)) | |
11 | return false; | |
12 | return true; | |
13 | } | |
14 | ||
15 | static ParseFen(fen) { | |
16 | const fenParts = fen.split(" "); | |
17 | return Object.assign( | |
18 | ChessRules.ParseFen(fen), | |
19 | { reserve: fenParts[5] } | |
20 | ); | |
21 | } | |
22 | ||
4313762d | 23 | static GenRandInitFen(options) { |
0b8bd121 | 24 | // Capturing and non-capturing reserves: |
4313762d | 25 | return ChessRules.GenRandInitFen(options) + " 00000000000000000000"; |
0b8bd121 BA |
26 | } |
27 | ||
28 | getFen() { | |
29 | return super.getFen() + " " + this.getReserveFen(); | |
30 | } | |
31 | ||
32 | getFenForRepeat() { | |
33 | return super.getFenForRepeat() + "_" + this.getReserveFen(); | |
34 | } | |
35 | ||
36 | getReserveFen() { | |
e50a8025 BA |
37 | return ( |
38 | Object.keys(this.reserve).map( | |
39 | c => Object.values(this.reserve[c]).join("")).join("") | |
40 | ); | |
41 | } | |
42 | ||
43 | getEpSquare(moveOrSquare) { | |
44 | if (!moveOrSquare) return undefined; | |
45 | if (typeof moveOrSquare === "string") { | |
46 | const square = moveOrSquare; | |
47 | if (square == "-") return undefined; | |
48 | return V.SquareToCoords(square); | |
49 | } | |
50 | const move = moveOrSquare; | |
51 | const s = move.start, | |
52 | e = move.end; | |
53 | if ( | |
54 | s.y == e.y && | |
55 | Math.abs(s.x - e.x) == 2 && | |
56 | move.vanish.length > 0 && ['p', 's'].includes(move.vanish[0].p) | |
0b8bd121 | 57 | ) { |
e50a8025 BA |
58 | return { |
59 | x: (s.x + e.x) / 2, | |
60 | y: s.y | |
61 | }; | |
0b8bd121 | 62 | } |
e50a8025 | 63 | return undefined; |
0b8bd121 BA |
64 | } |
65 | ||
66 | setOtherVariables(fen) { | |
67 | super.setOtherVariables(fen); | |
0b8bd121 | 68 | // Also init reserves (used by the interface to show landable pieces) |
e50a8025 BA |
69 | const reserve = |
70 | V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10)); | |
0b8bd121 BA |
71 | this.reserve = { |
72 | w: { | |
e50a8025 BA |
73 | 'p': reserve[0], |
74 | 'r': reserve[1], | |
75 | 'n': reserve[2], | |
76 | 'b': reserve[3], | |
77 | 'q': reserve[4], | |
78 | 's': reserve[5], | |
79 | 'u': reserve[6], | |
80 | 'o': reserve[7], | |
81 | 'c': reserve[8], | |
82 | 't': reserve[9] | |
0b8bd121 BA |
83 | }, |
84 | b: { | |
e50a8025 BA |
85 | 'p': reserve[10], |
86 | 'r': reserve[11], | |
87 | 'n': reserve[12], | |
88 | 'b': reserve[13], | |
89 | 'q': reserve[14], | |
90 | 's': reserve[15], | |
91 | 'u': reserve[16], | |
92 | 'o': reserve[17], | |
93 | 'c': reserve[18], | |
94 | 't': reserve[19] | |
0b8bd121 BA |
95 | } |
96 | }; | |
97 | } | |
98 | ||
99 | getColor(i, j) { | |
100 | if (i >= V.size.x) return i == V.size.x ? "w" : "b"; | |
101 | return this.board[i][j].charAt(0); | |
102 | } | |
103 | ||
104 | getPiece(i, j) { | |
105 | if (i >= V.size.x) return V.RESERVE_PIECES[j]; | |
106 | return this.board[i][j].charAt(1); | |
107 | } | |
108 | ||
e50a8025 BA |
109 | getPpath(b) { |
110 | return (V.NON_VIOLENT.includes(b[1]) ? "Clorange/" : "") + b; | |
111 | } | |
112 | ||
0b8bd121 | 113 | getReservePpath(index, color) { |
e50a8025 BA |
114 | const prefix = |
115 | (V.NON_VIOLENT.includes(V.RESERVE_PIECES[index]) ? "Clorange/" : ""); | |
116 | return prefix + color + V.RESERVE_PIECES[index]; | |
0b8bd121 BA |
117 | } |
118 | ||
119 | static get NON_VIOLENT() { | |
e50a8025 BA |
120 | return ['s', 'u', 'o', 'c', 't']; |
121 | } | |
122 | ||
123 | static get PIECES() { | |
124 | return ChessRules.PIECES.concat(V.NON_VIOLENT); | |
0b8bd121 BA |
125 | } |
126 | ||
127 | // Ordering on reserve pieces | |
128 | static get RESERVE_PIECES() { | |
e50a8025 | 129 | return V.PIECES.filter(p => p != 'k'); |
0b8bd121 BA |
130 | } |
131 | ||
132 | getReserveMoves([x, y]) { | |
133 | const color = this.turn; | |
134 | const p = V.RESERVE_PIECES[y]; | |
135 | if (this.reserve[color][p] == 0) return []; | |
136 | let moves = []; | |
e50a8025 BA |
137 | let rank1 = 0; |
138 | let rank2 = V.size.x - 1; | |
139 | if (['p', 's'].includes(p)) { | |
140 | if (color == 'w') rank1++; | |
141 | else rank2--; | |
142 | } | |
143 | for (let i = rank1; i <= rank2; i++) { | |
0b8bd121 BA |
144 | for (let j = 0; j < V.size.y; j++) { |
145 | if (this.board[i][j] == V.EMPTY) { | |
146 | let mv = new Move({ | |
147 | appear: [ | |
148 | new PiPo({ | |
149 | x: i, | |
150 | y: j, | |
151 | c: color, | |
152 | p: p | |
153 | }) | |
154 | ], | |
155 | vanish: [], | |
156 | start: { x: x, y: y }, //a bit artificial... | |
157 | end: { x: i, y: j } | |
158 | }); | |
159 | moves.push(mv); | |
160 | } | |
161 | } | |
162 | } | |
163 | return moves; | |
164 | } | |
165 | ||
0b8bd121 | 166 | getPotentialMovesFrom([x, y]) { |
e50a8025 | 167 | if (x >= V.size.x) |
0b8bd121 BA |
168 | // Reserves, outside of board: x == sizeX(+1) |
169 | return this.getReserveMoves([x, y]); | |
0b8bd121 | 170 | // Standard moves |
e50a8025 | 171 | switch (this.getPiece(x, y)) { |
165530a5 | 172 | case 's': return this.getPotentialPawnMoves([x, y]); |
e50a8025 BA |
173 | case 'u': return super.getPotentialRookMoves([x, y]); |
174 | case 'o': return super.getPotentialKnightMoves([x, y]); | |
175 | case 'c': return super.getPotentialBishopMoves([x, y]); | |
176 | case 't': return super.getPotentialQueenMoves([x, y]); | |
177 | default: return super.getPotentialMovesFrom([x, y]); | |
178 | } | |
179 | return []; //never reached | |
0b8bd121 BA |
180 | } |
181 | ||
e50a8025 BA |
182 | getPotentialPawnMoves(sq) { |
183 | let moves = super.getPotentialPawnMoves(sq); | |
165530a5 BA |
184 | if (moves.length > 0 && moves[0].vanish[0].p == 's') { |
185 | // Remove captures for non-violent pawns: | |
186 | moves = moves.filter(m => m.vanish.length == 1); | |
187 | moves.forEach(m => { | |
188 | if (m.appear[0].p != 's') { | |
189 | // Promotion pieces should be non-violent as well: | |
190 | const pIdx = ChessRules.PIECES.findIndex(p => p == m.appear[0].p) | |
191 | m.appear[0].p = V.NON_VIOLENT[pIdx]; | |
192 | } | |
193 | }); | |
194 | } | |
0b8bd121 BA |
195 | return moves; |
196 | } | |
197 | ||
4313762d BA |
198 | canTake([x1, y1], [x2, y2]) { |
199 | return ( | |
200 | this.getColor(x1, y1) !== this.getColor(x2, y2) && | |
201 | ChessRules.PIECES.includes(this.getPiece(x1, y1)) | |
202 | ); | |
e50a8025 BA |
203 | } |
204 | ||
0b8bd121 BA |
205 | getAllValidMoves() { |
206 | let moves = super.getAllPotentialMoves(); | |
207 | const color = this.turn; | |
208 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
209 | moves = moves.concat( | |
210 | this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i]) | |
211 | ); | |
212 | } | |
213 | return this.filterValid(moves); | |
214 | } | |
215 | ||
216 | atLeastOneMove() { | |
217 | if (!super.atLeastOneMove()) { | |
218 | // Search one reserve move | |
219 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
220 | let moves = this.filterValid( | |
221 | this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i]) | |
222 | ); | |
223 | if (moves.length > 0) return true; | |
224 | } | |
225 | return false; | |
226 | } | |
227 | return true; | |
228 | } | |
229 | ||
0b8bd121 BA |
230 | prePlay(move) { |
231 | super.prePlay(move); | |
232 | // Skip castle: | |
233 | if (move.vanish.length == 2 && move.appear.length == 2) return; | |
234 | const color = this.turn; | |
235 | if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]--; | |
e50a8025 BA |
236 | else if (move.vanish.length == 2) { |
237 | // Capture | |
238 | const normal = ChessRules.PIECES.includes(move.vanish[1].p); | |
239 | const pIdx = | |
240 | normal | |
241 | ? ChessRules.PIECES.findIndex(p => p == move.vanish[1].p) | |
242 | : V.NON_VIOLENT.findIndex(p => p == move.vanish[1].p); | |
243 | const rPiece = (normal ? V.NON_VIOLENT : ChessRules.PIECES)[pIdx]; | |
244 | this.reserve[move.vanish[1].c][rPiece]++; | |
245 | } | |
0b8bd121 BA |
246 | } |
247 | ||
248 | postUndo(move) { | |
249 | super.postUndo(move); | |
250 | if (move.vanish.length == 2 && move.appear.length == 2) return; | |
251 | const color = this.turn; | |
252 | if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]++; | |
e50a8025 BA |
253 | else if (move.vanish.length == 2) { |
254 | const normal = ChessRules.PIECES.includes(move.vanish[1].p); | |
255 | const pIdx = | |
256 | normal | |
257 | ? ChessRules.PIECES.findIndex(p => p == move.vanish[1].p) | |
258 | : V.NON_VIOLENT.findIndex(p => p == move.vanish[1].p); | |
259 | const rPiece = (normal ? V.NON_VIOLENT : ChessRules.PIECES)[pIdx]; | |
260 | this.reserve[move.vanish[1].c][rPiece]--; | |
261 | } | |
0b8bd121 BA |
262 | } |
263 | ||
264 | static get SEARCH_DEPTH() { | |
265 | return 2; | |
266 | } | |
267 | ||
e50a8025 BA |
268 | static get VALUES() { |
269 | return Object.assign( | |
270 | { | |
271 | s: 0.75, | |
272 | u: 4, | |
273 | o: 2, | |
274 | c: 2, | |
275 | t: 7 | |
276 | }, | |
277 | ChessRules.VALUES | |
278 | ); | |
279 | } | |
280 | ||
0b8bd121 BA |
281 | evalPosition() { |
282 | let evaluation = super.evalPosition(); | |
283 | // Add reserves: | |
284 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
285 | const p = V.RESERVE_PIECES[i]; | |
286 | evaluation += this.reserve["w"][p] * V.VALUES[p]; | |
287 | evaluation -= this.reserve["b"][p] * V.VALUES[p]; | |
288 | } | |
289 | return evaluation; | |
290 | } | |
291 | ||
292 | getNotation(move) { | |
293 | const finalSquare = V.CoordsToSquare(move.end); | |
294 | if (move.vanish.length > 0) { | |
e50a8025 BA |
295 | // Standard move (maybe with non-violent piece) |
296 | let notation = super.getNotation(move); | |
297 | if (move.vanish[0].p == 's' && move.appear[0].p != 's') | |
298 | // Fix non-violent promotions: | |
299 | notation += "=" + move.appear[0].p.toUpperCase(); | |
300 | return notation; | |
0b8bd121 BA |
301 | } |
302 | // Rebirth: | |
303 | const piece = | |
304 | move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; | |
305 | return piece + "@" + V.CoordsToSquare(move.end); | |
306 | } | |
7e8a7ea1 | 307 | |
0b8bd121 | 308 | }; |