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