Several small improvements + integrate options + first working draft of Cwda
[vchess.git] / client / src / variants / Grand.js
CommitLineData
0c3fe8a6
BA
1import { ChessRules } from "@/base_rules";
2import { ArrayFun } from "@/utils/array";
3import { randInt } from "@/utils/alea";
4
32f6285e 5export class GrandRules extends ChessRules {
7e8a7ea1 6
63543a13 7 static get HasFlags() {
0fb43db7
BA
8 return false;
9 }
10
6808d7a1 11 static IsGoodEnpassant(enpassant) {
472c0c4f 12 if (enpassant != "-") return !!enpassant.match(/^([a-j][0-9]{1,2},?)+$/);
dac39588
BA
13 return true;
14 }
15
241bf8f2
BA
16 getPpath(b) {
17 return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
18 }
19
6808d7a1
BA
20 static get size() {
21 return { x: 10, y: 10 };
22 }
dac39588 23
a6836242 24 // Rook + knight:
6808d7a1
BA
25 static get MARSHALL() {
26 return "m";
a6836242
BA
27 }
28
29 // Bishop + knight
6808d7a1
BA
30 static get CARDINAL() {
31 return "c";
a6836242 32 }
dac39588 33
6808d7a1
BA
34 static get PIECES() {
35 return ChessRules.PIECES.concat([V.MARSHALL, V.CARDINAL]);
dac39588
BA
36 }
37
6808d7a1
BA
38 getPotentialMovesFrom([x, y]) {
39 switch (this.getPiece(x, y)) {
dac39588 40 case V.MARSHALL:
6808d7a1 41 return this.getPotentialMarshallMoves([x, y]);
dac39588 42 case V.CARDINAL:
6808d7a1 43 return this.getPotentialCardinalMoves([x, y]);
dac39588 44 default:
6808d7a1 45 return super.getPotentialMovesFrom([x, y]);
dac39588
BA
46 }
47 }
48
49 // Special pawn rules: promotions to captured friendly pieces,
50 // optional on ranks 8-9 and mandatory on rank 10.
6808d7a1 51 getPotentialPawnMoves([x, y]) {
dac39588
BA
52 const color = this.turn;
53 let moves = [];
6808d7a1
BA
54 const [sizeX, sizeY] = [V.size.x, V.size.y];
55 const shiftX = color == "w" ? -1 : 1;
0fb43db7 56 const startRank = (color == "w" ? sizeX - 3 : 2);
6808d7a1
BA
57 const lastRanks =
58 color == "w" ? [0, 1, 2] : [sizeX - 1, sizeX - 2, sizeX - 3];
dac39588 59 // Always x+shiftX >= 0 && x+shiftX < sizeX, because no pawns on last rank
63543a13 60 let finalPieces = [V.PAWN];
6808d7a1 61 if (lastRanks.includes(x + shiftX)) {
63543a13
BA
62 // Determine which promotion pieces are available:
63 let promotionPieces = {
64 [V.ROOK]: 2,
65 [V.KNIGHT]: 2,
66 [V.BISHOP]: 2,
67 [V.QUEEN]: 1,
68 [V.MARSHALL]: 1,
69 [V.CARDINAL]: 1
70 };
71 for (let i=0; i<10; i++) {
72 for (let j=0; j<10; j++) {
73 if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color) {
74 const p = this.getPiece(i, j);
75 if (![V.PAWN, V.KING].includes(p)) promotionPieces[p]--;
76 }
77 }
78 }
79 const availablePieces =
80 Object.keys(promotionPieces).filter(k => promotionPieces[k] > 0);
81 if (x + shiftX == lastRanks[0]) finalPieces = availablePieces;
82 else Array.prototype.push.apply(finalPieces, availablePieces);
0fb43db7 83 }
6808d7a1 84 if (this.board[x + shiftX][y] == V.EMPTY) {
dac39588
BA
85 // One square forward
86 for (let piece of finalPieces)
6808d7a1
BA
87 moves.push(
88 this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
89 );
0fb43db7
BA
90 if (x == startRank && this.board[x + 2 * shiftX][y] == V.EMPTY)
91 // Two squares jump
92 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
dac39588
BA
93 }
94 // Captures
6808d7a1
BA
95 for (let shiftY of [-1, 1]) {
96 if (
97 y + shiftY >= 0 &&
98 y + shiftY < sizeY &&
99 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
100 this.canTake([x, y], [x + shiftX, y + shiftY])
101 ) {
102 for (let piece of finalPieces) {
103 moves.push(
104 this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
105 c: color,
106 p: piece
107 })
108 );
dac39588
BA
109 }
110 }
111 }
112
113 // En passant
0fb43db7
BA
114 Array.prototype.push.apply(
115 moves,
116 this.getEnpassantCaptures([x, y], shiftX)
117 );
dac39588
BA
118
119 return moves;
120 }
121
6808d7a1 122 getPotentialMarshallMoves(sq) {
dac39588 123 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
4313762d 124 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], 1)
6808d7a1 125 );
dac39588
BA
126 }
127
6808d7a1 128 getPotentialCardinalMoves(sq) {
dac39588 129 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
4313762d 130 this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], 1)
6808d7a1 131 );
dac39588
BA
132 }
133
68e19a44 134 isAttacked(sq, color) {
6808d7a1 135 return (
68e19a44
BA
136 super.isAttacked(sq, color) ||
137 this.isAttackedByMarshall(sq, color) ||
138 this.isAttackedByCardinal(sq, color)
6808d7a1 139 );
dac39588
BA
140 }
141
68e19a44 142 isAttackedByMarshall(sq, color) {
6808d7a1 143 return (
68e19a44 144 this.isAttackedBySlideNJump(sq, color, V.MARSHALL, V.steps[V.ROOK]) ||
4313762d 145 this.isAttackedBySlideNJump(sq, color, V.MARSHALL, V.steps[V.KNIGHT], 1)
6808d7a1 146 );
dac39588
BA
147 }
148
68e19a44 149 isAttackedByCardinal(sq, color) {
6808d7a1 150 return (
68e19a44 151 this.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) ||
4313762d 152 this.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.KNIGHT], 1)
6808d7a1 153 );
dac39588
BA
154 }
155
6808d7a1 156 static get VALUES() {
dac39588 157 return Object.assign(
a97bdbda
BA
158 { c: 5, m: 7 }, //experimental
159 ChessRules.VALUES
dac39588
BA
160 );
161 }
162
6808d7a1
BA
163 static get SEARCH_DEPTH() {
164 return 2;
165 }
dac39588 166
4313762d
BA
167 static GenRandInitFen(options) {
168 if (options.randomness == 0) {
2c5d7b20
BA
169 return (
170 "r8r/1nbqkmcbn1/pppppppppp/91/91/91/91/PPPPPPPPPP/1NBQKMCBN1/R8R " +
63543a13 171 "w 0 -"
2c5d7b20 172 );
7ba4a5bc
BA
173 }
174
0fb43db7
BA
175 let pieces = { w: new Array(8), b: new Array(8) };
176 // Shuffle pieces on second and before-last rank
6808d7a1 177 for (let c of ["w", "b"]) {
4313762d 178 if (c == 'b' && options.randomness == 1) {
7ba4a5bc
BA
179 pieces['b'] = pieces['w'];
180 break;
181 }
182
0fb43db7 183 let positions = ArrayFun.range(8);
dac39588
BA
184
185 // Get random squares for bishops
0fb43db7 186 let randIndex = 2 * randInt(4);
3208c667 187 const bishop1Pos = positions[randIndex];
dac39588 188 // The second bishop must be on a square of different color
0fb43db7 189 let randIndex_tmp = 2 * randInt(4) + 1;
3208c667 190 const bishop2Pos = positions[randIndex_tmp];
dac39588 191 // Remove chosen squares
6808d7a1
BA
192 positions.splice(Math.max(randIndex, randIndex_tmp), 1);
193 positions.splice(Math.min(randIndex, randIndex_tmp), 1);
dac39588
BA
194
195 // Get random squares for knights
0fb43db7 196 randIndex = randInt(6);
3208c667 197 const knight1Pos = positions[randIndex];
dac39588 198 positions.splice(randIndex, 1);
0fb43db7 199 randIndex = randInt(5);
3208c667 200 const knight2Pos = positions[randIndex];
dac39588
BA
201 positions.splice(randIndex, 1);
202
203 // Get random square for queen
0fb43db7 204 randIndex = randInt(4);
3208c667 205 const queenPos = positions[randIndex];
dac39588
BA
206 positions.splice(randIndex, 1);
207
208 // ...random square for marshall
0fb43db7 209 randIndex = randInt(3);
3208c667 210 const marshallPos = positions[randIndex];
dac39588
BA
211 positions.splice(randIndex, 1);
212
213 // ...random square for cardinal
0fb43db7 214 randIndex = randInt(2);
3208c667 215 const cardinalPos = positions[randIndex];
dac39588
BA
216 positions.splice(randIndex, 1);
217
0fb43db7 218 // King position is now fixed,
3208c667 219 const kingPos = positions[0];
dac39588
BA
220
221 // Finally put the shuffled pieces in the board array
6808d7a1
BA
222 pieces[c][knight1Pos] = "n";
223 pieces[c][bishop1Pos] = "b";
224 pieces[c][queenPos] = "q";
225 pieces[c][marshallPos] = "m";
226 pieces[c][cardinalPos] = "c";
227 pieces[c][kingPos] = "k";
228 pieces[c][bishop2Pos] = "b";
229 pieces[c][knight2Pos] = "n";
dac39588 230 }
6808d7a1 231 return (
0fb43db7
BA
232 "r8r/1" + pieces["b"].join("") + "1/" +
233 "pppppppppp/91/91/91/91/PPPPPPPPPP/" +
234 "1" + pieces["w"].join("").toUpperCase() + "1/R8R" +
63543a13 235 " w 0 -"
6808d7a1 236 );
dac39588 237 }
7e8a7ea1 238
6808d7a1 239};