Several small improvements + integrate options + first working draft of Cwda
[vchess.git] / client / src / variants / Monocolor.js
1 import { ChessRules } from "@/base_rules";
2
3 export class MonocolorRules extends ChessRules {
4
5 static get HasEnpassant() {
6 // Pawns would be on the same side
7 return false;
8 }
9
10 static get HasFlags() {
11 return false;
12 }
13
14 static get Lines() {
15 return [ [[4, 0], [4, 8]] ];
16 }
17
18 get showFirstTurn() {
19 return true;
20 }
21
22 static IsGoodPosition(position) {
23 if (position.length == 0) return false;
24 const rows = position.split("/");
25 if (rows.length != V.size.x) return false;
26 for (let row of rows) {
27 let sumElts = 0;
28 for (let i = 0; i < row.length; i++) {
29 if (V.PIECES.includes(row[i])) sumElts++;
30 else {
31 const num = parseInt(row[i], 10);
32 if (isNaN(num)) return false;
33 sumElts += num;
34 }
35 }
36 if (sumElts != V.size.y) return false;
37 }
38 return true;
39 }
40
41 getPpath(b) {
42 return (b[1] == V.KNIGHT ? "Enpassant/" : "") + b;
43 }
44
45 canIplay(side, [x, y]) {
46 const xBounds = side == 'w' ? [4,7] : [0,3];
47 return this.turn == side && x >= xBounds[0] && x <= xBounds[1];
48 }
49
50 canTake([x1, y1], [x2, y2]) {
51 // Capture in other half-board
52 return ((x1 <= 3 && x2 >= 4) || (x1 >= 4 && x2 <= 3));
53 }
54
55 // Trim all non-capturing moves
56 static KeepCaptures(moves) {
57 return moves.filter(m => m.vanish.length == 2);
58 }
59
60 getPotentialKnightMoves(sq) {
61 // Knight becomes knightrider:
62 return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT]);
63 }
64
65 getPotentialKingMoves(sq) {
66 // King become queen:
67 return (
68 this.getSlideNJumpMoves(sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]))
69 );
70 }
71
72 getAllPotentialMoves() {
73 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
74 let potentialMoves = [];
75 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
76 for (let j = 0; j < V.size.y; j++) {
77 if (this.board[i][j] != V.EMPTY) {
78 Array.prototype.push.apply(
79 potentialMoves,
80 this.getPotentialMovesFrom([i, j])
81 );
82 }
83 }
84 }
85 if (potentialMoves.some(m => m.vanish.length == 2 && m.appear.length == 1))
86 return V.KeepCaptures(potentialMoves);
87 return potentialMoves;
88 }
89
90 atLeastOneMove() {
91 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
92 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
93 for (let j = 0; j < V.size.y; j++) {
94 if (
95 this.board[i][j] != V.EMPTY &&
96 this.getPotentialMovesFrom([i, j]).length > 0
97 ) {
98 return true;
99 }
100 }
101 }
102 return false;
103 }
104
105 // Stop at the first capture found (if any)
106 atLeastOneCapture() {
107 const xBounds = this.turn == 'w' ? [4,7] : [0,3];
108 for (let i = xBounds[0]; i <= xBounds[1]; i++) {
109 for (let j = 0; j < V.size.y; j++) {
110 if (
111 this.board[i][j] != V.EMPTY &&
112 this.getPotentialMovesFrom([i, j]).some(m => m.vanish.length == 2)
113 ) {
114 return true;
115 }
116 }
117 }
118 return false;
119 }
120
121 getPossibleMovesFrom(sq) {
122 let moves = this.getPotentialMovesFrom(sq);
123 const captureMoves = V.KeepCaptures(moves);
124 if (captureMoves.length > 0) return captureMoves;
125 if (this.atLeastOneCapture()) return [];
126 return moves;
127 }
128
129 filterValid(moves) {
130 return moves;
131 }
132
133 isAttacked() {
134 return false;
135 }
136
137 getCheckSquares() {
138 return [];
139 }
140
141 getCurrentScore() {
142 // Is there anything in opponent's half board?
143 const color = V.GetOppCol(this.turn);
144 const xBounds = color == 'w' ? [4,7] : [0,3];
145 let nothingHere = true;
146 outerLoop: for (let i = xBounds[0]; i <= xBounds[1]; i++) {
147 for (let j = 0; j < V.size.y; j++) {
148 if (this.board[i][j] != V.EMPTY) {
149 nothingHere = false;
150 break outerLoop;
151 }
152 }
153 }
154 if (nothingHere) return color == 'w' ? "0-1" : "1-0";
155 if (this.atLeastOneMove()) return '*';
156 return "1/2";
157 }
158
159 static GenRandInitFen(options) {
160 // Remove the en-passant + castle part of the FEN
161 let fen = ChessRules.GenRandInitFen(options).slice(0, -6);
162 // Replace kings with queens
163 fen = fen.replace("k", "q").replace("K", "Q");
164 // Move pawns up:
165 fen = fen.replace("pppppppp/8","8/pppppppp")
166 .replace("8/PPPPPPPP","PPPPPPPP/8");
167 const firstSpace = fen.indexOf(' ');
168 // Paint it black:
169 fen =
170 fen.substr(0, firstSpace).replace(/[A-Z]/g, (c) => c.toLowerCase()) +
171 fen.substr(firstSpace);
172 return fen;
173 }
174
175 static get SEARCH_DEPTH() {
176 return 4;
177 }
178
179 evalPosition() {
180 let evaluation = 0;
181 for (let i = 0; i < 8; i++) {
182 for (let j = 0; j < V.size.y; j++) {
183 if (this.board[i][j] != V.EMPTY) {
184 const sign = (i <= 3 ? -1 : 1);
185 // I don't think taking pieces' values into account would help
186 evaluation += sign; //* V.VALUES[this.getPiece(i, j)];
187 }
188 }
189 }
190 return evaluation;
191 }
192
193 getNotation(move) {
194 // Translate initial square (because pieces may fly unusually!)
195 const initialSquare = V.CoordsToSquare(move.start);
196
197 // Translate final square
198 const finalSquare = V.CoordsToSquare(move.end);
199
200 let notation = "";
201 const piece = this.getPiece(move.start.x, move.start.y);
202 if (piece == V.PAWN) {
203 // pawn move (TODO: enPassant indication)
204 if (move.vanish.length == 2) {
205 // capture
206 notation = initialSquare + "x" + finalSquare;
207 }
208 else notation = finalSquare;
209 if (piece != move.appear[0].p)
210 //promotion
211 notation += "=" + move.appear[0].p.toUpperCase();
212 }
213 else {
214 // Piece movement
215 notation = piece.toUpperCase();
216 if (move.vanish.length > 1) notation += initialSquare + "x";
217 notation += finalSquare;
218 }
219 return notation;
220 }
221
222 };