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