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