Pandemonium: promotion on last 2 ranks
[vchess.git] / client / src / variants / Crazyhouse.js
CommitLineData
0c3fe8a6 1import { ChessRules, PiPo, Move } from "@/base_rules";
6808d7a1 2import { ArrayFun } from "@/utils/array";
0c3fe8a6 3
32f6285e 4export class CrazyhouseRules extends ChessRules {
7e8a7ea1 5
809ab1a8
BA
6 static get PawnSpecs() {
7 return Object.assign(
8 {},
9 ChessRules.PawnSpecs,
10 // Change names to know that this goes back to pawn after capture:
11 { promotions: ['u', 'o', 'c', 't'] }
12 );
13 }
14
15 static get PIECES() {
16 return ChessRules.PIECES.concat(['u', 'o', 'c', 't']);
17 }
18
19 getPpath(b) {
20 const prefix = (ChessRules.PIECES.includes(b[1]) ? "" : "Crazyhouse/");
21 return prefix + b;
22 }
23
6808d7a1
BA
24 static IsGoodFen(fen) {
25 if (!ChessRules.IsGoodFen(fen)) return false;
dac39588
BA
26 const fenParsed = V.ParseFen(fen);
27 // 5) Check reserves
28 if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{10,10}$/))
29 return false;
dac39588
BA
30 return true;
31 }
2d7194bd 32
6808d7a1 33 static ParseFen(fen) {
dac39588 34 const fenParts = fen.split(" ");
6f2f9437
BA
35 return Object.assign(
36 ChessRules.ParseFen(fen),
809ab1a8 37 { reserve: fenParts[5] }
6f2f9437 38 );
dac39588 39 }
fb6ceeff 40
7ba4a5bc
BA
41 static GenRandInitFen(randomness) {
42 return ChessRules.GenRandInitFen(randomness) + " 0000000000 -";
dac39588 43 }
2d7194bd 44
6808d7a1 45 getFen() {
809ab1a8 46 return super.getFen() + " " + this.getReserveFen();
dac39588 47 }
2d7194bd 48
f9c36b2d 49 getFenForRepeat() {
809ab1a8 50 return super.getFenForRepeat() + "_" + this.getReserveFen();
f9c36b2d
BA
51 }
52
6808d7a1 53 getReserveFen() {
dac39588 54 let counts = new Array(10);
6808d7a1
BA
55 for (
56 let i = 0;
57 i < V.PIECES.length - 1;
58 i++ //-1: no king reserve
59 ) {
dac39588 60 counts[i] = this.reserve["w"][V.PIECES[i]];
6808d7a1 61 counts[5 + i] = this.reserve["b"][V.PIECES[i]];
dac39588
BA
62 }
63 return counts.join("");
64 }
2d7194bd 65
6808d7a1 66 setOtherVariables(fen) {
dac39588 67 super.setOtherVariables(fen);
ded43c88 68 const fenParsed = V.ParseFen(fen);
dac39588 69 // Also init reserves (used by the interface to show landable pieces)
ded43c88 70 const reserve = fenParsed.reserve.split("").map(x => parseInt(x, 10));
6808d7a1
BA
71 this.reserve = {
72 w: {
e50a8025
BA
73 [V.PAWN]: reserve[0],
74 [V.ROOK]: reserve[1],
75 [V.KNIGHT]: reserve[2],
76 [V.BISHOP]: reserve[3],
77 [V.QUEEN]: reserve[4]
dac39588 78 },
6808d7a1 79 b: {
e50a8025
BA
80 [V.PAWN]: reserve[5],
81 [V.ROOK]: reserve[6],
82 [V.KNIGHT]: reserve[7],
83 [V.BISHOP]: reserve[8],
84 [V.QUEEN]: reserve[9]
dac39588
BA
85 }
86 };
dac39588 87 }
5c42c64e 88
6808d7a1
BA
89 getColor(i, j) {
90 if (i >= V.size.x) return i == V.size.x ? "w" : "b";
dac39588
BA
91 return this.board[i][j].charAt(0);
92 }
2d7194bd 93
809ab1a8
BA
94 // Pieces types after pawn promotion
95 static get PromotionMap() {
96 return {
97 u: 'r',
98 o: 'n',
99 c: 'b',
100 t: 'q'
101 };
102 }
103
6808d7a1
BA
104 getPiece(i, j) {
105 if (i >= V.size.x) return V.RESERVE_PIECES[j];
809ab1a8
BA
106 const p = this.board[i][j].charAt(1);
107 if (ChessRules.PIECES.includes(p)) return p;
108 // Pawn promotion:
109 return V.PromotionMap[p];
dac39588 110 }
a6abf094 111
dac39588 112 // Used by the interface:
241bf8f2 113 getReservePpath(index, color) {
dac39588
BA
114 return color + V.RESERVE_PIECES[index];
115 }
9d4a0218
BA
116// // Version if some day I have pieces with numbers printed on it:
117// getReservePpath(index, color) {
118// return (
119// "Crazyhouse/" +
120// color + V.RESERVE_PIECES[index] +
121// "_" + this.vr.reserve[playingColor][V.RESERVE_PIECES[i]]
122// );
123// }
a6abf094 124
dac39588 125 // Ordering on reserve pieces
6808d7a1
BA
126 static get RESERVE_PIECES() {
127 return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
dac39588 128 }
1221ac47 129
6808d7a1 130 getReserveMoves([x, y]) {
dac39588
BA
131 const color = this.turn;
132 const p = V.RESERVE_PIECES[y];
6808d7a1 133 if (this.reserve[color][p] == 0) return [];
dac39588 134 let moves = [];
6808d7a1
BA
135 const pawnShift = p == V.PAWN ? 1 : 0;
136 for (let i = pawnShift; i < V.size.x - pawnShift; i++) {
137 for (let j = 0; j < V.size.y; j++) {
138 if (this.board[i][j] == V.EMPTY) {
dac39588
BA
139 let mv = new Move({
140 appear: [
141 new PiPo({
142 x: i,
143 y: j,
144 c: color,
145 p: p
146 })
147 ],
148 vanish: [],
6808d7a1
BA
149 start: { x: x, y: y }, //a bit artificial...
150 end: { x: i, y: j }
dac39588
BA
151 });
152 moves.push(mv);
153 }
154 }
155 }
156 return moves;
157 }
a6abf094 158
6808d7a1 159 getPotentialMovesFrom([x, y]) {
1e8a8386 160 if (x >= V.size.x)
dac39588 161 // Reserves, outside of board: x == sizeX(+1)
6808d7a1 162 return this.getReserveMoves([x, y]);
dac39588 163 // Standard moves
6808d7a1 164 return super.getPotentialMovesFrom([x, y]);
dac39588 165 }
a6abf094 166
6808d7a1 167 getAllValidMoves() {
0e001022 168 let moves = super.getAllPotentialMoves();
dac39588 169 const color = this.turn;
0e001022 170 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
6808d7a1
BA
171 moves = moves.concat(
172 this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
173 );
0e001022 174 }
dac39588
BA
175 return this.filterValid(moves);
176 }
a6abf094 177
6808d7a1 178 atLeastOneMove() {
d982fffc
BA
179 if (super.atLeastOneMove()) return true;
180 // Search one reserve move
181 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
182 const moves = this.filterValid(
183 this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
184 );
185 if (moves.length > 0) return true;
dac39588 186 }
d982fffc 187 return false;
dac39588 188 }
a6abf094 189
3a2a7b5f
BA
190 postPlay(move) {
191 super.postPlay(move);
2c5d7b20
BA
192 // Skip castle:
193 if (move.vanish.length == 2 && move.appear.length == 2) return;
dac39588 194 const color = move.appear[0].c;
6808d7a1 195 if (move.vanish.length == 0) {
dac39588
BA
196 this.reserve[color][move.appear[0].p]--;
197 return;
198 }
809ab1a8
BA
199 if (move.vanish.length == 2) {
200 if (V.PawnSpecs.promotions.includes(move.vanish[1].p))
201 this.reserve[color][V.PAWN]++;
202 else this.reserve[color][move.vanish[1].p]++;
203 }
dac39588 204 }
1221ac47 205
3a2a7b5f
BA
206 postUndo(move) {
207 super.postUndo(move);
6808d7a1 208 if (move.vanish.length == 2 && move.appear.length == 2) return;
dac39588 209 const color = this.turn;
6808d7a1 210 if (move.vanish.length == 0) {
dac39588
BA
211 this.reserve[color][move.appear[0].p]++;
212 return;
213 }
809ab1a8
BA
214 if (move.vanish.length == 2) {
215 if (V.PawnSpecs.promotions.includes(move.vanish[1].p))
216 this.reserve[color][V.PAWN]--;
217 else this.reserve[color][move.vanish[1].p]--;
218 }
dac39588 219 }
a6abf094 220
6808d7a1
BA
221 static get SEARCH_DEPTH() {
222 return 2;
78d64531 223 }
a6abf094 224
6808d7a1 225 evalPosition() {
dac39588
BA
226 let evaluation = super.evalPosition();
227 // Add reserves:
6808d7a1 228 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
dac39588
BA
229 const p = V.RESERVE_PIECES[i];
230 evaluation += this.reserve["w"][p] * V.VALUES[p];
231 evaluation -= this.reserve["b"][p] * V.VALUES[p];
232 }
233 return evaluation;
234 }
6752407b 235
6808d7a1
BA
236 getNotation(move) {
237 if (move.vanish.length > 0) return super.getNotation(move);
dac39588 238 // Rebirth:
809ab1a8
BA
239 let piece = move.appear[0].p;
240 if (ChessRules.PIECES.includes(piece)) {
241 if (move.appear[0].p != V.PAWN) piece = move.appear[0].p.toUpperCase();
242 }
243 else piece = V.PromotionMap[piece].toUpperCase();
dac39588
BA
244 return piece + "@" + V.CoordsToSquare(move.end);
245 }
7e8a7ea1 246
6808d7a1 247};