Commit | Line | Data |
---|---|---|
0c3fe8a6 BA |
1 | import { ChessRules, PiPo, Move } from "@/base_rules"; |
2 | import { ArrayFun} from "@/utils/array"; | |
3 | ||
4 | export const VariantRules = class CrazyhouseRules extends ChessRules | |
a6abf094 | 5 | { |
2d7194bd | 6 | static IsGoodFen(fen) |
a6abf094 | 7 | { |
2d7194bd BA |
8 | if (!ChessRules.IsGoodFen(fen)) |
9 | return false; | |
10 | const fenParsed = V.ParseFen(fen); | |
11 | // 5) Check reserves | |
12 | if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{10,10}$/)) | |
13 | return false; | |
14 | // 6) Check promoted array | |
15 | if (!fenParsed.promoted) | |
16 | return false; | |
a5d56686 | 17 | if (fenParsed.promoted == "-") |
2d7194bd | 18 | return true; //no promoted piece on board |
a5d56686 | 19 | const squares = fenParsed.promoted.split(","); |
2d7194bd BA |
20 | for (let square of squares) |
21 | { | |
22 | const c = V.SquareToCoords(square); | |
23 | if (c.y < 0 || c.y > V.size.y || isNaN(c.x) || c.x < 0 || c.x > V.size.x) | |
24 | return false; | |
25 | } | |
26 | return true; | |
27 | } | |
28 | ||
fb6ceeff BA |
29 | static ParseFen(fen) |
30 | { | |
31 | const fenParts = fen.split(" "); | |
32 | return Object.assign( | |
33 | ChessRules.ParseFen(fen), | |
34 | { | |
b6487fb9 BA |
35 | reserve: fenParts[5], |
36 | promoted: fenParts[6], | |
fb6ceeff BA |
37 | } |
38 | ); | |
39 | } | |
40 | ||
2d7194bd BA |
41 | static GenRandInitFen() |
42 | { | |
fb6ceeff | 43 | return ChessRules.GenRandInitFen() + " 0000000000 -"; |
2d7194bd BA |
44 | } |
45 | ||
46 | getFen() | |
47 | { | |
48 | return super.getFen() + " " + this.getReserveFen() + " " + this.getPromotedFen(); | |
49 | } | |
50 | ||
51 | getReserveFen() | |
52 | { | |
a5d56686 BA |
53 | let counts = new Array(10); |
54 | for (let i=0; i<V.PIECES.length-1; i++) //-1: no king reserve | |
2d7194bd BA |
55 | { |
56 | counts[i] = this.reserve["w"][V.PIECES[i]]; | |
57 | counts[5+i] = this.reserve["b"][V.PIECES[i]]; | |
58 | } | |
59 | return counts.join(""); | |
60 | } | |
61 | ||
62 | getPromotedFen() | |
63 | { | |
64 | let res = ""; | |
65 | for (let i=0; i<V.size.x; i++) | |
66 | { | |
67 | for (let j=0; j<V.size.y; j++) | |
68 | { | |
69 | if (this.promoted[i][j]) | |
70 | res += V.CoordsToSquare({x:i,y:j}); | |
71 | } | |
72 | } | |
73 | if (res.length > 0) | |
74 | res = res.slice(0,-1); //remove last comma | |
fb6ceeff BA |
75 | else |
76 | res = "-"; | |
2d7194bd BA |
77 | return res; |
78 | } | |
79 | ||
80 | setOtherVariables(fen) | |
81 | { | |
82 | super.setOtherVariables(fen); | |
83 | const fenParsed = V.ParseFen(fen); | |
84 | // Also init reserves (used by the interface to show landable pieces) | |
a6abf094 BA |
85 | this.reserve = |
86 | { | |
87 | "w": | |
88 | { | |
2d7194bd BA |
89 | [V.PAWN]: parseInt(fenParsed.reserve[0]), |
90 | [V.ROOK]: parseInt(fenParsed.reserve[1]), | |
91 | [V.KNIGHT]: parseInt(fenParsed.reserve[2]), | |
92 | [V.BISHOP]: parseInt(fenParsed.reserve[3]), | |
93 | [V.QUEEN]: parseInt(fenParsed.reserve[4]), | |
a6abf094 BA |
94 | }, |
95 | "b": | |
96 | { | |
2d7194bd BA |
97 | [V.PAWN]: parseInt(fenParsed.reserve[5]), |
98 | [V.ROOK]: parseInt(fenParsed.reserve[6]), | |
99 | [V.KNIGHT]: parseInt(fenParsed.reserve[7]), | |
100 | [V.BISHOP]: parseInt(fenParsed.reserve[8]), | |
101 | [V.QUEEN]: parseInt(fenParsed.reserve[9]), | |
a6abf094 BA |
102 | } |
103 | }; | |
0c3fe8a6 | 104 | this.promoted = ArrayFun.init(V.size.x, V.size.y, false); |
fb6ceeff | 105 | if (fenParsed.promoted != "-") |
2d7194bd | 106 | { |
fb6ceeff BA |
107 | for (let square of fenParsed.promoted.split(",")) |
108 | { | |
109 | const [x,y] = V.SquareToCoords(square); | |
110 | promoted[x][y] = true; | |
111 | } | |
2d7194bd | 112 | } |
5c42c64e BA |
113 | } |
114 | ||
115 | getColor(i,j) | |
116 | { | |
0b7d99ec BA |
117 | if (i >= V.size.x) |
118 | return (i==V.size.x ? "w" : "b"); | |
5c42c64e BA |
119 | return this.board[i][j].charAt(0); |
120 | } | |
2d7194bd | 121 | |
5c42c64e BA |
122 | getPiece(i,j) |
123 | { | |
0b7d99ec BA |
124 | if (i >= V.size.x) |
125 | return V.RESERVE_PIECES[j]; | |
5c42c64e | 126 | return this.board[i][j].charAt(1); |
a6abf094 BA |
127 | } |
128 | ||
129 | // Used by the interface: | |
1221ac47 | 130 | getReservePpath(color, index) |
a6abf094 | 131 | { |
0b7d99ec | 132 | return color + V.RESERVE_PIECES[index]; |
a6abf094 BA |
133 | } |
134 | ||
92342261 | 135 | // Ordering on reserve pieces |
2d7194bd BA |
136 | static get RESERVE_PIECES() |
137 | { | |
1221ac47 BA |
138 | return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]; |
139 | } | |
140 | ||
141 | getReserveMoves([x,y]) | |
a6abf094 | 142 | { |
a6abf094 | 143 | const color = this.turn; |
0b7d99ec | 144 | const p = V.RESERVE_PIECES[y]; |
1221ac47 BA |
145 | if (this.reserve[color][p] == 0) |
146 | return []; | |
147 | let moves = []; | |
0b7d99ec BA |
148 | const pawnShift = (p==V.PAWN ? 1 : 0); |
149 | for (let i=pawnShift; i<V.size.x-pawnShift; i++) | |
1221ac47 | 150 | { |
0b7d99ec | 151 | for (let j=0; j<V.size.y; j++) |
1221ac47 | 152 | { |
0b7d99ec | 153 | if (this.board[i][j] == V.EMPTY) |
1221ac47 BA |
154 | { |
155 | let mv = new Move({ | |
156 | appear: [ | |
157 | new PiPo({ | |
158 | x: i, | |
159 | y: j, | |
160 | c: color, | |
161 | p: p | |
162 | }) | |
5c42c64e BA |
163 | ], |
164 | vanish: [], | |
6752407b | 165 | start: {x:x, y:y}, //a bit artificial... |
5c42c64e | 166 | end: {x:i, y:j} |
1221ac47 BA |
167 | }); |
168 | moves.push(mv); | |
169 | } | |
170 | } | |
171 | } | |
a6abf094 BA |
172 | return moves; |
173 | } | |
174 | ||
1221ac47 | 175 | getPotentialMovesFrom([x,y]) |
a6abf094 | 176 | { |
0b7d99ec | 177 | if (x >= V.size.x) |
6752407b | 178 | { |
92342261 | 179 | // Reserves, outside of board: x == sizeX(+1) |
6752407b BA |
180 | return this.getReserveMoves([x,y]); |
181 | } | |
182 | // Standard moves | |
183 | return super.getPotentialMovesFrom([x,y]); | |
a6abf094 BA |
184 | } |
185 | ||
a6abf094 BA |
186 | getAllValidMoves() |
187 | { | |
1221ac47 BA |
188 | let moves = super.getAllValidMoves(); |
189 | const color = this.turn; | |
0b7d99ec BA |
190 | for (let i=0; i<V.RESERVE_PIECES.length; i++) |
191 | moves = moves.concat(this.getReserveMoves([V.size.x+(color=="w"?0:1),i])); | |
1221ac47 | 192 | return this.filterValid(moves); |
a6abf094 BA |
193 | } |
194 | ||
a6abf094 BA |
195 | atLeastOneMove() |
196 | { | |
1221ac47 BA |
197 | if (!super.atLeastOneMove()) |
198 | { | |
0b7d99ec BA |
199 | const color = this.turn; |
200 | // Search one reserve move | |
201 | for (let i=0; i<V.RESERVE_PIECES.length; i++) | |
1221ac47 | 202 | { |
0b7d99ec BA |
203 | let moves = this.filterValid( |
204 | this.getReserveMoves([V.size.x+(this.turn=="w"?0:1), i]) ); | |
1221ac47 BA |
205 | if (moves.length > 0) |
206 | return true; | |
207 | } | |
208 | return false; | |
209 | } | |
210 | return true; | |
a6abf094 BA |
211 | } |
212 | ||
a6abf094 BA |
213 | updateVariables(move) |
214 | { | |
1221ac47 | 215 | super.updateVariables(move); |
6752407b BA |
216 | if (move.vanish.length == 2 && move.appear.length == 2) |
217 | return; //skip castle | |
388e4c40 | 218 | const color = move.appear[0].c; |
6752407b | 219 | if (move.vanish.length == 0) |
6752407b | 220 | { |
8a60cacd BA |
221 | this.reserve[color][move.appear[0].p]--; |
222 | return; | |
6752407b | 223 | } |
8a60cacd BA |
224 | move.movePromoted = this.promoted[move.start.x][move.start.y]; |
225 | move.capturePromoted = this.promoted[move.end.x][move.end.y] | |
226 | this.promoted[move.start.x][move.start.y] = false; | |
227 | this.promoted[move.end.x][move.end.y] = move.movePromoted | |
228 | || (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN); | |
229 | if (move.capturePromoted) | |
0b7d99ec | 230 | this.reserve[color][V.PAWN]++; |
8a60cacd BA |
231 | else if (move.vanish.length == 2) |
232 | this.reserve[color][move.vanish[1].p]++; | |
a6abf094 | 233 | } |
1221ac47 | 234 | |
a6abf094 BA |
235 | unupdateVariables(move) |
236 | { | |
1221ac47 | 237 | super.unupdateVariables(move); |
8a60cacd BA |
238 | if (move.vanish.length == 2 && move.appear.length == 2) |
239 | return; | |
1221ac47 | 240 | const color = this.turn; |
6752407b | 241 | if (move.vanish.length == 0) |
6752407b | 242 | { |
8a60cacd BA |
243 | this.reserve[color][move.appear[0].p]++; |
244 | return; | |
6752407b | 245 | } |
8a60cacd BA |
246 | if (move.movePromoted) |
247 | this.promoted[move.start.x][move.start.y] = true; | |
248 | this.promoted[move.end.x][move.end.y] = move.capturePromoted; | |
249 | if (move.capturePromoted) | |
0b7d99ec | 250 | this.reserve[color][V.PAWN]--; |
8a60cacd BA |
251 | else if (move.vanish.length == 2) |
252 | this.reserve[color][move.vanish[1].p]--; | |
a6abf094 BA |
253 | } |
254 | ||
255 | static get SEARCH_DEPTH() { return 2; } //high branching factor | |
256 | ||
6752407b BA |
257 | evalPosition() |
258 | { | |
259 | let evaluation = super.evalPosition(); | |
260 | // Add reserves: | |
0b7d99ec | 261 | for (let i=0; i<V.RESERVE_PIECES.length; i++) |
6752407b | 262 | { |
0b7d99ec BA |
263 | const p = V.RESERVE_PIECES[i]; |
264 | evaluation += this.reserve["w"][p] * V.VALUES[p]; | |
265 | evaluation -= this.reserve["b"][p] * V.VALUES[p]; | |
6752407b BA |
266 | } |
267 | return evaluation; | |
268 | } | |
269 | ||
a6abf094 BA |
270 | getNotation(move) |
271 | { | |
272 | if (move.vanish.length > 0) | |
273 | return super.getNotation(move); | |
274 | // Rebirth: | |
275 | const piece = | |
0b7d99ec | 276 | (move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""); |
2d7194bd | 277 | return piece + "@" + V.CoordsToSquare(move.end); |
a6abf094 | 278 | } |
6752407b BA |
279 | |
280 | getLongNotation(move) | |
281 | { | |
282 | if (move.vanish.length > 0) | |
283 | return super.getLongNotation(move); | |
2d7194bd | 284 | return "@" + V.CoordsToSquare(move.end); |
6752407b | 285 | } |
a6abf094 | 286 | } |