Several small improvements + integrate options + first working draft of Cwda
[vchess.git] / client / src / variants / Parachute.js
1 import { ChessRules, PiPo, Move } from "@/base_rules";
2
3 export class ParachuteRules extends ChessRules {
4
5 static get Options() {
6 // TODO: later, allow variant, placing piece giving check maybe...
7 return null;
8 }
9
10 static get HasFlags() {
11 return false;
12 }
13
14 static IsGoodFen(fen) {
15 if (!ChessRules.IsGoodFen(fen)) return false;
16 const fenParsed = V.ParseFen(fen);
17 // 5) Check reserves
18 if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{12,12}$/))
19 return false;
20 return true;
21 }
22
23 static IsGoodPosition(position) {
24 if (position.length == 0) return false;
25 const rows = position.split("/");
26 if (rows.length != V.size.x) return false;
27 for (let row of rows) {
28 let sumElts = 0;
29 for (let i = 0; i < row.length; i++) {
30 if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
31 else {
32 const num = parseInt(row[i], 10);
33 if (isNaN(num)) return false;
34 sumElts += num;
35 }
36 }
37 if (sumElts != V.size.y) return false;
38 }
39 return true;
40 }
41
42 static ParseFen(fen) {
43 const fenParts = fen.split(" ");
44 return Object.assign(
45 ChessRules.ParseFen(fen),
46 { reserve: fenParts[4] }
47 );
48 }
49
50 static GenRandInitFen() {
51 // ChessRules.PIECES order is P, R, N, B, Q, K:
52 return "8/8/8/8/8/8/8/8 w 0 - 822211822211";
53 }
54
55 getFen() {
56 return super.getFen() + " " + this.getReserveFen();
57 }
58
59 getFenForRepeat() {
60 return super.getFenForRepeat() + "_" + this.getReserveFen();
61 }
62
63 getReserveFen() {
64 let counts = new Array(12);
65 for (let i = 0; i < V.PIECES.length; i++) {
66 counts[i] = this.reserve["w"][V.PIECES[i]];
67 counts[6 + i] = this.reserve["b"][V.PIECES[i]];
68 }
69 return counts.join("");
70 }
71
72 setOtherVariables(fen) {
73 super.setOtherVariables(fen);
74 // Also init reserves (used by the interface to show landable pieces)
75 const reserve =
76 V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
77 this.reserve = {
78 w: {
79 [V.PAWN]: reserve[0],
80 [V.ROOK]: reserve[1],
81 [V.KNIGHT]: reserve[2],
82 [V.BISHOP]: reserve[3],
83 [V.QUEEN]: reserve[4],
84 [V.KING]: reserve[5]
85 },
86 b: {
87 [V.PAWN]: reserve[6],
88 [V.ROOK]: reserve[7],
89 [V.KNIGHT]: reserve[8],
90 [V.BISHOP]: reserve[9],
91 [V.QUEEN]: reserve[10],
92 [V.KING]: reserve[11]
93 }
94 };
95 }
96
97 getColor(i, j) {
98 if (i >= V.size.x) return i == V.size.x ? "w" : "b";
99 return this.board[i][j].charAt(0);
100 }
101
102 getPiece(i, j) {
103 if (i >= V.size.x) return V.RESERVE_PIECES[j];
104 return this.board[i][j].charAt(1);
105 }
106
107 // Used by the interface:
108 getReservePpath(index, color) {
109 return color + V.RESERVE_PIECES[index];
110 }
111
112 // Ordering on reserve pieces (matching V.PIECES order)
113 static get RESERVE_PIECES() {
114 return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING];
115 }
116
117 getReserveMoves([x, y]) {
118 const color = this.turn;
119 const oppCol = V.GetOppCol(color);
120 const p = V.RESERVE_PIECES[y];
121 if (this.reserve[color][p] == 0) return [];
122 let moves = [];
123 let boundary =
124 p == V.PAWN
125 // Pawns can land only on 4 first ranks:
126 ? (color == 'w' ? [4, 8] : [0, 4])
127 : [0, 8];
128 for (let i = boundary[0]; i < boundary[1]; i++) {
129 for (let j = 0; j < 8; j++) {
130 if (this.board[i][j] == V.EMPTY) {
131 let mv = new Move({
132 appear: [
133 new PiPo({
134 x: i,
135 y: j,
136 c: color,
137 p: p
138 })
139 ],
140 vanish: [],
141 start: { x: x, y: y }, //a bit artificial...
142 end: { x: i, y: j }
143 });
144 this.play(mv);
145 // Landing with check is forbidden:
146 if (!this.underCheck(oppCol)) moves.push(mv);
147 this.undo(mv);
148 }
149 }
150 }
151 return moves;
152 }
153
154 getPotentialMovesFrom([x, y]) {
155 let moves =
156 x >= 8
157 ? this.getReserveMoves([x, y])
158 : super.getPotentialMovesFrom([x, y]);
159 // Forbid captures if king not landed yet:
160 if (x < 8 && moves.length > 0 && this.kingPos[moves[0].appear[0].c][0] < 0)
161 moves = moves.filter(m => m.vanish.length == 1);
162 return moves;
163 }
164
165 getAllValidMoves() {
166 let moves = super.getAllValidMoves();
167 const color = this.turn;
168 for (let i = 0; i < V.RESERVE_PIECES.length; i++)
169 moves = moves.concat(
170 this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
171 );
172 return this.filterValid(moves);
173 }
174
175 isAttacked(sq, color) {
176 // While the king hasn't landed, nothing is attacked:
177 if (this.kingPos[color][0] < 0) return false;
178 return super.isAttacked(sq, color);
179 }
180
181 atLeastOneMove() {
182 if (!super.atLeastOneMove()) {
183 // Search one reserve move
184 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
185 let moves = this.filterValid(
186 this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
187 );
188 if (moves.length > 0) return true;
189 }
190 return false;
191 }
192 return true;
193 }
194
195 underCheck(color) {
196 if (this.kingPos[color][0] < 0)
197 // A king outside the board isn't under check
198 return false;
199 return this.isAttacked(this.kingPos[color], V.GetOppCol(color));
200 }
201
202 prePlay(move) {
203 super.prePlay(move);
204 if (move.vanish.length == 0) this.reserve[this.turn][move.appear[0].p]--;
205 }
206
207 postUndo(move) {
208 if (move.vanish.length == 0) this.reserve[this.turn][move.appear[0].p]++;
209 // (Potentially) Reset king position
210 if (move.appear[0].p == V.KING) {
211 const c = move.appear[0].c;
212 if (move.vanish.length == 0)
213 // Landing king
214 this.kingPos[c] = [-1, -1];
215 else
216 // King movement
217 this.kingPos[c] = [move.start.x, move.start.y];
218 }
219 }
220
221 static get SEARCH_DEPTH() {
222 return 1;
223 }
224
225 evalPosition() {
226 let evaluation = super.evalPosition();
227 // Add reserves:
228 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
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 }
235
236 getNotation(move) {
237 if (move.vanish.length > 0) return super.getNotation(move);
238 // Parachutage:
239 const piece =
240 move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
241 return piece + "@" + V.CoordsToSquare(move.end);
242 }
243
244 };