Commit | Line | Data |
---|---|---|
0c3fe8a6 | 1 | import { ChessRules } from "@/base_rules"; |
6808d7a1 | 2 | import { ArrayFun } from "@/utils/array"; |
0c3fe8a6 BA |
3 | import { randInt } from "@/utils/alea"; |
4 | ||
c583ef1c BA |
5 | export const VariantRules = class Antiking1Rules extends ChessRules { |
6 | static get HasEnpassant() { | |
7 | return false; | |
8 | } | |
9 | ||
10 | static get HasCastle() { | |
11 | return false; | |
12 | } | |
13 | ||
6808d7a1 BA |
14 | static get ANTIKING() { |
15 | return "a"; | |
16 | } | |
dac39588 | 17 | |
6808d7a1 | 18 | static get PIECES() { |
dac39588 BA |
19 | return ChessRules.PIECES.concat([V.ANTIKING]); |
20 | } | |
21 | ||
241bf8f2 BA |
22 | getPpath(b) { |
23 | return b[1] == "a" ? "Antiking/" + b : b; | |
24 | } | |
25 | ||
6808d7a1 | 26 | setOtherVariables(fen) { |
dac39588 | 27 | super.setOtherVariables(fen); |
6808d7a1 | 28 | this.antikingPos = { w: [-1, -1], b: [-1, -1] }; |
dac39588 | 29 | const rows = V.ParseFen(fen).position.split("/"); |
6808d7a1 | 30 | for (let i = 0; i < rows.length; i++) { |
dac39588 | 31 | let k = 0; |
6808d7a1 BA |
32 | for (let j = 0; j < rows[i].length; j++) { |
33 | switch (rows[i].charAt(j)) { | |
34 | case "a": | |
35 | this.antikingPos["b"] = [i, k]; | |
dac39588 | 36 | break; |
6808d7a1 BA |
37 | case "A": |
38 | this.antikingPos["w"] = [i, k]; | |
dac39588 | 39 | break; |
6808d7a1 | 40 | default: { |
dac39588 | 41 | const num = parseInt(rows[i].charAt(j)); |
6808d7a1 BA |
42 | if (!isNaN(num)) k += num - 1; |
43 | } | |
dac39588 BA |
44 | } |
45 | k++; | |
46 | } | |
47 | } | |
48 | } | |
49 | ||
c583ef1c BA |
50 | // (Anti)King flags at 1 (true) if they can knight-jump |
51 | setFlags(fenflags) { | |
52 | this.kingFlags = { | |
53 | // King then antiking | |
54 | w: [...Array(2).fill(false)], | |
55 | b: [...Array(2).fill(false)] | |
56 | }; | |
57 | for (let c of ["w", "b"]) { | |
58 | for (let i = 0; i < 2; i++) | |
59 | this.kingFlags[c][i] = fenflags.charAt((c == "w" ? 0 : 2) + i) == "1"; | |
60 | } | |
61 | } | |
62 | ||
63 | aggregateFlags() { | |
64 | return this.kingFlags; | |
65 | } | |
66 | ||
67 | disaggregateFlags(flags) { | |
68 | this.kingFlags = flags; | |
69 | } | |
70 | ||
71 | getFlagsFen() { | |
72 | // Return kings flags | |
73 | let flags = ""; | |
74 | for (let c of ["w", "b"]) { | |
75 | for (let i = 0; i < 2; i++) flags += this.kingFlags[c][i] ? "1" : "0"; | |
76 | } | |
77 | return flags; | |
78 | } | |
79 | ||
6808d7a1 BA |
80 | canTake([x1, y1], [x2, y2]) { |
81 | const piece1 = this.getPiece(x1, y1); | |
82 | const piece2 = this.getPiece(x2, y2); | |
83 | const color1 = this.getColor(x1, y1); | |
84 | const color2 = this.getColor(x2, y2); | |
85 | return ( | |
86 | piece2 != "a" && | |
87 | ((piece1 != "a" && color1 != color2) || | |
88 | (piece1 == "a" && color1 == color2)) | |
89 | ); | |
dac39588 BA |
90 | } |
91 | ||
6808d7a1 | 92 | getPotentialMovesFrom([x, y]) { |
c583ef1c BA |
93 | let moves = []; |
94 | let addKnightJumps = false; | |
95 | const piece = this.getPiece(x, y); | |
96 | const color = this.getColor(x, y); | |
97 | if (piece == V.ANTIKING) { | |
98 | moves = this.getPotentialAntikingMoves([x, y]); | |
99 | addKnightJumps = this.kingFlags[color][1]; | |
100 | } else { | |
101 | moves = super.getPotentialMovesFrom([x, y]); | |
102 | if (piece == V.KING) addKnightJumps = this.kingFlags[color][0]; | |
103 | } | |
104 | if (addKnightJumps) { | |
105 | // Add potential knight jump to (anti)kings | |
106 | const knightJumps = super.getPotentialKnightMoves([x, y]); | |
107 | // Remove captures (TODO: could be done more efficiently...) | |
108 | moves = moves.concat(knightJumps.filter(m => m.vanish.length == 1)); | |
109 | } | |
110 | return moves; | |
111 | } | |
112 | ||
113 | getPotentialPawnMoves([x, y]) { | |
114 | const color = this.turn; | |
115 | let moves = []; | |
116 | const [sizeX, sizeY] = [V.size.x, V.size.y]; | |
117 | const shiftX = color == "w" ? -1 : 1; | |
118 | const startRank = color == "w" ? sizeX - 2 : 1; | |
119 | const lastRank = color == "w" ? 0 : sizeX - 1; | |
120 | const finalPieces = | |
121 | x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN]; | |
122 | ||
123 | // One square diagonally | |
124 | for (let shiftY of [-1, 1]) { | |
125 | if (this.board[x + shiftX][y + shiftY] == V.EMPTY) { | |
126 | for (let piece of finalPieces) { | |
127 | moves.push( | |
128 | this.getBasicMove([x, y], [x + shiftX, y + shiftY], { | |
129 | c: color, | |
130 | p: piece | |
131 | }) | |
132 | ); | |
133 | } | |
134 | } | |
dac39588 | 135 | } |
c583ef1c BA |
136 | // Capture |
137 | if ( | |
138 | this.board[x + shiftX][y] != V.EMPTY && | |
139 | this.canTake([x, y], [x + shiftX, y]) | |
140 | ) { | |
141 | for (let piece of finalPieces) | |
142 | moves.push( | |
143 | this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece }) | |
144 | ); | |
145 | } | |
146 | ||
147 | return moves; | |
dac39588 BA |
148 | } |
149 | ||
6808d7a1 | 150 | getPotentialAntikingMoves(sq) { |
c583ef1c | 151 | // The antiking moves like a king (only captured colors differ) |
6808d7a1 BA |
152 | return this.getSlideNJumpMoves( |
153 | sq, | |
154 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), | |
155 | "oneStep" | |
156 | ); | |
dac39588 BA |
157 | } |
158 | ||
68e19a44 | 159 | isAttacked(sq, color) { |
6808d7a1 | 160 | return ( |
68e19a44 BA |
161 | super.isAttacked(sq, color) || |
162 | this.isAttackedByAntiking(sq, color) | |
6808d7a1 | 163 | ); |
dac39588 BA |
164 | } |
165 | ||
c583ef1c BA |
166 | isAttackedByPawn([x, y], color) { |
167 | let pawnShift = (color == "w" ? 1 : -1); | |
168 | if (x + pawnShift >= 0 && x + pawnShift < V.size.x) { | |
169 | if ( | |
170 | this.getPiece(x + pawnShift, y) == V.PAWN && | |
171 | this.getColor(x + pawnShift, y) == color | |
172 | ) { | |
173 | return true; | |
174 | } | |
175 | } | |
176 | return false; | |
177 | } | |
178 | ||
68e19a44 BA |
179 | isAttackedByKing([x, y], color) { |
180 | // Antiking is not attacked by king: | |
181 | if (this.getPiece(x, y) == V.ANTIKING) return false; | |
6808d7a1 BA |
182 | return this.isAttackedBySlideNJump( |
183 | [x, y], | |
68e19a44 | 184 | color, |
6808d7a1 BA |
185 | V.KING, |
186 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), | |
187 | "oneStep" | |
188 | ); | |
dac39588 BA |
189 | } |
190 | ||
68e19a44 BA |
191 | isAttackedByAntiking([x, y], color) { |
192 | // (Anti)King is not attacked by antiking | |
193 | if ([V.KING, V.ANTIKING].includes(this.getPiece(x, y))) return false; | |
6808d7a1 BA |
194 | return this.isAttackedBySlideNJump( |
195 | [x, y], | |
68e19a44 | 196 | color, |
6808d7a1 BA |
197 | V.ANTIKING, |
198 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), | |
199 | "oneStep" | |
200 | ); | |
dac39588 BA |
201 | } |
202 | ||
6808d7a1 | 203 | underCheck(color) { |
dac39588 | 204 | const oppCol = V.GetOppCol(color); |
6808d7a1 | 205 | let res = |
68e19a44 BA |
206 | this.isAttacked(this.kingPos[color], oppCol) || |
207 | !this.isAttacked(this.antikingPos[color], oppCol); | |
dac39588 BA |
208 | return res; |
209 | } | |
210 | ||
6808d7a1 | 211 | getCheckSquares(color) { |
c583ef1c BA |
212 | let res = []; |
213 | const oppCol = V.GetOppCol(color); | |
214 | if (this.isAttacked(this.kingPos[color], oppCol)) | |
215 | res.push(JSON.parse(JSON.stringify(this.kingPos[color]))); | |
216 | if (!this.isAttacked(this.antikingPos[color], oppCol)) | |
dac39588 BA |
217 | res.push(JSON.parse(JSON.stringify(this.antikingPos[color]))); |
218 | return res; | |
219 | } | |
220 | ||
3a2a7b5f BA |
221 | postPlay(move) { |
222 | super.postPlay(move); | |
dac39588 BA |
223 | const piece = move.vanish[0].p; |
224 | const c = move.vanish[0].c; | |
c583ef1c | 225 | // Update antiking position, and kings flags |
6808d7a1 | 226 | if (piece == V.ANTIKING) { |
dac39588 BA |
227 | this.antikingPos[c][0] = move.appear[0].x; |
228 | this.antikingPos[c][1] = move.appear[0].y; | |
c583ef1c BA |
229 | this.kingFlags[c][1] = false; |
230 | } else if (piece == V.KING) this.kingFlags[c][0] = false; | |
dac39588 BA |
231 | } |
232 | ||
3a2a7b5f BA |
233 | postUndo(move) { |
234 | super.postUndo(move); | |
dac39588 BA |
235 | const c = move.vanish[0].c; |
236 | if (move.vanish[0].p == V.ANTIKING) | |
237 | this.antikingPos[c] = [move.start.x, move.start.y]; | |
238 | } | |
239 | ||
6808d7a1 BA |
240 | getCurrentScore() { |
241 | if (this.atLeastOneMove()) | |
0c3fe8a6 BA |
242 | return "*"; |
243 | ||
244 | const color = this.turn; | |
dac39588 | 245 | const oppCol = V.GetOppCol(color); |
6808d7a1 | 246 | if ( |
68e19a44 BA |
247 | !this.isAttacked(this.kingPos[color], oppCol) && |
248 | this.isAttacked(this.antikingPos[color], oppCol) | |
6808d7a1 | 249 | ) { |
dac39588 BA |
250 | return "1/2"; |
251 | } | |
252 | return color == "w" ? "0-1" : "1-0"; | |
253 | } | |
254 | ||
255 | static get VALUES() { | |
a97bdbda BA |
256 | return Object.assign( |
257 | { a: 1000 }, | |
258 | ChessRules.VALUES | |
259 | ); | |
dac39588 BA |
260 | } |
261 | ||
c583ef1c BA |
262 | static GenRandInitFen() { |
263 | // Always deterministic setup | |
264 | return "2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0 1111"; | |
dac39588 | 265 | } |
b83a675a BA |
266 | |
267 | static get SEARCH_DEPTH() { | |
268 | return 2; | |
269 | } | |
11482348 BA |
270 | |
271 | // TODO: notation copied from Berolina | |
272 | getNotation(move) { | |
273 | const piece = this.getPiece(move.start.x, move.start.y); | |
274 | if (piece == V.PAWN) { | |
275 | // Pawn move | |
276 | const finalSquare = V.CoordsToSquare(move.end); | |
277 | let notation = ""; | |
278 | if (move.vanish.length == 2) | |
279 | //capture | |
280 | notation = "Px" + finalSquare; | |
281 | else { | |
282 | // No capture: indicate the initial square for potential ambiguity | |
283 | const startSquare = V.CoordsToSquare(move.start); | |
284 | notation = startSquare + finalSquare; | |
285 | } | |
286 | if (move.appear[0].p != V.PAWN) | |
287 | // Promotion | |
288 | notation += "=" + move.appear[0].p.toUpperCase(); | |
289 | return notation; | |
290 | } | |
291 | return super.getNotation(move); //all other pieces are orthodox | |
292 | } | |
6808d7a1 | 293 | }; |