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