1 import { ChessRules
} from "@/base_rules";
2 import { randInt
, shuffle
} from "@/utils/alea";
3 import { ArrayFun
} from "@/utils/array";
5 export class AmbiguousRules
extends ChessRules
{
7 static get HasFlags() {
11 setOtherVariables(fen
) {
12 super.setOtherVariables(fen
);
13 if (this.movesCount
== 0) this.subTurn
= 2;
14 else this.subTurn
= 1;
17 // Subturn 1: play a move for the opponent on the designated square.
18 // Subturn 2: play a move for me (which just indicate a square).
19 getPotentialMovesFrom([x
, y
]) {
20 const color
= this.turn
;
21 const oppCol
= V
.GetOppCol(color
);
22 if (this.subTurn
== 2) {
23 // Just play a normal move (which in fact only indicate a square)
26 super.getPotentialMovesFrom([x
, y
])
28 // Filter promotions: keep only one, since no choice now.
29 if (m
.appear
[0].p
!= m
.vanish
[0].p
) {
30 const hash
= V
.CoordsToSquare(m
.start
) + V
.CoordsToSquare(m
.end
);
31 if (!movesHash
[hash
]) {
32 movesHash
[hash
] = true;
40 if (m
.vanish
.length
== 1) m
.appear
[0].p
= V
.GOAL
;
41 else m
.appear
[0].p
= V
.TARGET_CODE
[m
.vanish
[1].p
];
42 m
.appear
[0].c
= oppCol
;
48 // At subTurn == 1, play a targeted move for opponent
49 // Search for target (we could also have it in a stack...)
50 let target
= { x: -1, y: -1 };
51 outerLoop: for (let i
= 0; i
< V
.size
.x
; i
++) {
52 for (let j
= 0; j
< V
.size
.y
; j
++) {
53 if (this.board
[i
][j
] != V
.EMPTY
) {
54 const piece
= this.board
[i
][j
][1];
57 Object
.keys(V
.TARGET_DECODE
).includes(piece
)
59 target
= { x: i
, y: j
};
65 // TODO: could be more efficient than generating all moves.
67 const emptyTarget
= (this.board
[target
.x
][target
.y
][1] == V
.GOAL
);
68 if (emptyTarget
) this.board
[target
.x
][target
.y
] = V
.EMPTY
;
69 let moves
= super.getPotentialMovesFrom([x
, y
]);
71 this.board
[target
.x
][target
.y
] = color
+ V
.GOAL
;
82 return moves
.filter(m
=> m
.end
.x
== target
.x
&& m
.end
.y
== target
.y
);
85 canIplay(side
, [x
, y
]) {
86 const color
= this.getColor(x
, y
);
88 (this.subTurn
== 1 && color
!= side
) ||
89 (this.subTurn
== 2 && color
== side
)
94 if (b
[1] == V
.GOAL
|| Object
.keys(V
.TARGET_DECODE
).includes(b
[1]))
95 return "Ambiguous/" + b
;
99 // Code for empty square target
104 static get TARGET_DECODE() {
115 static get TARGET_CODE() {
126 static get PIECES() {
128 ChessRules
.PIECES
.concat(Object
.keys(V
.TARGET_DECODE
)).concat([V
.GOAL
])
132 getAllPotentialMoves() {
133 const color
= this.turn
;
134 let potentialMoves
= [];
135 for (let i
= 0; i
< V
.size
.x
; i
++) {
136 for (let j
= 0; j
< V
.size
.y
; j
++) {
137 const colIJ
= this.getColor(i
, j
);
139 this.board
[i
][j
] != V
.EMPTY
&&
141 (this.subTurn
== 2 && colIJ
== color
) ||
143 this.subTurn
== 1 && colIJ
!= color
&&
144 this.board
[i
][j
][1] != V
.GOAL
&&
145 !(Object
.keys(V
.TARGET_DECODE
).includes(this.board
[i
][j
][1]))
149 Array
.prototype.push
.apply(
151 this.getPotentialMovesFrom([i
, j
])
156 return potentialMoves
;
160 // Since there are no checks this seems true (same as for Magnetic...)
173 // This function is only called at subTurn 1
174 const color
= V
.GetOppCol(this.turn
);
175 if (this.kingPos
[color
][0] < 0) return (color
== 'w' ? "0-1" : "1-0");
180 const c
= V
.GetOppCol(this.turn
);
181 const piece
= move.vanish
[0].p
;
182 if (piece
== V
.KING
) {
184 this.kingPos
[c
][0] = move.appear
[0].x
;
185 this.kingPos
[c
][1] = move.appear
[0].y
;
187 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
188 // (My) king is captured:
189 this.kingPos
[this.turn
] = [-1, -1];
193 let kingCaptured
= false;
194 if (this.subTurn
== 1) {
196 this.epSquares
.push(this.getEpSquare(move));
197 kingCaptured
= this.kingPos
[this.turn
][0] < 0;
199 if (kingCaptured
) move.kingCaptured
= true;
200 V
.PlayOnBoard(this.board
, move);
201 if (this.subTurn
== 2 || kingCaptured
) {
202 this.turn
= V
.GetOppCol(this.turn
);
205 if (!kingCaptured
) this.subTurn
= 3 - this.subTurn
;
209 if (!move.kingCaptured
) this.subTurn
= 3 - this.subTurn
;
210 if (this.subTurn
== 2 || !!move.kingCaptured
) {
211 this.turn
= V
.GetOppCol(this.turn
);
214 V
.UndoOnBoard(this.board
, move);
215 if (this.subTurn
== 1) {
216 this.epSquares
.pop();
222 // (Potentially) Reset king(s) position
223 const c
= V
.GetOppCol(this.turn
);
224 const piece
= move.vanish
[0].p
;
225 if (piece
== V
.KING
) {
227 this.kingPos
[c
][0] = move.vanish
[0].x
;
228 this.kingPos
[c
][1] = move.vanish
[0].y
;
230 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
231 // (My) king was captured:
232 this.kingPos
[this.turn
] = [move.vanish
[1].x
, move.vanish
[1].y
];
235 static GenRandInitFen(randomness
) {
237 return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
239 let pieces
= { w: new Array(8), b: new Array(8) };
240 for (let c
of ["w", "b"]) {
241 if (c
== 'b' && randomness
== 1) {
242 pieces
['b'] = pieces
['w'];
246 // Get random squares for every piece, totally freely
247 let positions
= shuffle(ArrayFun
.range(8));
248 const composition
= ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
249 const rem2
= positions
[0] % 2;
250 if (rem2
== positions
[1] % 2) {
251 // Fix bishops (on different colors)
252 for (let i
=2; i
<8; i
++) {
253 if (positions
[i
] % 2 != rem2
) {
254 [positions
[1], positions
[i
]] = [positions
[i
], positions
[1]];
259 for (let i
= 0; i
< 8; i
++) pieces
[c
][positions
[i
]] = composition
[i
];
262 pieces
["b"].join("") +
263 "/pppppppp/8/8/8/8/PPPPPPPP/" +
264 pieces
["w"].join("").toUpperCase() +
265 // En-passant allowed, but no flags
271 let moves
= this.getAllValidMoves();
272 if (moves
.length
== 0) return null;
273 // Random mover for now
274 const color
= this.turn
;
275 const m1
= moves
[randInt(moves
.length
)];
278 if (this.turn
!= color
) m
= m1
;
280 const moves2
= this.getAllValidMoves();
281 m
= [m1
, moves2
[randInt(moves2
.length
)]];
288 if (this.subTurn
== 2) return "T:" + V
.CoordsToSquare(move.end
);
289 // Remove and re-add target to get a good notation:
290 const withTarget
= move.vanish
[1];
291 if (move.vanish
[1].p
== V
.GOAL
) move.vanish
.pop();
292 else move.vanish
[1].p
= V
.TARGET_DECODE
[move.vanish
[1].p
];
293 const notation
= super.getNotation(move);
294 if (move.vanish
.length
== 1) move.vanish
.push(withTarget
);
295 else move.vanish
[1].p
= V
.TARGET_CODE
[move.vanish
[1].p
];