Improve Crazyhouse; still not debugged
[vchess.git] / public / javascripts / variants / Crazyhouse.js
1 class CrazyhouseRules extends ChessRules
2 {
3 initVariables(fen)
4 {
5 super.initVariables(fen);
6 // Also init reserves (used by the interface to show landing pieces)
7 const V = VariantRules;
8 this.reserve =
9 {
10 "w":
11 {
12 [V.PAWN]: 0,
13 [V.ROOK]: 0,
14 [V.KNIGHT]: 0,
15 [V.BISHOP]: 0,
16 [V.QUEEN]: 0,
17 },
18 "b":
19 {
20 [V.PAWN]: 0,
21 [V.ROOK]: 0,
22 [V.KNIGHT]: 0,
23 [V.BISHOP]: 0,
24 [V.QUEEN]: 0,
25 }
26 };
27 const [sizeX,sizeY] = VariantRules.size;
28 this.promoted = doubleArray(sizeX, sizeY, false);
29 // May be a continuation: adjust numbers of pieces in reserve + promoted pieces
30 this.moves.forEach(m => { this.updateVariables(m); });
31 }
32
33 getColor(i,j)
34 {
35 const sizeX = VariantRules.size[0];
36 if (i >= sizeX)
37 return (i==sizeX ? "w" : "b");
38 return this.board[i][j].charAt(0);
39 }
40 getPiece(i,j)
41 {
42 const sizeX = VariantRules.size[0];
43 if (i >= sizeX)
44 return VariantRules.RESERVE_PIECES[j];
45 return this.board[i][j].charAt(1);
46 }
47
48 // Used by the interface:
49 getReservePpath(color, index)
50 {
51 return color + VariantRules.RESERVE_PIECES[index];
52 }
53
54 // Put an ordering on reserve pieces
55 static get RESERVE_PIECES() {
56 const V = VariantRules;
57 return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
58 }
59
60 getReserveMoves([x,y])
61 {
62 const color = this.turn;
63 const p = VariantRules.RESERVE_PIECES[y];
64 if (this.reserve[color][p] == 0)
65 return [];
66 let moves = [];
67 const [sizeX,sizeY] = VariantRules.size;
68 const pawnShift = (p==VariantRules.PAWN ? 1 : 0);
69 for (let i=pawnShift; i<sizeX-pawnShift; i++)
70 {
71 for (let j=0; j<sizeY; j++)
72 {
73 if (this.board[i][j] == VariantRules.EMPTY)
74 {
75 let mv = new Move({
76 appear: [
77 new PiPo({
78 x: i,
79 y: j,
80 c: color,
81 p: p
82 })
83 ],
84 vanish: [],
85 start: {x:x, y:y}, //a bit artificial...
86 end: {x:i, y:j}
87 });
88 moves.push(mv);
89 }
90 }
91 }
92 return moves;
93 }
94
95 getPotentialMovesFrom([x,y])
96 {
97 const sizeX = VariantRules.size[0];
98 if (x >= sizeX)
99 {
100 // Reserves, outside of board: x == sizeX
101 return this.getReserveMoves([x,y]);
102 }
103 // Standard moves
104 return super.getPotentialMovesFrom([x,y]);
105 }
106
107 getAllValidMoves()
108 {
109 let moves = super.getAllValidMoves();
110 const color = this.turn;
111 const sizeX = VariantRules.size[0];
112 for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
113 moves = moves.concat(this.getReserveMoves([sizeX+(color=="w"?0:1),i]));
114 return this.filterValid(moves);
115 }
116
117 atLeastOneMove()
118 {
119 if (!super.atLeastOneMove())
120 {
121 const sizeX = VariantRules.size[0];
122 // Scan for reserve moves
123 for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
124 {
125 let moves = this.filterValid(this.getReserveMoves([sizeX,i]));
126 if (moves.length > 0)
127 return true;
128 }
129 return false;
130 }
131 return true;
132 }
133
134 updateVariables(move)
135 {
136 super.updateVariables(move);
137 if (move.vanish.length == 2 && move.appear.length == 2)
138 return; //skip castle
139 const color = this.turn;
140 const V = VariantRules;
141 // Three types of move:
142 // 1. Rebirth: just update material
143 // 2. Standard move:
144 // a. check if a promoted piece is moving
145 // b. check if it's a promotion (mutually exclusive)
146 // 3. Capture:
147 // a. check if a promoted piece is captured (and mark move)
148 // b. check if a promoted piece is moving
149 // c. check if it's a promotion (mutually exclusive with b)
150 if (move.vanish.length == 0)
151 this.reserve[color][move.appear[0].p]--;
152 else if (move.vanish.length == 1)
153 {
154 if (this.promoted[move.start.x][move.start.y])
155 {
156 this.promoted[move.start.x][move.start.y] = false;
157 this.promoted[move.end.x][move.end.y] = true;
158 }
159 else if (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN)
160 this.promoted[move.end.x][move.end.y] = true;
161 }
162 else //capture
163 {
164 if (this.promoted[move.end.x][move.end.y])
165 {
166 move.capturePromoted = true; //required for undo
167 this.reserve[color][VariantRules.PAWN]++;
168 this.promoted[move.end.x][move.end.y] = false;
169 }
170 else
171 this.reserve[color][move.vanish[1].p]++;
172 if (this.promoted[move.start.x][move.start.y])
173 {
174 this.promoted[move.start.x][move.start.y] = false;
175 this.promoted[move.end.x][move.end.y] = true;
176 }
177 else if (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN)
178 this.promoted[move.end.x][move.end.y] = true;
179 }
180 }
181
182 unupdateVariables(move)
183 {
184 super.unupdateVariables(move);
185 const color = this.turn;
186 const V = VariantRules;
187 if (move.vanish.length == 0)
188 this.reserve[color][move.appear[0].p]++;
189 else if (move.vanish.length == 1)
190 {
191 if (this.promoted[move.end.x][move.end.y])
192 {
193 this.promoted[move.end.x][move.end.y] = false;
194 if (move.vanish[0].p != V.PAWN || move.appear[0].p == V.PAWN)
195 {
196 // Not a promotion (= promoted piece creation)
197 this.promoted[move.start.x][move.start.y] = true;
198 }
199 }
200 }
201 else //capture
202 {
203 if (this.promoted[move.end.x][move.end.y])
204 {
205 this.promoted[move.end.x][move.end.y] = !!move.capturePromoted;
206 if (move.vanish[0].p != V.PAWN || move.appear[0].p == V.PAWN)
207 this.promoted[move.start.x][move.start.y] = true;
208 }
209 // Un-update material:
210 if (move.capturePromoted)
211 this.reserve[color][VariantRules.PAWN]--;
212 else
213 this.reserve[color][move.vanish[1].p]--;
214 }
215 }
216
217 static get SEARCH_DEPTH() { return 2; } //high branching factor
218
219 evalPosition()
220 {
221 let evaluation = super.evalPosition();
222 // Add reserves:
223 for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
224 {
225 const p = VariantRules.RESERVE_PIECES[i];
226 evaluation += this.reserve["w"][p] * VariantRules.VALUES[p];
227 evaluation -= this.reserve["b"][p] * VariantRules.VALUES[p];
228 }
229 return evaluation;
230 }
231
232 getNotation(move)
233 {
234 if (move.vanish.length > 0)
235 return super.getNotation(move);
236 // Rebirth:
237 const piece =
238 (move.appear[0].p != VariantRules.PAWN ? move.appear[0].p.toUpperCase() : "");
239 const finalSquare =
240 String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
241 return piece + "@" + finalSquare;
242 }
243
244 getLongNotation(move)
245 {
246 if (move.vanish.length > 0)
247 return super.getLongNotation(move);
248 const finalSquare =
249 String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
250 return "@" + finalSquare;
251 }
252 }