24e8ef29608d44c4c4c728697128219367dd8149
[vchess.git] / client / src / variants / Suicide.js
1 import { ChessRules } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { randInt } from "@/utils/alea";
4
5 export const VariantRules = class SuicideRules extends ChessRules {
6 static get HasFlags() {
7 return false;
8 }
9
10 getPotentialPawnMoves([x, y]) {
11 let moves = super.getPotentialPawnMoves([x, y]);
12
13 // Complete with promotion(s) into king, if possible
14 const color = this.turn;
15 const shift = color == "w" ? -1 : 1;
16 const lastRank = color == "w" ? 0 : V.size.x - 1;
17 if (x + shift == lastRank) {
18 // Normal move
19 if (this.board[x + shift][y] == V.EMPTY)
20 moves.push(
21 this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
22 );
23 // Captures
24 if (
25 y > 0 &&
26 this.canTake([x, y], [x + shift, y - 1]) &&
27 this.board[x + shift][y - 1] != V.EMPTY
28 ) {
29 moves.push(
30 this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
31 );
32 }
33 if (
34 y < V.size.y - 1 &&
35 this.canTake([x, y], [x + shift, y + 1]) &&
36 this.board[x + shift][y + 1] != V.EMPTY
37 ) {
38 moves.push(
39 this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
40 );
41 }
42 }
43
44 return moves;
45 }
46
47 getPotentialKingMoves(sq) {
48 // No castle:
49 return this.getSlideNJumpMoves(
50 sq,
51 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
52 "oneStep"
53 );
54 }
55
56 // Trim all non-capturing moves (not the most efficient, but easy)
57 static KeepCaptures(moves) {
58 return moves.filter(m => m.vanish.length == 2);
59 }
60
61 // Stop at the first capture found (if any)
62 atLeastOneCapture() {
63 const color = this.turn;
64 const oppCol = V.GetOppCol(color);
65 for (let i = 0; i < V.size.x; i++) {
66 for (let j = 0; j < V.size.y; j++) {
67 if (
68 this.board[i][j] != V.EMPTY &&
69 this.getColor(i, j) != oppCol &&
70 this.getPotentialMovesFrom([i, j]).some(m => m.vanish.length == 2)
71 ) {
72 return true;
73 }
74 }
75 }
76 return false;
77 }
78
79 getPossibleMovesFrom(sq) {
80 let moves = this.getPotentialMovesFrom(sq);
81 const captureMoves = V.KeepCaptures(moves);
82 if (captureMoves.length > 0) return captureMoves;
83 if (this.atLeastOneCapture()) return [];
84 return moves;
85 }
86
87 filterValid(moves) {
88 return moves;
89 }
90
91 getAllValidMoves() {
92 const moves = super.getAllValidMoves();
93 if (moves.some(m => m.vanish.length == 2)) return V.KeepCaptures(moves);
94 return moves;
95 }
96
97 atLeastOneMove() {
98 const color = this.turn;
99 for (let i = 0; i < V.size.x; i++) {
100 for (let j = 0; j < V.size.y; j++) {
101 if (
102 this.getColor(i, j) == color &&
103 this.getPotentialMovesFrom([i, j]).length > 0
104 ) {
105 return true;
106 }
107 }
108 }
109 return false;
110 }
111
112 getCheckSquares() {
113 return [];
114 }
115
116 // No variables update because no royal king + no castling
117 prePlay() {}
118 postPlay() {}
119 preUndo() {}
120 postUndo() {}
121
122 getCurrentScore() {
123 if (this.atLeastOneMove()) return "*";
124 // No valid move: the side who cannot move wins
125 return this.turn == "w" ? "1-0" : "0-1";
126 }
127
128 static get VALUES() {
129 return {
130 p: 1,
131 r: 7,
132 n: 3,
133 b: 3,
134 q: 5,
135 k: 5
136 };
137 }
138
139 static get SEARCH_DEPTH() {
140 return 4;
141 }
142
143 evalPosition() {
144 // Less material is better:
145 return -super.evalPosition();
146 }
147
148 static GenRandInitFen(randomness) {
149 if (randomness == 0)
150 return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
151
152 let pieces = { w: new Array(8), b: new Array(8) };
153 // Shuffle pieces on first and last rank
154 for (let c of ["w", "b"]) {
155 if (c == 'b' && randomness == 1) {
156 pieces['b'] = pieces['w'];
157 break;
158 }
159
160 let positions = ArrayFun.range(8);
161
162 // Get random squares for bishops
163 let randIndex = 2 * randInt(4);
164 let bishop1Pos = positions[randIndex];
165 // The second bishop must be on a square of different color
166 let randIndex_tmp = 2 * randInt(4) + 1;
167 let bishop2Pos = positions[randIndex_tmp];
168 // Remove chosen squares
169 positions.splice(Math.max(randIndex, randIndex_tmp), 1);
170 positions.splice(Math.min(randIndex, randIndex_tmp), 1);
171
172 // Get random squares for knights
173 randIndex = randInt(6);
174 let knight1Pos = positions[randIndex];
175 positions.splice(randIndex, 1);
176 randIndex = randInt(5);
177 let knight2Pos = positions[randIndex];
178 positions.splice(randIndex, 1);
179
180 // Get random square for queen
181 randIndex = randInt(4);
182 let queenPos = positions[randIndex];
183 positions.splice(randIndex, 1);
184
185 // Random square for king (no castle)
186 randIndex = randInt(3);
187 let kingPos = positions[randIndex];
188 positions.splice(randIndex, 1);
189
190 // Rooks positions are now fixed
191 let rook1Pos = positions[0];
192 let rook2Pos = positions[1];
193
194 // Finally put the shuffled pieces in the board array
195 pieces[c][rook1Pos] = "r";
196 pieces[c][knight1Pos] = "n";
197 pieces[c][bishop1Pos] = "b";
198 pieces[c][queenPos] = "q";
199 pieces[c][kingPos] = "k";
200 pieces[c][bishop2Pos] = "b";
201 pieces[c][knight2Pos] = "n";
202 pieces[c][rook2Pos] = "r";
203 }
204 return (
205 pieces["b"].join("") +
206 "/pppppppp/8/8/8/8/PPPPPPPP/" +
207 pieces["w"].join("").toUpperCase() +
208 // En-passant allowed, but no flags
209 " w 0 -"
210 );
211 }
212 };