Improve Bario rules description about castling
[vchess.git] / client / src / variants / Hypnotic.js
1 import { ChessRules } from "@/base_rules";
2
3 export class HypnoticRules extends ChessRules {
4
5 static IsGoodFen(fen) {
6 if (!ChessRules.IsGoodFen(fen)) return false;
7 const fenParsed = V.ParseFen(fen);
8 // 5) Check arrival of last hypnotizing move (if any)
9 if (
10 !fenParsed.hSquare ||
11 (fenParsed.hSquare != "-" && !fenParsed.hSquare.match(/^[a-h][1-8]$/))
12 ) {
13 return false;
14 }
15 return true;
16 }
17
18 static ParseFen(fen) {
19 const fenParts = fen.split(" ");
20 return Object.assign(
21 { hSquare: fenParts[5] },
22 ChessRules.ParseFen(fen)
23 );
24 }
25
26 static GenRandInitFen(randomness) {
27 return ChessRules.GenRandInitFen(randomness) + " -";
28 }
29
30 setOtherVariables(fen) {
31 super.setOtherVariables(fen);
32 const parsedFen = V.ParseFen(fen);
33 this.hSquares = [
34 parsedFen.hSquare != "-"
35 ? V.SquareToCoords(parsedFen.hSquare)
36 : null
37 ];
38 }
39
40 getFen() {
41 const L = this.hSquares.length;
42 return (
43 super.getFen() + " " +
44 (!this.hSquares[L-1] ? "-" : V.CoordsToSquare(this.hSquares[L-1]))
45 );
46 }
47
48 canIplay(side) {
49 // Wrong, but sufficient approximation let's say
50 return this.turn == side;
51 }
52
53 canTake([x1, y1], [x2, y2]) {
54 const c = this.turn;
55 const c1 = this.getColor(x1, y1);
56 const c2 = this.getColor(x2, y2);
57 return (c == c1 && c1 != c2) || (c != c1 && c1 == c2);
58 }
59
60 getPotentialMovesFrom([x, y]) {
61 const L = this.hSquares.length;
62 const lh = this.hSquares[L-1];
63 if (!!lh && lh.x == x && lh.y == y) return [];
64 const c = this.getColor(x, y);
65 if (c == this.turn) return super.getPotentialMovesFrom([x, y]);
66 // Playing opponent's pieces: hypnotizing moves. Allowed?
67 if (!this.isAttacked([x, y], this.turn)) return [];
68 const moves =
69 this.getPiece(x, y) == V.KING
70 // No castling with enemy king (...yes, should eat it but...)
71 ? super.getSlideNJumpMoves(
72 [x, y], V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep")
73 : super.getPotentialMovesFrom([x, y]);
74 return moves;
75 }
76
77 getAllPotentialMoves() {
78 let potentialMoves = [];
79 for (let i = 0; i < V.size.x; i++) {
80 for (let j = 0; j < V.size.y; j++) {
81 if (this.board[i][j] != V.EMPTY) {
82 Array.prototype.push.apply(
83 potentialMoves,
84 this.getPotentialMovesFrom([i, j])
85 );
86 }
87 }
88 }
89 return potentialMoves;
90 }
91
92 getEnpassantCaptures([x, y], shiftX) {
93 const Lep = this.epSquares.length;
94 const epSquare = this.epSquares[Lep - 1]; //always at least one element
95 let enpassantMove = null;
96 const c = this.getColor(x, y);
97 if (
98 !!epSquare &&
99 epSquare.x == x + shiftX &&
100 Math.abs(epSquare.y - y) == 1 &&
101 // Next conditions to avoid capturing self hypnotized pawns:
102 this.board[x][epSquare.y] != V.EMPTY &&
103 this.getColor(x, epSquare.y) != c //TODO: probably redundant
104 ) {
105 enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
106 enpassantMove.vanish.push({
107 x: x,
108 y: epSquare.y,
109 p: this.board[x][epSquare.y].charAt(1),
110 c: this.getColor(x, epSquare.y)
111 });
112 }
113 return !!enpassantMove ? [enpassantMove] : [];
114 }
115
116 // TODO: avoid following code duplication, by using getColor()
117 // instead of this.turn at the beginning of 2 next methods
118 addPawnMoves([x1, y1], [x2, y2], moves, promotions) {
119 let finalPieces = [V.PAWN];
120 const color = this.getColor(x1, y1);
121 const lastRank = (color == "w" ? 0 : V.size.x - 1);
122 if (x2 == lastRank) finalPieces = V.PawnSpecs.promotions;
123 let tr = null;
124 for (let piece of finalPieces) {
125 tr = (piece != V.PAWN ? { c: color, p: piece } : null);
126 moves.push(this.getBasicMove([x1, y1], [x2, y2], tr));
127 }
128 }
129
130 getPotentialPawnMoves([x, y], promotions) {
131 const color = this.getColor(x, y);
132 const [sizeX, sizeY] = [V.size.x, V.size.y];
133 const forward = (color == 'w' ? -1 : 1);
134
135 let moves = [];
136 if (x + forward >= 0 && x + forward < sizeX) {
137 if (this.board[x + forward][y] == V.EMPTY) {
138 this.addPawnMoves([x, y], [x + forward, y], moves, promotions);
139 if (
140 ((color == 'w' && x == 6) || (color == 'b' && x == 1)) &&
141 this.board[x + 2 * forward][y] == V.EMPTY
142 ) {
143 moves.push(this.getBasicMove([x, y], [x + 2 * forward, y]));
144 }
145 }
146 for (let shiftY of [-1, 1]) {
147 if (
148 y + shiftY >= 0 && y + shiftY < sizeY &&
149 this.board[x + forward][y + shiftY] != V.EMPTY &&
150 this.canTake([x, y], [x + forward, y + shiftY])
151 ) {
152 this.addPawnMoves(
153 [x, y], [x + forward, y + shiftY],
154 moves, promotions
155 );
156 }
157 }
158 }
159 Array.prototype.push.apply(moves,
160 this.getEnpassantCaptures([x, y], forward));
161 return moves;
162 }
163
164 postPlay(move) {
165 super.postPlay(move);
166 if (move.vanish[0].c == this.turn)
167 this.hSquares.push({ x: move.appear[0].x, y: move.appear[0].y });
168 else this.hSquares.push(null);
169 if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
170 this.kingPos[move.vanish[1].c] = [-1, -1];
171 }
172 postUndo(move) {
173 super.postUndo(move);
174 this.hSquares.pop();
175 if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
176 this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
177 }
178
179 getCheckSquares() {
180 return [];
181 }
182 filterValid(moves) {
183 return moves;
184 }
185
186 getCurrentScore() {
187 const c = this.turn;
188 if (this.kingPos[c][0] < 0) return (c == 'w' ? "0-1" : "1-0");
189 return "*";
190 }
191
192 static get SEARCH_DEPTH() {
193 return 2;
194 }
195
196 };