954a050f34f9e82cb34394478bc91b4b2d5d237e
[vchess.git] / client / src / variants / Monochrome.js
1 import { ChessRules } from "@/base_rules";
2
3 export class MonochromeRules extends ChessRules {
4 static get HasEnpassant() {
5 // Pawns would be on the same side
6 return false;
7 }
8
9 static get HasFlags() {
10 return false;
11 }
12
13 static get Lines() {
14 return [ [[4, 0], [4, 8]] ];
15 }
16
17 get showFirstTurn() {
18 return true;
19 }
20
21 static IsGoodPosition(position) {
22 if (position.length == 0) return false;
23 const rows = position.split("/");
24 if (rows.length != V.size.x) return false;
25 for (let row of rows) {
26 let sumElts = 0;
27 for (let i = 0; i < row.length; i++) {
28 if (V.PIECES.includes(row[i])) sumElts++;
29 else {
30 const num = parseInt(row[i], 10);
31 if (isNaN(num)) return false;
32 sumElts += num;
33 }
34 }
35 if (sumElts != V.size.y) return false;
36 }
37 return true;
38 }
39
40 canIplay(side, [x, y]) {
41 const xBounds = side == 'w' ? [4,7] : [0,3];
42 return this.turn == side && x >= xBounds[0] && x <= xBounds[1];
43 }
44
45 canTake([x1, y1], [x2, y2]) {
46 // Capture in other half-board
47 return ((x1 <= 3 && x2 >= 4) || (x1 >= 4 && x2 <= 3));
48 }
49
50 // follow steps from x,y until something is met.
51 // if met piece is opponent and same movement (asA): eat it!
52 findCaptures_aux([x, y], asA) {
53 let moves = [];
54 const steps =
55 asA != V.PAWN
56 ? [V.QUEEN, V.KING].includes(asA)
57 ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
58 : V.steps[asA]
59 : this.turn == "w"
60 ? [
61 [-1, -1],
62 [-1, 1]
63 ]
64 : [
65 [1, -1],
66 [1, 1]
67 ];
68 const oneStep = [V.KNIGHT, V.PAWN, V.KING].includes(asA);
69 outerLoop: for (let loop = 0; loop < steps.length; loop++) {
70 const step = steps[loop];
71 let i = x + step[0];
72 let j = y + step[1];
73 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
74 if (oneStep) continue outerLoop;
75 i += step[0];
76 j += step[1];
77 }
78 if (
79 V.OnBoard(i, j) &&
80 this.getPiece(i, j) == asA &&
81 this.canTake([i, j], [x, y])
82 ) {
83 // eat!
84 moves.push(this.getBasicMove([x, y], [i, j]));
85 }
86 }
87 return moves;
88 }
89
90 // Find possible captures from a square: look in every direction!
91 findCaptures(sq) {
92 let moves = [];
93 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.PAWN));
94 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.ROOK));
95 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.KNIGHT));
96 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.BISHOP));
97 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.QUEEN));
98 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.KING));
99 return moves;
100 }
101
102 // Trim all non-capturing moves
103 static KeepCaptures(moves) {
104 return moves.filter(m => m.vanish.length == 2);
105 }
106
107 getPotentialMovesFrom(sq) {
108 const moves = super.getPotentialMovesFrom(sq);
109 const zenCaptures = this.findCaptures(sq);
110 // Remove duplicate captures in a lazy way (TODO: more efficient...)
111 let hashMoves = {};
112 moves.forEach(m => {
113 if (m.vanish.length == 2) {
114 const hash =
115 m.start.x + "." + m.start.y + "." + m.end.x + "." + m.end.y;
116 hashMoves[hash] = true;
117 }
118 });
119 return moves.concat(zenCaptures.filter(m => {
120 const hash = m.start.x + "." + m.start.y + "." + m.end.x + "." + m.end.y;
121 return !hashMoves[hash];
122 }));
123 }
124
125 getAllPotentialMoves() {
126 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
127 let potentialMoves = [];
128 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
129 for (let j = 0; j < V.size.y; j++) {
130 if (this.board[i][j] != V.EMPTY) {
131 Array.prototype.push.apply(
132 potentialMoves,
133 this.getPotentialMovesFrom([i, j])
134 );
135 }
136 }
137 }
138 if (potentialMoves.some(m => m.vanish.length == 2 && m.appear.length == 1))
139 return V.KeepCaptures(potentialMoves);
140 return potentialMoves;
141 }
142
143 atLeastOneMove() {
144 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
145 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
146 for (let j = 0; j < V.size.y; j++) {
147 if (
148 this.board[i][j] != V.EMPTY &&
149 this.getPotentialMovesFrom([i, j]).length > 0
150 ) {
151 return true;
152 }
153 }
154 }
155 return false;
156 }
157
158 // Stop at the first capture found (if any)
159 atLeastOneCapture() {
160 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
161 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
162 for (let j = 0; j < V.size.y; j++) {
163 if (
164 this.board[i][j] != V.EMPTY &&
165 this.getPotentialMovesFrom([i, j]).some(m => m.vanish.length == 2)
166 ) {
167 return true;
168 }
169 }
170 }
171 return false;
172 }
173
174 getPossibleMovesFrom(sq) {
175 let moves = this.getPotentialMovesFrom(sq);
176 const captureMoves = V.KeepCaptures(moves);
177 if (captureMoves.length > 0) return captureMoves;
178 if (this.atLeastOneCapture()) return [];
179 return moves;
180 }
181
182 filterValid(moves) {
183 return moves;
184 }
185
186 isAttacked() {
187 return false;
188 }
189
190 getCheckSquares() {
191 return [];
192 }
193
194 getCurrentScore() {
195 // Is there anything in opponent's half board?
196 const color = V.GetOppCol(this.turn);
197 const xBounds = color == 'w' ? [4,7] : [0,3];
198 let nothingHere = true;
199 outerLoop: for (let i = xBounds[0]; i <= xBounds[1]; i++) {
200 for (let j = 0; j < V.size.y; j++) {
201 if (this.board[i][j] != V.EMPTY) {
202 nothingHere = false;
203 break outerLoop;
204 }
205 }
206 }
207 if (nothingHere) return color == 'w' ? "0-1" : "1-0";
208 if (this.atLeastOneMove()) return '*';
209 return "1/2";
210 }
211
212 static GenRandInitFen(randomness) {
213 // Remove the en-passant + castle part of the FEN
214 const fen = ChessRules.GenRandInitFen(randomness).slice(0, -6);
215 const firstSpace = fen.indexOf(' ');
216 return (
217 fen.substr(0, firstSpace).replace(/[A-Z]/g, (c) => c.toLowerCase()) +
218 fen.substr(firstSpace)
219 );
220 }
221
222 static get SEARCH_DEPTH() {
223 return 4;
224 }
225
226 evalPosition() {
227 let evaluation = 0;
228 for (let i = 0; i < 8; i++) {
229 for (let j = 0; j < V.size.y; j++) {
230 if (this.board[i][j] != V.EMPTY) {
231 const sign = (i <= 3 ? -1 : 1);
232 // I don't think taking pieces' values into account would help
233 evaluation += sign; //* V.VALUES[this.getPiece(i, j)];
234 }
235 }
236 }
237 return evaluation;
238 }
239
240 getNotation(move) {
241 // Translate initial square (because pieces may fly unusually!)
242 const initialSquare = V.CoordsToSquare(move.start);
243
244 // Translate final square
245 const finalSquare = V.CoordsToSquare(move.end);
246
247 let notation = "";
248 const piece = this.getPiece(move.start.x, move.start.y);
249 if (piece == V.PAWN) {
250 // pawn move (TODO: enPassant indication)
251 if (move.vanish.length == 2) {
252 // capture
253 notation = initialSquare + "x" + finalSquare;
254 }
255 else notation = finalSquare;
256 if (piece != move.appear[0].p)
257 //promotion
258 notation += "=" + move.appear[0].p.toUpperCase();
259 }
260 else {
261 // Piece movement
262 notation = piece.toUpperCase();
263 if (move.vanish.length > 1) notation += initialSquare + "x";
264 notation += finalSquare;
265 }
266 return notation;
267 }
268 };