1 import { ChessRules
} from "@/base_rules";
2 import { randInt
, shuffle
} from "@/utils/alea";
4 export class AmbiguousRules
extends ChessRules
{
5 static get HasFlags() {
9 setOtherVariables(fen
) {
10 super.setOtherVariables(fen
);
11 if (this.movesCount
== 0) this.subTurn
= 2;
12 else this.subTurn
= 1;
15 // Subturn 1: play a move for the opponent on the designated square.
16 // Subturn 2: play a move for me (which just indicate a square).
17 getPotentialMovesFrom([x
, y
]) {
18 const color
= this.turn
;
19 const oppCol
= V
.GetOppCol(color
);
20 if (this.subTurn
== 2) {
21 // Just play a normal move (which in fact only indicate a square)
24 super.getPotentialMovesFrom([x
, y
])
26 // Filter promotions: keep only one, since no choice now.
27 if (m
.appear
[0].p
!= m
.vanish
[0].p
) {
28 const hash
= V
.CoordsToSquare(m
.start
) + V
.CoordsToSquare(m
.end
);
29 if (!movesHash
[hash
]) {
30 movesHash
[hash
] = true;
38 if (m
.vanish
.length
== 1) m
.appear
[0].p
= V
.GOAL
;
39 else m
.appear
[0].p
= V
.TARGET_CODE
[m
.vanish
[1].p
];
40 m
.appear
[0].c
= oppCol
;
46 // At subTurn == 1, play a targeted move for opponent
47 // Search for target (we could also have it in a stack...)
48 let target
= { x: -1, y: -1 };
49 outerLoop: for (let i
= 0; i
< V
.size
.x
; i
++) {
50 for (let j
= 0; j
< V
.size
.y
; j
++) {
51 if (this.board
[i
][j
] != V
.EMPTY
) {
52 const piece
= this.board
[i
][j
][1];
55 Object
.keys(V
.TARGET_DECODE
).includes(piece
)
57 target
= { x: i
, y: j
};
63 // TODO: could be more efficient than generating all moves.
65 const emptyTarget
= (this.board
[target
.x
][target
.y
][1] == V
.GOAL
);
66 if (emptyTarget
) this.board
[target
.x
][target
.y
] = V
.EMPTY
;
67 let moves
= super.getPotentialMovesFrom([x
, y
]);
69 this.board
[target
.x
][target
.y
] = color
+ V
.GOAL
;
80 return moves
.filter(m
=> m
.end
.x
== target
.x
&& m
.end
.y
== target
.y
);
83 canIplay(side
, [x
, y
]) {
84 const color
= this.getColor(x
, y
);
86 (this.subTurn
== 1 && color
!= side
) ||
87 (this.subTurn
== 2 && color
== side
)
92 if (b
[1] == V
.GOAL
|| Object
.keys(V
.TARGET_DECODE
).includes(b
[1]))
93 return "Ambiguous/" + b
;
97 // Code for empty square target
102 static get TARGET_DECODE() {
113 static get TARGET_CODE() {
124 static get PIECES() {
126 ChessRules
.PIECES
.concat(Object
.keys(V
.TARGET_DECODE
)).concat([V
.GOAL
])
130 getAllPotentialMoves() {
131 const color
= this.turn
;
132 let potentialMoves
= [];
133 for (let i
= 0; i
< V
.size
.x
; i
++) {
134 for (let j
= 0; j
< V
.size
.y
; j
++) {
135 const colIJ
= this.getColor(i
, j
);
137 this.board
[i
][j
] != V
.EMPTY
&&
139 (this.subTurn
== 2 && colIJ
== color
) ||
141 this.subTurn
== 1 && colIJ
!= color
&&
142 this.board
[i
][j
][1] != V
.GOAL
&&
143 !(Object
.keys(V
.TARGET_DECODE
).includes(this.board
[i
][j
][1]))
147 Array
.prototype.push
.apply(
149 this.getPotentialMovesFrom([i
, j
])
154 return potentialMoves
;
158 // Since there are no checks this seems true (same as for Magnetic...)
171 // This function is only called at subTurn 1
172 const color
= V
.GetOppCol(this.turn
);
173 if (this.kingPos
[color
][0] < 0) return (color
== 'w' ? "0-1" : "1-0");
178 const c
= V
.GetOppCol(this.turn
);
179 const piece
= move.vanish
[0].p
;
180 if (piece
== V
.KING
) {
182 this.kingPos
[c
][0] = move.appear
[0].x
;
183 this.kingPos
[c
][1] = move.appear
[0].y
;
185 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
186 // (My) king is captured:
187 this.kingPos
[this.turn
] = [-1, -1];
191 let kingCaptured
= false;
192 if (this.subTurn
== 1) {
194 this.epSquares
.push(this.getEpSquare(move));
195 kingCaptured
= this.kingPos
[this.turn
][0] < 0;
197 if (kingCaptured
) move.kingCaptured
= true;
198 V
.PlayOnBoard(this.board
, move);
199 if (this.subTurn
== 2 || kingCaptured
) {
200 this.turn
= V
.GetOppCol(this.turn
);
203 if (!kingCaptured
) this.subTurn
= 3 - this.subTurn
;
207 if (!move.kingCaptured
) this.subTurn
= 3 - this.subTurn
;
208 if (this.subTurn
== 2 || !!move.kingCaptured
) {
209 this.turn
= V
.GetOppCol(this.turn
);
212 V
.UndoOnBoard(this.board
, move);
213 if (this.subTurn
== 1) {
214 this.epSquares
.pop();
220 // (Potentially) Reset king(s) position
221 const c
= V
.GetOppCol(this.turn
);
222 const piece
= move.vanish
[0].p
;
223 if (piece
== V
.KING
) {
225 this.kingPos
[c
][0] = move.vanish
[0].x
;
226 this.kingPos
[c
][1] = move.vanish
[0].y
;
228 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
229 // (My) king was captured:
230 this.kingPos
[this.turn
] = [move.vanish
[1].x
, move.vanish
[1].y
];
233 static GenRandInitFen(randomness
) {
235 return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
237 let pieces
= { w: new Array(8), b: new Array(8) };
238 for (let c
of ["w", "b"]) {
239 if (c
== 'b' && randomness
== 1) {
240 pieces
['b'] = pieces
['w'];
244 // Get random squares for every piece, totally freely
245 let positions
= shuffle(ArrayFun
.range(8));
246 const composition
= ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
247 const rem2
= positions
[0] % 2;
248 if (rem2
== positions
[1] % 2) {
249 // Fix bishops (on different colors)
250 for (let i
=2; i
<8; i
++) {
251 if (positions
[i
] % 2 != rem2
)
252 [positions
[1], positions
[i
]] = [positions
[i
], positions
[1]];
255 for (let i
= 0; i
< 8; i
++) pieces
[c
][positions
[i
]] = composition
[i
];
258 pieces
["b"].join("") +
259 "/pppppppp/8/8/8/8/PPPPPPPP/" +
260 pieces
["w"].join("").toUpperCase() +
261 // En-passant allowed, but no flags
267 let moves
= this.getAllValidMoves();
268 if (moves
.length
== 0) return null;
269 // Random mover for now
270 const color
= this.turn
;
271 const m1
= moves
[randInt(moves
.length
)];
274 if (this.turn
!= color
) m
= m1
;
276 const moves2
= this.getAllValidMoves();
277 m
= [m1
, moves2
[randInt(moves2
.length
)]];
284 if (this.subTurn
== 2) return "T:" + V
.CoordsToSquare(move.end
);
285 // Remove and re-add target to get a good notation:
286 const withTarget
= move.vanish
[1];
287 if (move.vanish
[1].p
== V
.GOAL
) move.vanish
.pop();
288 else move.vanish
[1].p
= V
.TARGET_DECODE
[move.vanish
[1].p
];
289 const notation
= super.getNotation(move);
290 if (move.vanish
.length
== 1) move.vanish
.push(withTarget
);
291 else move.vanish
[1].p
= V
.TARGET_CODE
[move.vanish
[1].p
];