Commit | Line | Data |
---|---|---|
2fac4d67 BA |
1 | import { ChessRules } from "@/base_rules"; |
2 | import { Antiking2Rules } from "@/variants/Antiking2"; | |
3 | ||
4 | export class MesmerRules extends ChessRules { | |
5 | ||
6 | static IsGoodFen(fen) { | |
7 | if (!ChessRules.IsGoodFen(fen)) return false; | |
8 | const fenParsed = V.ParseFen(fen); | |
9 | // 5) Check arrival of last hypnotizing move (if any) | |
10 | if ( | |
11 | !fenParsed.hSquare || | |
12 | (fenParsed.hSquare != "-" && !fenParsed.hSquare.match(/^[a-h][1-8]$/)) | |
13 | ) { | |
14 | return false; | |
15 | } | |
16 | return true; | |
17 | } | |
18 | ||
19 | static get MESMERIST() { | |
20 | return 'm'; | |
21 | } | |
22 | ||
23 | static get PIECES() { | |
24 | return ChessRules.PIECES.concat([V.MESMERIST]); | |
25 | } | |
26 | ||
27 | getPpath(b) { | |
28 | return (b.charAt(1) == 'm' ? "Mesmer/" : "") + b; | |
29 | } | |
30 | ||
31 | static ParseFen(fen) { | |
32 | const fenParts = fen.split(" "); | |
33 | return Object.assign( | |
34 | { hSquare: fenParts[5] }, | |
35 | ChessRules.ParseFen(fen) | |
36 | ); | |
37 | } | |
38 | ||
4313762d BA |
39 | static GenRandInitFen(options) { |
40 | const antikingFen = Antiking2Rules.GenRandInitFen(options); | |
2fac4d67 BA |
41 | return antikingFen.replace('a', 'M').replace('A', 'm') + " -"; |
42 | } | |
43 | ||
44 | setOtherVariables(fen) { | |
45 | super.setOtherVariables(fen); | |
46 | const parsedFen = V.ParseFen(fen); | |
47 | this.hSquares = [ | |
48 | parsedFen.hSquare != "-" | |
49 | ? V.SquareToCoords(parsedFen.hSquare) | |
50 | : null | |
51 | ]; | |
52 | } | |
53 | ||
54 | scanKings(fen) { | |
55 | super.scanKings(fen); | |
56 | // Squares of white and black mesmerist: | |
57 | this.mesmerPos = { w: [-1, -1], b: [-1, -1] }; | |
58 | const fenRows = V.ParseFen(fen).position.split("/"); | |
59 | for (let i = 0; i < fenRows.length; i++) { | |
60 | let k = 0; | |
61 | for (let j = 0; j < fenRows[i].length; j++) { | |
62 | switch (fenRows[i].charAt(j)) { | |
63 | case "m": | |
64 | this.mesmerPos["b"] = [i, k]; | |
65 | break; | |
66 | case "M": | |
67 | this.mesmerPos["w"] = [i, k]; | |
68 | break; | |
69 | default: { | |
70 | const num = parseInt(fenRows[i].charAt(j), 10); | |
71 | if (!isNaN(num)) k += num - 1; | |
72 | } | |
73 | } | |
74 | k++; | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
79 | getFen() { | |
80 | const L = this.hSquares.length; | |
81 | return ( | |
82 | super.getFen() + " " + | |
83 | (!this.hSquares[L-1] ? "-" : V.CoordsToSquare(this.hSquares[L-1])) | |
84 | ); | |
85 | } | |
86 | ||
87 | canIplay(side) { | |
88 | // Wrong, but sufficient approximation let's say | |
89 | return this.turn == side; | |
90 | } | |
91 | ||
92 | canTake([x1, y1], [x2, y2]) { | |
93 | const c = this.turn; | |
94 | const c1 = this.getColor(x1, y1); | |
95 | const c2 = this.getColor(x2, y2); | |
96 | return (c == c1 && c1 != c2) || (c != c1 && c1 == c2); | |
97 | } | |
98 | ||
99 | getPotentialMovesFrom([x, y]) { | |
100 | const L = this.hSquares.length; | |
101 | const lh = this.hSquares[L-1]; | |
102 | if (!!lh && lh.x == x && lh.y == y) return []; | |
103 | const c = this.getColor(x, y); | |
104 | const piece = this.getPiece(x, y); | |
105 | if (c == this.turn) { | |
106 | if (piece == V.MESMERIST) return this.getPotentialMesmeristMoves([x, y]); | |
107 | return super.getPotentialMovesFrom([x, y]); | |
108 | } | |
109 | // Playing opponent's pieces: hypnotizing moves. Allowed? | |
110 | if (piece == V.MESMERIST || !this.isAttackedByMesmerist([x, y], this.turn)) | |
111 | return []; | |
112 | const moves = | |
113 | piece == V.KING | |
114 | // No castling with enemy king (...yes, should eat it but...) | |
115 | ? super.getSlideNJumpMoves( | |
4313762d | 116 | [x, y], V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1) |
2fac4d67 BA |
117 | : super.getPotentialMovesFrom([x, y]); |
118 | return moves; | |
119 | } | |
120 | ||
121 | // Moves like a queen without capturing | |
122 | getPotentialMesmeristMoves([x, y]) { | |
123 | const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); | |
124 | let moves = []; | |
125 | for (let step of steps) { | |
126 | let i = x + step[0]; | |
127 | let j = y + step[1]; | |
128 | while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { | |
129 | moves.push(this.getBasicMove([x, y], [i, j])); | |
130 | i += step[0]; | |
131 | j += step[1]; | |
132 | } | |
133 | } | |
134 | return moves; | |
135 | } | |
136 | ||
137 | isAttackedByMesmerist(sq, color) { | |
138 | return ( | |
139 | super.isAttackedBySlideNJump( | |
140 | sq, color, V.MESMERIST, V.steps[V.ROOK].concat(V.steps[V.BISHOP])) | |
141 | ); | |
142 | } | |
143 | ||
144 | getEnpassantCaptures([x, y], shiftX) { | |
145 | const Lep = this.epSquares.length; | |
146 | const epSquare = this.epSquares[Lep - 1]; //always at least one element | |
147 | let enpassantMove = null; | |
148 | const c = this.getColor(x, y); | |
149 | if ( | |
150 | !!epSquare && | |
151 | epSquare.x == x + shiftX && | |
152 | Math.abs(epSquare.y - y) == 1 && | |
153 | // Next conditions to avoid capturing self hypnotized pawns: | |
154 | this.board[x][epSquare.y] != V.EMPTY && | |
155 | this.getColor(x, epSquare.y) != c //TODO: probably redundant | |
156 | ) { | |
157 | enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]); | |
158 | enpassantMove.vanish.push({ | |
159 | x: x, | |
160 | y: epSquare.y, | |
161 | p: this.board[x][epSquare.y].charAt(1), | |
162 | c: this.getColor(x, epSquare.y) | |
163 | }); | |
164 | } | |
165 | return !!enpassantMove ? [enpassantMove] : []; | |
166 | } | |
167 | ||
168 | // TODO: avoid following code duplication, by using getColor() | |
169 | // instead of this.turn at the beginning of 2 next methods | |
170 | addPawnMoves([x1, y1], [x2, y2], moves, promotions) { | |
171 | let finalPieces = [V.PAWN]; | |
172 | const color = this.getColor(x1, y1); | |
173 | const lastRank = (color == "w" ? 0 : V.size.x - 1); | |
174 | if (x2 == lastRank) finalPieces = V.PawnSpecs.promotions; | |
175 | let tr = null; | |
176 | for (let piece of finalPieces) { | |
177 | tr = (piece != V.PAWN ? { c: color, p: piece } : null); | |
178 | moves.push(this.getBasicMove([x1, y1], [x2, y2], tr)); | |
179 | } | |
180 | } | |
181 | ||
182 | getPotentialPawnMoves([x, y], promotions) { | |
183 | const color = this.getColor(x, y); | |
184 | const [sizeX, sizeY] = [V.size.x, V.size.y]; | |
185 | const forward = (color == 'w' ? -1 : 1); | |
186 | ||
187 | let moves = []; | |
188 | if (x + forward >= 0 && x + forward < sizeX) { | |
189 | if (this.board[x + forward][y] == V.EMPTY) { | |
190 | this.addPawnMoves([x, y], [x + forward, y], moves, promotions); | |
191 | if ( | |
192 | ((color == 'w' && x == 6) || (color == 'b' && x == 1)) && | |
193 | this.board[x + 2 * forward][y] == V.EMPTY | |
194 | ) { | |
195 | moves.push(this.getBasicMove([x, y], [x + 2 * forward, y])); | |
196 | } | |
197 | } | |
198 | for (let shiftY of [-1, 1]) { | |
199 | if ( | |
200 | y + shiftY >= 0 && y + shiftY < sizeY && | |
201 | this.board[x + forward][y + shiftY] != V.EMPTY && | |
202 | this.canTake([x, y], [x + forward, y + shiftY]) | |
203 | ) { | |
204 | this.addPawnMoves( | |
205 | [x, y], [x + forward, y + shiftY], | |
206 | moves, promotions | |
207 | ); | |
208 | } | |
209 | } | |
210 | } | |
211 | Array.prototype.push.apply(moves, | |
212 | this.getEnpassantCaptures([x, y], forward)); | |
213 | return moves; | |
214 | } | |
215 | ||
216 | postPlay(move) { | |
217 | super.postPlay(move); | |
218 | if (move.vanish[0].p == V.MESMERIST) | |
219 | this.mesmerPos[move.vanish[0].c] = [move.appear[0].x, move.appear[0].y]; | |
220 | if (move.vanish[0].c == this.turn) | |
221 | this.hSquares.push({ x: move.appear[0].x, y: move.appear[0].y }); | |
222 | else this.hSquares.push(null); | |
223 | if (move.vanish.length == 2) { | |
224 | if (move.vanish[1].p == V.KING) | |
225 | this.kingPos[move.vanish[1].c] = [-1, -1]; | |
226 | else if (move.vanish[1].p == V.MESMERIST) | |
227 | this.mesmerPos[move.vanish[1].c] = [-1, -1] | |
228 | } | |
229 | } | |
230 | postUndo(move) { | |
231 | super.postUndo(move); | |
232 | if (move.vanish[0].p == V.MESMERIST) | |
233 | this.mesmerPos[move.vanish[0].c] = [move.vanish[0].x, move.vanish[0].y]; | |
234 | this.hSquares.pop(); | |
235 | if (move.vanish.length == 2) { | |
236 | const v = move.vanish[1]; | |
237 | if (v.p == V.KING) | |
238 | this.kingPos[v.c] = [v.x, v.y]; | |
239 | else if (v.p == V.MESMERIST) | |
240 | this.mesmerPos[v.c] = [v.x, v.y]; | |
241 | } | |
242 | } | |
243 | ||
244 | getCheckSquares() { | |
245 | return []; | |
246 | } | |
247 | filterValid(moves) { | |
248 | return moves; | |
249 | } | |
250 | ||
251 | getCurrentScore() { | |
252 | const c = this.turn; | |
253 | if (this.kingPos[c][0] < 0) return (c == 'w' ? "0-1" : "1-0"); | |
254 | if (this.mesmerPos[c][0] < 0) return (c == 'w' ? "0-1" : "1-0"); | |
255 | return "*"; | |
256 | } | |
257 | ||
258 | static get SEARCH_DEPTH() { | |
259 | return 2; | |
260 | } | |
261 | ||
262 | }; |