297eef75e2b9c37579534289edf44c22fa7db900
[vchess.git] / client / src / variants / Suction.js
1 import { ChessRules, PiPo, Move } from "@/base_rules";
2
3 export const VariantRules = class SuctionRules extends ChessRules {
4 setOtherVariables(fen) {
5 super.setOtherVariables(fen);
6 // Local stack of captures
7 this.cmoves = [];
8 const cmove = fen.split(" ")[5];
9 if (cmove == "-") this.cmoves.push(null);
10 else {
11 this.cmoves.push({
12 start: ChessRules.SquareToCoords(cmove.substr(0, 2)),
13 end: ChessRules.SquareToCoords(cmove.substr(2))
14 });
15 }
16 }
17
18 static IsGoodFen(fen) {
19 if (!ChessRules.IsGoodFen(fen)) return false;
20 const fenParts = fen.split(" ");
21 if (fenParts.length != 6) return false;
22 if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
23 return false;
24 return true;
25 }
26
27 getCmove(move) {
28 if (move.vanish.length == 2)
29 return { start: move.start, end: move.end };
30 return null;
31 }
32
33 getBasicMove([sx, sy], [ex, ey]) {
34 const startColor = this.getColor(sx, sy);
35 const startPiece = this.getPiece(sx, sy);
36 let mv = new Move({
37 appear: [
38 new PiPo({
39 x: ex,
40 y: ey,
41 c: startColor,
42 p: startPiece
43 })
44 ],
45 vanish: [
46 new PiPo({
47 x: sx,
48 y: sy,
49 c: startColor,
50 p: startPiece
51 })
52 ]
53 });
54
55 if (this.board[ex][ey] != V.EMPTY) {
56 const endColor = this.getColor(ex, ey);
57 const endPiece = this.getPiece(ex, ey);
58 mv.vanish.push(
59 new PiPo({
60 x: ex,
61 y: ey,
62 c: endColor,
63 p: endPiece
64 })
65 );
66 mv.appear.push(
67 new PiPo({
68 x: sx,
69 y: sy,
70 c: endColor,
71 p: endPiece
72 })
73 );
74 }
75 return mv;
76 }
77
78 getPotentialPawnMoves([x, y]) {
79 const color = this.turn;
80 let moves = [];
81 const [sizeX, sizeY] = [V.size.x, V.size.y];
82 const shiftX = color == "w" ? -1 : 1;
83 const startRank = color == "w" ? sizeX - 2 : 1;
84 const firstRank = color == "w" ? sizeX - 1 : 0;
85
86 if (x + shiftX >= 0 && x + shiftX < sizeX) {
87 // One square forward
88 if (this.board[x + shiftX][y] == V.EMPTY) {
89 moves.push(
90 this.getBasicMove([x, y], [x + shiftX, y], {
91 c: color,
92 p: "p"
93 })
94 );
95 if (
96 [startRank,firstRank].includes(x) &&
97 this.board[x + 2 * shiftX][y] == V.EMPTY
98 ) {
99 // Two squares jump
100 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
101 }
102 }
103 // Swaps
104 for (let shiftY of [-1, 1]) {
105 if (
106 y + shiftY >= 0 &&
107 y + shiftY < sizeY &&
108 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
109 this.canTake([x, y], [x + shiftX, y + shiftY])
110 ) {
111 moves.push(
112 this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
113 c: color,
114 p: "p"
115 })
116 );
117 }
118 }
119 }
120
121 // En passant
122 const Lep = this.epSquares.length;
123 const epSquare = this.epSquares[Lep - 1]; //always at least one element
124 if (
125 !!epSquare &&
126 epSquare.x == x + shiftX &&
127 Math.abs(epSquare.y - y) == 1
128 ) {
129 let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
130 const oppCol = V.GetOppCol(color);
131 enpassantMove.vanish.push({
132 x: x,
133 y: epSquare.y,
134 p: "p",
135 c: oppCol
136 });
137 enpassantMove.appear.push({
138 x: x,
139 y: y,
140 p: "p",
141 c: oppCol
142 });
143 moves.push(enpassantMove);
144 }
145
146 return moves;
147 }
148
149 getPotentialKingMoves() {
150 return [];
151 }
152
153 // Does m2 un-do m1 ? (to disallow undoing captures)
154 oppositeMoves(m1, m2) {
155 return (
156 m1 &&
157 m2.vanish.length == 2 &&
158 m1.start.x == m2.start.x &&
159 m1.end.x == m2.end.x &&
160 m1.start.y == m2.start.y &&
161 m1.end.y == m2.end.y
162 );
163 }
164
165 filterValid(moves) {
166 if (moves.length == 0) return [];
167 const color = this.turn;
168 return moves.filter(m => {
169 const L = this.cmoves.length; //at least 1: init from FEN
170 return !this.oppositeMoves(this.cmoves[L - 1], m);
171 });
172 }
173
174 updateVariables(move) {
175 super.updateVariables(move);
176 if (move.vanish.length == 2) {
177 // Was opponent king swapped?
178 if (move.vanish[1].p == V.KING)
179 this.kingPos[this.turn] = [move.appear[1].x, move.appear[1].y];
180 }
181 }
182
183 unupdateVariables(move) {
184 super.unupdateVariables(move);
185 if (move.appear.length == 2) {
186 // Was opponent king swapped?
187 if (move.appear[1].p == V.KING)
188 this.kingPos[move.vanish[1].c] = [move.vanish[1].x,move.vanish[1].y];
189 }
190 }
191
192 static GenRandInitFen() {
193 // Add empty cmove:
194 return ChessRules.GenRandInitFen() + " -";
195 }
196
197 getFen() {
198 const L = this.cmoves.length;
199 const cmoveFen = !this.cmoves[L - 1]
200 ? "-"
201 : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) +
202 ChessRules.CoordsToSquare(this.cmoves[L - 1].end);
203 return super.getFen() + " " + cmoveFen;
204 }
205
206 play(move) {
207 this.cmoves.push(this.getCmove(move));
208 super.play(move);
209 }
210
211 undo(move) {
212 this.cmoves.pop();
213 super.undo(move);
214 }
215
216 atLeastOneMove() {
217 return true;
218 }
219
220 getCheckSquares() {
221 return [];
222 }
223
224 getCurrentScore() {
225 const color = this.turn;
226 const kp = this.kingPos[color];
227 if (color == "w" && kp[0] == 0)
228 return "0-1";
229 if (color == "b" && kp[0] == V.size.x - 1)
230 return "1-0";
231 // King is not on the opposite edge: game not over
232 return "*";
233 }
234
235 evalPosition() {
236 // Very simple criterion for now: kings position
237 return this.kingPos["w"][0] + this.kingPos["b"][0];
238 }
239
240 getNotation(move) {
241 // Translate final square
242 const finalSquare = V.CoordsToSquare(move.end);
243
244 const piece = this.getPiece(move.start.x, move.start.y);
245 if (piece == V.PAWN) {
246 // Pawn move
247 let notation = "";
248 if (move.vanish.length == 2) {
249 // Capture
250 const startColumn = V.CoordToColumn(move.start.y);
251 notation = startColumn + "x" + finalSquare;
252 }
253 else notation = finalSquare;
254 return notation;
255 }
256 // Piece movement
257 return (
258 piece.toUpperCase() +
259 (move.vanish.length == 2 ? "x" : "") +
260 finalSquare
261 );
262 }
263 };