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