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
{
6 static get HasFlags() {
10 setOtherVariables(fen
) {
11 super.setOtherVariables(fen
);
12 if (this.movesCount
== 0) this.subTurn
= 2;
13 else this.subTurn
= 1;
16 // Subturn 1: play a move for the opponent on the designated square.
17 // Subturn 2: play a move for me (which just indicate a square).
18 getPotentialMovesFrom([x
, y
]) {
19 const color
= this.turn
;
20 const oppCol
= V
.GetOppCol(color
);
21 if (this.subTurn
== 2) {
22 // Just play a normal move (which in fact only indicate a square)
25 super.getPotentialMovesFrom([x
, y
])
27 // Filter promotions: keep only one, since no choice now.
28 if (m
.appear
[0].p
!= m
.vanish
[0].p
) {
29 const hash
= V
.CoordsToSquare(m
.start
) + V
.CoordsToSquare(m
.end
);
30 if (!movesHash
[hash
]) {
31 movesHash
[hash
] = true;
39 if (m
.vanish
.length
== 1) m
.appear
[0].p
= V
.GOAL
;
40 else m
.appear
[0].p
= V
.TARGET_CODE
[m
.vanish
[1].p
];
41 m
.appear
[0].c
= oppCol
;
47 // At subTurn == 1, play a targeted move for opponent
48 // Search for target (we could also have it in a stack...)
49 let target
= { x: -1, y: -1 };
50 outerLoop: for (let i
= 0; i
< V
.size
.x
; i
++) {
51 for (let j
= 0; j
< V
.size
.y
; j
++) {
52 if (this.board
[i
][j
] != V
.EMPTY
) {
53 const piece
= this.board
[i
][j
][1];
56 Object
.keys(V
.TARGET_DECODE
).includes(piece
)
58 target
= { x: i
, y: j
};
64 // TODO: could be more efficient than generating all moves.
66 const emptyTarget
= (this.board
[target
.x
][target
.y
][1] == V
.GOAL
);
67 if (emptyTarget
) this.board
[target
.x
][target
.y
] = V
.EMPTY
;
68 let moves
= super.getPotentialMovesFrom([x
, y
]);
70 this.board
[target
.x
][target
.y
] = color
+ V
.GOAL
;
81 return moves
.filter(m
=> m
.end
.x
== target
.x
&& m
.end
.y
== target
.y
);
84 canIplay(side
, [x
, y
]) {
85 const color
= this.getColor(x
, y
);
87 (this.subTurn
== 1 && color
!= side
) ||
88 (this.subTurn
== 2 && color
== side
)
93 if (b
[1] == V
.GOAL
|| Object
.keys(V
.TARGET_DECODE
).includes(b
[1]))
94 return "Ambiguous/" + b
;
98 // Code for empty square target
103 static get TARGET_DECODE() {
114 static get TARGET_CODE() {
125 static get PIECES() {
127 ChessRules
.PIECES
.concat(Object
.keys(V
.TARGET_DECODE
)).concat([V
.GOAL
])
131 getAllPotentialMoves() {
132 const color
= this.turn
;
133 let potentialMoves
= [];
134 for (let i
= 0; i
< V
.size
.x
; i
++) {
135 for (let j
= 0; j
< V
.size
.y
; j
++) {
136 const colIJ
= this.getColor(i
, j
);
138 this.board
[i
][j
] != V
.EMPTY
&&
140 (this.subTurn
== 2 && colIJ
== color
) ||
142 this.subTurn
== 1 && colIJ
!= color
&&
143 this.board
[i
][j
][1] != V
.GOAL
&&
144 !(Object
.keys(V
.TARGET_DECODE
).includes(this.board
[i
][j
][1]))
148 Array
.prototype.push
.apply(
150 this.getPotentialMovesFrom([i
, j
])
155 return potentialMoves
;
159 // Since there are no checks this seems true (same as for Magnetic...)
172 // This function is only called at subTurn 1
173 const color
= V
.GetOppCol(this.turn
);
174 if (this.kingPos
[color
][0] < 0) return (color
== 'w' ? "0-1" : "1-0");
179 const c
= V
.GetOppCol(this.turn
);
180 const piece
= move.vanish
[0].p
;
181 if (piece
== V
.KING
) {
183 this.kingPos
[c
][0] = move.appear
[0].x
;
184 this.kingPos
[c
][1] = move.appear
[0].y
;
186 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
187 // (My) king is captured:
188 this.kingPos
[this.turn
] = [-1, -1];
192 let kingCaptured
= false;
193 if (this.subTurn
== 1) {
195 this.epSquares
.push(this.getEpSquare(move));
196 kingCaptured
= this.kingPos
[this.turn
][0] < 0;
198 if (kingCaptured
) move.kingCaptured
= true;
199 V
.PlayOnBoard(this.board
, move);
200 if (this.subTurn
== 2 || kingCaptured
) {
201 this.turn
= V
.GetOppCol(this.turn
);
204 if (!kingCaptured
) this.subTurn
= 3 - this.subTurn
;
208 if (!move.kingCaptured
) this.subTurn
= 3 - this.subTurn
;
209 if (this.subTurn
== 2 || !!move.kingCaptured
) {
210 this.turn
= V
.GetOppCol(this.turn
);
213 V
.UndoOnBoard(this.board
, move);
214 if (this.subTurn
== 1) {
215 this.epSquares
.pop();
221 // (Potentially) Reset king(s) position
222 const c
= V
.GetOppCol(this.turn
);
223 const piece
= move.vanish
[0].p
;
224 if (piece
== V
.KING
) {
226 this.kingPos
[c
][0] = move.vanish
[0].x
;
227 this.kingPos
[c
][1] = move.vanish
[0].y
;
229 if (move.vanish
.length
== 2 && [V
.KING
, 'l'].includes(move.vanish
[1].p
))
230 // (My) king was captured:
231 this.kingPos
[this.turn
] = [move.vanish
[1].x
, move.vanish
[1].y
];
234 static GenRandInitFen(randomness
) {
236 return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
238 let pieces
= { w: new Array(8), b: new Array(8) };
239 for (let c
of ["w", "b"]) {
240 if (c
== 'b' && randomness
== 1) {
241 pieces
['b'] = pieces
['w'];
245 // Get random squares for every piece, totally freely
246 let positions
= shuffle(ArrayFun
.range(8));
247 const composition
= ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
248 const rem2
= positions
[0] % 2;
249 if (rem2
== positions
[1] % 2) {
250 // Fix bishops (on different colors)
251 for (let i
=2; i
<8; i
++) {
252 if (positions
[i
] % 2 != rem2
)
253 [positions
[1], positions
[i
]] = [positions
[i
], positions
[1]];
256 for (let i
= 0; i
< 8; i
++) pieces
[c
][positions
[i
]] = composition
[i
];
259 pieces
["b"].join("") +
260 "/pppppppp/8/8/8/8/PPPPPPPP/" +
261 pieces
["w"].join("").toUpperCase() +
262 // En-passant allowed, but no flags
268 let moves
= this.getAllValidMoves();
269 if (moves
.length
== 0) return null;
270 // Random mover for now
271 const color
= this.turn
;
272 const m1
= moves
[randInt(moves
.length
)];
275 if (this.turn
!= color
) m
= m1
;
277 const moves2
= this.getAllValidMoves();
278 m
= [m1
, moves2
[randInt(moves2
.length
)]];
285 if (this.subTurn
== 2) return "T:" + V
.CoordsToSquare(move.end
);
286 // Remove and re-add target to get a good notation:
287 const withTarget
= move.vanish
[1];
288 if (move.vanish
[1].p
== V
.GOAL
) move.vanish
.pop();
289 else move.vanish
[1].p
= V
.TARGET_DECODE
[move.vanish
[1].p
];
290 const notation
= super.getNotation(move);
291 if (move.vanish
.length
== 1) move.vanish
.push(withTarget
);
292 else move.vanish
[1].p
= V
.TARGET_CODE
[move.vanish
[1].p
];