Fix Progressive2. Fixing attempt on Doublemove1
[vchess.git] / client / src / variants / Colorbound.js
1 import { ChessRules, Move, PiPo } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { randInt } from "@/utils/alea";
4
5 export class ColorboundRules extends ChessRules {
6 static get PawnSpecs() {
7 return Object.assign(
8 {},
9 ChessRules.PawnSpecs,
10 {
11 promotions:
12 ChessRules.PawnSpecs.promotions.concat(
13 [V.C_ROOK, V.C_KNIGHT, V.C_BISHOP, V.C_QUEEN])
14 }
15 );
16 }
17
18 getPpath(b) {
19 if ([V.C_ROOK, V.C_KNIGHT, V.C_BISHOP, V.C_QUEEN].includes(b[1]))
20 return "Colorbound/" + b;
21 return b;
22 }
23
24 static GenRandInitFen(randomness) {
25 if (randomness == 0)
26 return "dhaskahd/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -";
27
28 // Mapping white --> black (at least at start):
29 const piecesMap = {
30 'r': 'd',
31 'n': 'h',
32 'b': 'a',
33 'q': 's',
34 'k': 'k'
35 };
36
37 let pieces = { w: new Array(8), b: new Array(8) };
38 let flags = "";
39 // Shuffle pieces on first (and last rank if randomness == 2)
40 for (let c of ["w", "b"]) {
41 if (c == 'b' && randomness == 1) {
42 pieces['b'] = pieces['w'].map(p => piecesMap[p]);
43 flags += flags;
44 break;
45 }
46
47 // TODO: same code as in base_rules. Should extract and factorize?
48
49 let positions = ArrayFun.range(8);
50
51 let randIndex = 2 * randInt(4);
52 const bishop1Pos = positions[randIndex];
53 let randIndex_tmp = 2 * randInt(4) + 1;
54 const bishop2Pos = positions[randIndex_tmp];
55 positions.splice(Math.max(randIndex, randIndex_tmp), 1);
56 positions.splice(Math.min(randIndex, randIndex_tmp), 1);
57
58 randIndex = randInt(6);
59 const knight1Pos = positions[randIndex];
60 positions.splice(randIndex, 1);
61 randIndex = randInt(5);
62 const knight2Pos = positions[randIndex];
63 positions.splice(randIndex, 1);
64
65 randIndex = randInt(4);
66 const queenPos = positions[randIndex];
67 positions.splice(randIndex, 1);
68
69 const rook1Pos = positions[0];
70 const kingPos = positions[1];
71 const rook2Pos = positions[2];
72
73 pieces[c][rook1Pos] = "r";
74 pieces[c][knight1Pos] = "n";
75 pieces[c][bishop1Pos] = "b";
76 pieces[c][queenPos] = "q";
77 pieces[c][kingPos] = "k";
78 pieces[c][bishop2Pos] = "b";
79 pieces[c][knight2Pos] = "n";
80 pieces[c][rook2Pos] = "r";
81 if (c == 'b') pieces[c] = pieces[c].map(p => piecesMap[p]);
82 flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos);
83 }
84 // Add turn + flags + enpassant
85 return (
86 pieces["b"].join("") +
87 "/pppppppp/8/8/8/8/PPPPPPPP/" +
88 pieces["w"].join("").toUpperCase() +
89 " w 0 " + flags + " -"
90 );
91 }
92
93 static get C_ROOK() {
94 return 'd';
95 }
96 static get C_KNIGHT() {
97 return 'h';
98 }
99 static get C_BISHOP() {
100 return 'a';
101 }
102 static get C_QUEEN() {
103 return 's';
104 }
105
106 static get PIECES() {
107 return (
108 ChessRules.PIECES.concat([V.C_ROOK, V.C_KNIGHT, V.C_BISHOP, V.C_QUEEN])
109 );
110 }
111
112 getPotentialMovesFrom([x, y]) {
113 switch (this.getPiece(x, y)) {
114 case V.C_ROOK:
115 return this.getPotentialC_rookMoves([x, y]);
116 case V.C_KNIGHT:
117 return this.getPotentialC_knightMoves([x, y]);
118 case V.C_BISHOP:
119 return this.getPotentialC_bishopMoves([x, y]);
120 case V.C_QUEEN:
121 return this.getPotentialC_queenMoves([x, y]);
122 default:
123 return super.getPotentialMovesFrom([x, y]);
124 }
125 return [];
126 }
127
128 static get steps() {
129 return Object.assign(
130 {},
131 ChessRules.steps,
132 {
133 // Dabbabah
134 'd': [
135 [-2, 0],
136 [0, -2],
137 [2, 0],
138 [0, 2]
139 ],
140 // Alfil
141 'a': [
142 [2, 2],
143 [2, -2],
144 [-2, 2],
145 [-2, -2]
146 ],
147 // Ferz
148 'f': [
149 [1, 1],
150 [1, -1],
151 [-1, 1],
152 [-1, -1]
153 ]
154 }
155 );
156 }
157
158 getPotentialC_rookMoves(sq) {
159 return (
160 this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
161 this.getSlideNJumpMoves(sq, V.steps['d'], "oneStep"))
162 );
163 }
164
165 getPotentialC_knightMoves(sq) {
166 return (
167 this.getSlideNJumpMoves(sq, V.steps['a'], "oneStep").concat(
168 this.getSlideNJumpMoves(sq, V.steps[V.ROOK], "oneStep"))
169 );
170 }
171
172 getPotentialC_bishopMoves(sq) {
173 return (
174 this.getSlideNJumpMoves(sq, V.steps['d'], "oneStep").concat(
175 this.getSlideNJumpMoves(sq, V.steps['a'], "oneStep")).concat(
176 this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], "oneStep"))
177 );
178 }
179
180 getPotentialC_queenMoves(sq) {
181 return (
182 this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
183 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"))
184 );
185 }
186
187 // TODO: really find a way to avoid duolicating most of the castling code
188 // each time: here just the queenside castling squares change for black.
189 getCastleMoves([x, y]) {
190 const c = this.getColor(x, y);
191 if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
192 return [];
193
194 const oppCol = V.GetOppCol(c);
195 let moves = [];
196 let i = 0;
197 // King, then rook:
198 const finalSquares = [
199 // Black castle long in an unusual way:
200 (c == 'w' ? [2, 3] : [1, 2]),
201 [V.size.y - 2, V.size.y - 3]
202 ];
203 castlingCheck: for (
204 let castleSide = 0;
205 castleSide < 2;
206 castleSide++ //large, then small
207 ) {
208 if (this.castleFlags[c][castleSide] >= V.size.y) continue;
209
210 const rookPos = this.castleFlags[c][castleSide];
211 const castlingPiece = this.getPiece(x, rookPos);
212 const finDist = finalSquares[castleSide][0] - y;
213 let step = finDist / Math.max(1, Math.abs(finDist));
214 i = y;
215 do {
216 if (
217 this.isAttacked([x, i], oppCol) ||
218 (this.board[x][i] != V.EMPTY &&
219 (this.getColor(x, i) != c ||
220 ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
221 ) {
222 continue castlingCheck;
223 }
224 i += step;
225 } while (i != finalSquares[castleSide][0]);
226
227 step = castleSide == 0 ? -1 : 1;
228 for (i = y + step; i != rookPos; i += step) {
229 if (this.board[x][i] != V.EMPTY) continue castlingCheck;
230 }
231
232 for (i = 0; i < 2; i++) {
233 if (
234 finalSquares[castleSide][i] != rookPos &&
235 this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
236 (
237 this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
238 this.getColor(x, finalSquares[castleSide][i]) != c
239 )
240 ) {
241 continue castlingCheck;
242 }
243 }
244
245 moves.push(
246 new Move({
247 appear: [
248 new PiPo({
249 x: x,
250 y: finalSquares[castleSide][0],
251 p: V.KING,
252 c: c
253 }),
254 new PiPo({
255 x: x,
256 y: finalSquares[castleSide][1],
257 p: castlingPiece,
258 c: c
259 })
260 ],
261 vanish: [
262 new PiPo({ x: x, y: y, p: V.KING, c: c }),
263 new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
264 ],
265 end:
266 Math.abs(y - rookPos) <= 2
267 ? { x: x, y: rookPos }
268 : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
269 })
270 );
271 }
272
273 return moves;
274 }
275
276 isAttacked(sq, color) {
277 return (
278 super.isAttacked(sq, color) ||
279 this.isAttackedByC_rook(sq, color) ||
280 this.isAttackedByC_knight(sq, color) ||
281 this.isAttackedByC_bishop(sq, color) ||
282 this.isAttackedByC_queen(sq, color)
283 );
284 }
285
286 isAttackedByC_rook(sq, color) {
287 return (
288 this.isAttackedBySlideNJump(sq, color, V.C_ROOK, V.steps[V.BISHOP]) ||
289 this.isAttackedBySlideNJump(
290 sq, color, V.C_ROOK, V.steps['d'], "oneStep")
291 );
292 }
293
294 isAttackedByC_knight(sq, color) {
295 return (
296 this.isAttackedBySlideNJump(
297 sq, color, V.C_KNIGHT, V.steps[V.ROOK], "oneStep") ||
298 this.isAttackedBySlideNJump(
299 sq, color, V.C_KNIGHT, V.steps['a'], "oneStep")
300 );
301 }
302
303 isAttackedByC_bishop(sq, color) {
304 return (
305 this.isAttackedBySlideNJump(
306 sq, color, V.C_BISHOP, V.steps['d'], "oneStep") ||
307 this.isAttackedBySlideNJump(
308 sq, color, V.C_BISHOP, V.steps['a'], "oneStep") ||
309 this.isAttackedBySlideNJump(
310 sq, color, V.C_BISHOP, V.steps['f'], "oneStep")
311 );
312 }
313
314 isAttackedByC_queen(sq, color) {
315 return (
316 this.isAttackedBySlideNJump(sq, color, V.C_QUEEN, V.steps[V.BISHOP]) ||
317 this.isAttackedBySlideNJump(
318 sq, color, V.C_QUEEN, V.steps[V.KNIGHT], "oneStep")
319 );
320 }
321
322 static get VALUES() {
323 return Object.assign(
324 {},
325 ChessRules.VALUES,
326 {
327 d: 4,
328 h: 3,
329 a: 5,
330 s: 6
331 }
332 );
333 }
334
335 static get SEARCH_DEPTH() {
336 return 2;
337 }
338 };