Improve(?) Monochrome + simplify Zen variant
[vchess.git] / client / src / variants / Monochrome.js
CommitLineData
665eed90
BA
1import { ChessRules } from "@/base_rules";
2
3export class MonochromeRules extends ChessRules {
4 static get HasEnpassant() {
5 // Pawns would be on the same side
6 return false;
7 }
8
8ca6042e
BA
9 static get HasFlags() {
10 return false;
11 }
12
ad030c7d
BA
13 static get Lines() {
14 return [ [[4, 0], [4, 8]] ];
15 }
16
5246b49d
BA
17 get showFirstTurn() {
18 return true;
19 }
20
665eed90
BA
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 {
e50a8025 30 const num = parseInt(row[i], 10);
665eed90
BA
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
8ca6042e
BA
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
665eed90
BA
102 // Trim all non-capturing moves
103 static KeepCaptures(moves) {
8ca6042e
BA
104 return moves.filter(m => m.vanish.length == 2);
105 }
106
107 getPotentialMovesFrom(sq) {
108 return super.getPotentialMovesFrom(sq).concat(this.findCaptures(sq));
665eed90
BA
109 }
110
111 getAllPotentialMoves() {
112 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
113 let potentialMoves = [];
114 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
115 for (let j = 0; j < V.size.y; j++) {
116 if (this.board[i][j] != V.EMPTY) {
117 Array.prototype.push.apply(
118 potentialMoves,
119 this.getPotentialMovesFrom([i, j])
120 );
121 }
122 }
123 }
124 if (potentialMoves.some(m => m.vanish.length == 2 && m.appear.length == 1))
125 return V.KeepCaptures(potentialMoves);
126 return potentialMoves;
127 }
128
129 atLeastOneMove() {
130 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
131 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
132 for (let j = 0; j < V.size.y; j++) {
133 if (
134 this.board[i][j] != V.EMPTY &&
135 this.getPotentialMovesFrom([i, j]).length > 0
136 ) {
137 return true;
138 }
139 }
140 }
141 return false;
142 }
143
144 // Stop at the first capture found (if any)
145 atLeastOneCapture() {
146 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
147 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
148 for (let j = 0; j < V.size.y; j++) {
149 if (
150 this.board[i][j] != V.EMPTY &&
8ca6042e 151 this.getPotentialMovesFrom([i, j]).some(m => m.vanish.length == 2)
665eed90
BA
152 ) {
153 return true;
154 }
155 }
156 }
157 return false;
158 }
159
160 getPossibleMovesFrom(sq) {
161 let moves = this.getPotentialMovesFrom(sq);
162 const captureMoves = V.KeepCaptures(moves);
163 if (captureMoves.length > 0) return captureMoves;
164 if (this.atLeastOneCapture()) return [];
165 return moves;
166 }
167
168 filterValid(moves) {
169 return moves;
170 }
171
172 isAttacked() {
173 return false;
174 }
175
176 getCheckSquares() {
177 return [];
178 }
179
180 getCurrentScore() {
8ca6042e 181 // Is there anything in opponent's half board?
665eed90
BA
182 const color = V.GetOppCol(this.turn);
183 const xBounds = color == 'w' ? [4,7] : [0,3];
184 let nothingHere = true;
185 outerLoop: for (let i = xBounds[0]; i <= xBounds[1]; i++) {
186 for (let j = 0; j < V.size.y; j++) {
187 if (this.board[i][j] != V.EMPTY) {
188 nothingHere = false;
189 break outerLoop;
190 }
191 }
192 }
193 if (nothingHere) return color == 'w' ? "0-1" : "1-0";
194 if (this.atLeastOneMove()) return '*';
195 return "1/2";
196 }
197
198 static GenRandInitFen(randomness) {
8ca6042e
BA
199 // Remove the en-passant + castle part of the FEN
200 const fen = ChessRules.GenRandInitFen(randomness).slice(0, -6);
665eed90
BA
201 const firstSpace = fen.indexOf(' ');
202 return (
203 fen.substr(0, firstSpace).replace(/[A-Z]/g, (c) => c.toLowerCase()) +
204 fen.substr(firstSpace)
205 );
206 }
207
208 static get SEARCH_DEPTH() {
209 return 4;
210 }
211
212 evalPosition() {
213 let evaluation = 0;
214 for (let i = 0; i < 8; i++) {
215 for (let j = 0; j < V.size.y; j++) {
216 if (this.board[i][j] != V.EMPTY) {
217 const sign = (i <= 3 ? -1 : 1);
218 // I don't think taking pieces' values into account would help
219 evaluation += sign; //* V.VALUES[this.getPiece(i, j)];
220 }
221 }
222 }
223 return evaluation;
224 }
8ca6042e
BA
225
226 getNotation(move) {
227 // Translate initial square (because pieces may fly unusually!)
228 const initialSquare = V.CoordsToSquare(move.start);
229
230 // Translate final square
231 const finalSquare = V.CoordsToSquare(move.end);
232
233 let notation = "";
234 const piece = this.getPiece(move.start.x, move.start.y);
235 if (piece == V.PAWN) {
236 // pawn move (TODO: enPassant indication)
237 if (move.vanish.length == 2) {
238 // capture
239 notation = initialSquare + "x" + finalSquare;
240 }
241 else notation = finalSquare;
242 if (piece != move.appear[0].p)
243 //promotion
244 notation += "=" + move.appear[0].p.toUpperCase();
245 }
246 else {
247 // Piece movement
248 notation = piece.toUpperCase();
249 if (move.vanish.length > 1) notation += initialSquare + "x";
250 notation += finalSquare;
251 }
252 return notation;
253 }
665eed90 254};