1 import { ChessRules
} from "@/base_rules";
2 import { randInt
} 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)
23 super.getPotentialMovesFrom([x
, y
])
25 if (m
.vanish
.length
== 1) m
.appear
[0].p
= V
.GOAL
;
26 else m
.appear
[0].p
= V
.TARGET_CODE
[m
.vanish
[1].p
];
27 m
.appear
[0].c
= oppCol
;
33 // At subTurn == 1, play a targeted move for opponent
34 // Search for target (we could also have it in a stack...)
35 let target
= { x: -1, y: -1 };
36 outerLoop: for (let i
= 0; i
< V
.size
.x
; i
++) {
37 for (let j
= 0; j
< V
.size
.y
; j
++) {
38 if (this.board
[i
][j
] != V
.EMPTY
) {
39 const piece
= this.board
[i
][j
][1];
42 Object
.keys(V
.TARGET_DECODE
).includes(piece
)
44 target
= { x: i
, y: j
};
50 // TODO: could be more efficient than generating all moves.
52 const emptyTarget
= (this.board
[target
.x
][target
.y
][1] == V
.GOAL
);
53 if (emptyTarget
) this.board
[target
.x
][target
.y
] = V
.EMPTY
;
54 let moves
= super.getPotentialMovesFrom([x
, y
]);
56 this.board
[target
.x
][target
.y
] = color
+ V
.GOAL
;
67 return moves
.filter(m
=> m
.end
.x
== target
.x
&& m
.end
.y
== target
.y
);
70 canIplay(side
, [x
, y
]) {
71 const color
= this.getColor(x
, y
);
73 (this.subTurn
== 1 && color
!= side
) ||
74 (this.subTurn
== 2 && color
== side
)
79 if (b
[1] == V
.GOAL
|| Object
.keys(V
.TARGET_DECODE
).includes(b
[1]))
80 return "Ambiguous/" + b
;
84 // Code for empty square target
89 static get TARGET_DECODE() {
100 static get TARGET_CODE() {
111 static get PIECES() {
113 ChessRules
.PIECES
.concat(Object
.keys(V
.TARGET_DECODE
)).concat([V
.GOAL
])
117 getAllPotentialMoves() {
118 const color
= this.turn
;
119 let potentialMoves
= [];
120 for (let i
= 0; i
< V
.size
.x
; i
++) {
121 for (let j
= 0; j
< V
.size
.y
; j
++) {
122 const colIJ
= this.getColor(i
, j
);
124 this.board
[i
][j
] != V
.EMPTY
&&
126 (this.subTurn
== 2 && colIJ
== color
) ||
128 this.subTurn
== 1 && colIJ
!= color
&&
129 this.board
[i
][j
][1] != V
.GOAL
&&
130 !(Object
.keys(V
.TARGET_DECODE
).includes(this.board
[i
][j
][1]))
134 Array
.prototype.push
.apply(
136 this.getPotentialMovesFrom([i
, j
])
141 return potentialMoves
;
145 // Since there are no checks this seems true (same as for Magnetic...)
158 // This function is only called at subTurn 1
159 const color
= V
.GetOppCol(this.turn
);
160 if (this.kingPos
[color
][0] < 0) return (color
== 'w' ? "0-1" : "1-0");
165 const c
= V
.GetOppCol(this.turn
);
166 const piece
= move.vanish
[0].p
;
167 if (piece
== V
.KING
) {
169 this.kingPos
[c
][0] = move.appear
[0].x
;
170 this.kingPos
[c
][1] = move.appear
[0].y
;
172 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
173 // (My) king is captured:
174 this.kingPos
[this.turn
] = [-1, -1];
178 let kingCaptured
= false;
179 if (this.subTurn
== 1) {
181 this.epSquares
.push(this.getEpSquare(move));
182 kingCaptured
= this.kingPos
[this.turn
][0] < 0;
184 if (kingCaptured
) move.kingCaptured
= true;
185 V
.PlayOnBoard(this.board
, move);
186 if (this.subTurn
== 2 || kingCaptured
) {
187 this.turn
= V
.GetOppCol(this.turn
);
190 if (!kingCaptured
) this.subTurn
= 3 - this.subTurn
;
194 if (!move.kingCaptured
) this.subTurn
= 3 - this.subTurn
;
195 if (this.subTurn
== 2 || !!move.kingCaptured
) {
196 this.turn
= V
.GetOppCol(this.turn
);
199 V
.UndoOnBoard(this.board
, move);
200 if (this.subTurn
== 1) {
201 this.epSquares
.pop();
207 // (Potentially) Reset king(s) position
208 const c
= V
.GetOppCol(this.turn
);
209 const piece
= move.vanish
[0].p
;
210 if (piece
== V
.KING
) {
212 this.kingPos
[c
][0] = move.vanish
[0].x
;
213 this.kingPos
[c
][1] = move.vanish
[0].y
;
215 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
216 // (My) king was captured:
217 this.kingPos
[this.turn
] = [move.vanish
[1].x
, move.vanish
[1].y
];
220 static GenRandInitFen(randomness
) {
222 return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
224 let pieces
= { w: new Array(8), b: new Array(8) };
225 for (let c
of ["w", "b"]) {
226 if (c
== 'b' && randomness
== 1) {
227 pieces
['b'] = pieces
['w'];
231 // Get random squares for every piece, totally freely
232 let positions
= shuffle(ArrayFun
.range(8));
233 const composition
= ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
234 const rem2
= positions
[0] % 2;
235 if (rem2
== positions
[1] % 2) {
236 // Fix bishops (on different colors)
237 for (let i
=2; i
<8; i
++) {
238 if (positions
[i
] % 2 != rem2
)
239 [positions
[1], positions
[i
]] = [positions
[i
], positions
[1]];
242 for (let i
= 0; i
< 8; i
++) pieces
[c
][positions
[i
]] = composition
[i
];
245 pieces
["b"].join("") +
246 "/pppppppp/8/8/8/8/PPPPPPPP/" +
247 pieces
["w"].join("").toUpperCase() +
248 // En-passant allowed, but no flags
254 let moves
= this.getAllValidMoves();
255 if (moves
.length
== 0) return null;
256 // Random mover for now
257 const color
= this.turn
;
258 const m1
= moves
[randInt(moves
.length
)];
261 if (this.turn
!= color
) m
= m1
;
263 const moves2
= this.getAllValidMoves();
264 m
= [m1
, moves2
[randInt(moves2
.length
)]];
271 if (this.subTurn
== 2) return "T:" + V
.CoordsToSquare(move.end
);
272 // Remove and re-add target to get a good notation:
273 const withTarget
= move.vanish
[1];
274 if (move.vanish
[1].p
== V
.GOAL
) move.vanish
.pop();
275 else move.vanish
[1].p
= V
.TARGET_DECODE
[move.vanish
[1].p
];
276 const notation
= super.getNotation(move);
277 if (move.vanish
.length
== 1) move.vanish
.push(withTarget
);
278 else move.vanish
[1].p
= V
.TARGET_CODE
[move.vanish
[1].p
];