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