import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-// TODO: issue with undo of specialisation to cover check, subTurn decremented to 0
-
export class BarioRules extends ChessRules {
// Does not really seem necessary (although the author mention it)
// Instead, first move = pick a square for the king.
- static get HasCastle() {
+ static get HasFlags() {
return false;
}
);
}
- hoverHighlight(x, y) {
+ hoverHighlight([x, y]) {
const c = this.turn;
return (
this.movesCount <= 1 &&
);
}
+ onlyClick([x, y]) {
+ return (
+ this.movesCount <= 1 ||
+ // TODO: next line theoretically shouldn't be required...
+ (this.movesCount == 2 && this.getColor(x, y) != this.turn)
+ );
+ }
+
// Initiate the game by choosing a square for the king:
doClick(square) {
const c = this.turn;
appear: [
new PiPo({ x: square[0], y: square[1], c: c, p: V.KING })
],
- vanish: [],
+ vanish: [
+ new PiPo({ x: square[0], y: square[1], c: c, p: V.UNDEFINED })
+ ],
start: { x: -1, y: -1 },
});
}
getReserveFen() {
let counts = new Array(8);
for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
- counts[i] = this.reserve["w"][V.PIECES[i]];
- counts[4 + i] = this.reserve["b"][V.PIECES[i]];
+ counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]];
+ counts[4 + i] = this.reserve["b"][V.RESERVE_PIECES[i]];
}
return counts.join("");
}
}
static GenRandInitFen() {
- return "8/pppppppp/8/8/8/8/PPPPPPPP/8 w 0 - 22212221 -";
+ return "uuuuuuuu/pppppppp/8/8/8/8/PPPPPPPP/UUUUUUUU w 0 - 22212221 -";
}
setOtherVariables(fen) {
if (this.subTurn == 0) {
const L = this.captureUndefined.length;
const cu = this.captureUndefined[L-1];
- return (
+ return [
+ // Nothing changes on the board, just mark start.p for reserve update
new Move({
- appear: [
- new PiPo({ x: cu.x, y: cu.y, c: color, p: p })
- ],
- vanish: [
- new PiPo({ x: cu.x, y: cu.y, c: color, p: V.UNDEFINED })
- ],
- start: { x: x, y: y }
+ appear: [],
+ vanish: [],
+ start: { x: x, y: y, p: p },
+ end: { x: cu.x, y: cu.y }
})
- );
+ ];
}
// or, subTurn == 1 => target any undefined piece that we own.
let moves = [];
return super.getPotentialMovesFrom([x, y]);
}
- getAllValidMoves() {
+ getAllPotentialMoves() {
+ const color = this.turn;
+ if (this.movesCount <= 1) {
+ // Just put the king on the board
+ const firstRank = (color == 'w' ? 7 : 0);
+ return [...Array(8)].map((x, j) => {
+ return new Move({
+ appear: [
+ new PiPo({ x: firstRank, y: j, c: color, p: V.KING })
+ ],
+ vanish: [
+ new PiPo({ x: firstRank, y: j, c: color, p: V.UNDEFINED })
+ ],
+ start: { x: -1, y: -1 }
+ });
+ });
+ }
const getAllReserveMoves = () => {
let moves = [];
- const color = this.turn;
for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
moves = moves.concat(
this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i])
}
filterValid(moves) {
+ if (this.movesCount <= 1) return moves;
const color = this.turn;
return moves.filter(m => {
- if (m.vanish.length == 0) return true;
+ if (m.vanish.length == 0) {
+ // subTurn == 0: need to check if a move exists at subTurn == 1
+ this.play(m);
+ const res = this.filterValid(this.getAllPotentialMoves()).length > 0;
+ this.undo(m);
+ return res;
+ }
const start = { x: m.vanish[0].x, y: m.vanish[0].y };
const end = { x: m.appear[0].x, y: m.appear[0].y };
- if (start.x == end.x && start.y == end.y) return true; //unfinished turn
+ if (start.x == end.x && start.y == end.y) {
+ // Unfinished turn: require careful check
+ this.play(m);
+ let res = false;
+ if (this.subTurn == 1)
+ // Can either play a move, or specialize a piece
+ res = this.filterValid(this.getAllPotentialMoves()).length > 0;
+ else {
+ // subTurn == 2: can only play a specialized piece
+ res = this.filterValid(
+ this.getPotentialMovesFrom([m.end.x, m.end.y])).length > 0;
+ }
+ this.undo(m);
+ return res;
+ }
this.play(m);
const res = !this.underCheck(color);
this.undo(m);
}
return false;
};
- if (this.subTurn == 0) return true; //always one reserve for an undefined
- if (!super.atLeastOneMove()) return atLeastOneReserveMove();
- return true;
+ if (this.subTurn == 0) return atLeastOneReserveMove();
+ const canMoveSomething = super.atLeastOneMove();
+ if (this.subTurn == 2) return canMoveSomething;
+ return (canMoveSomething || atLeastOneReserveMove());
}
underCheck(color) {
];
let [i, j] = [x1 + step[0], y1 + step[1]];
while (i != x2 || j != y2) {
- if (this.board[i][j] != V.EMPTY) return false;
+ if (!V.OnBoard(i, j) || this.board[i][j] != V.EMPTY) return false;
i += step[0];
j += step[1];
}
return false;
}
+ getCheckSquares() {
+ if (this.movesCount <= 2) return [];
+ return super.getCheckSquares();
+ }
+
play(move) {
+ move.turn = [this.turn, this.subTurn]; //easier undo (TODO?)
const toNextPlayer = () => {
V.PlayOnBoard(this.board, move);
this.turn = V.GetOppCol(this.turn);
this.movesCount++;
this.postPlay(move);
};
- if (move.vanish.length == 0) {
- toNextPlayer();
- return;
+ if (this.movesCount <= 1) toNextPlayer();
+ else if (move.vanish.length == 0) {
+ // Removal (subTurn == 0 --> 1)
+ this.reserve[this.turn][move.start.p]--;
+ this.subTurn++;
}
- const start = { x: move.vanish[0].x, y: move.vanish[0].y };
- const end = { x: move.appear[0].x, y: move.appear[0].y };
- if (start.x == end.x && start.y == end.y) {
- // Specialisation (subTurn == 1 before 2), or Removal (subTurn == 0).
- // In both cases, turn not over, and a piece removed from reserve
- this.reserve[this.turn][move.appear[0].p]--;
- if (move.appear[0].c == move.vanish[0].c) {
- // Specialisation: play "move" on board
+ else {
+ const start = { x: move.vanish[0].x, y: move.vanish[0].y };
+ const end = { x: move.appear[0].x, y: move.appear[0].y };
+ if (start.x == end.x && start.y == end.y) {
+ // Specialisation (subTurn == 1 before 2)
+ this.reserve[this.turn][move.appear[0].p]--;
V.PlayOnBoard(this.board, move);
this.definitions.push(move.end);
+ this.subTurn++;
+ }
+ else {
+ // Normal move (subTurn 1 or 2: change turn)
+ this.epSquares.push(this.getEpSquare(move));
+ toNextPlayer();
}
- this.subTurn++;
- }
- else {
- // Normal move (subTurn 1 or 2: change turn)
- this.epSquares.push(this.getEpSquare(move));
- toNextPlayer();
}
}
postPlay(move) {
const color = V.GetOppCol(this.turn);
- if (move.vanish.length == 0) {
- this.kingPos[color] = [move.end.x, move.end.y];
- const firstRank = (color == 'w' ? 7 : 0);
- for (let j = 0; j < 8; j++) {
- if (j != move.end.y) this.board[firstRank][j] = color + V.UNDEFINED;
- }
- }
+ if (this.movesCount <= 2) this.kingPos[color] = [move.end.x, move.end.y];
else {
if (move.vanish.length == 2 && move.vanish[1].p == V.UNDEFINED)
this.captureUndefined.push(move.end);
+ else this.captureUndefined.push(null);
if (move.appear[0].p == V.KING) super.postPlay(move);
else {
// If now all my pieces are defined, back to undefined state,
})
) {
const piecesList = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
- let myPieces = {};
+ const oppCol = this.turn;
+ let definedPieces = { w: {}, b: {} };
for (let i=0; i<8; i++) {
for (let j=0; j<8; j++) {
- if (
- this.board[i][j] != V.EMPTY &&
- this.getColor(i, j) == color
- ) {
+ if (this.board[i][j] != V.EMPTY) {
const p = this.getPiece(i, j);
- if (piecesList.includes(p))
- myPieces[p] = (!myPieces[p] ? 1 : myPieces[p] + 1);
+ const c = this.getColor(i, j);
+ if (piecesList.includes(p)) {
+ definedPieces[c][p] =
+ (!definedPieces[c][p] ? 1 : definedPieces[c][p] + 1);
+ }
}
}
}
- const pk = Object.keys(myPieces);
- if (pk.length >= 2) {
+ const my_pk = Object.keys(definedPieces[color]);
+ const opp_pk = Object.keys(definedPieces[oppCol]);
+ const oppRevert = (
+ opp_pk.length >= 2 ||
+ (
+ // Only one opponent's piece is defined, but
+ // at least a different piece wait in reserve:
+ opp_pk.length == 1 &&
+ Object.keys(this.reserve[oppCol]).some(k => {
+ return (k != opp_pk[0] && this.reserve[oppCol][k] >= 1);
+ })
+ )
+ );
+ if (my_pk.length >= 2 || oppRevert) {
+ // NOTE: necessary HACK... because the move is played already.
+ V.UndoOnBoard(this.board, move);
move.position = this.getBaseFen();
- for (let p of pk) this.reserve[color][p] = myPieces[p];
- for (let i=0; i<8; i++) {
- for (let j=0; j<8; j++) {
- if (
- this.board[i][j] != V.EMPTY &&
- this.getColor(i, j) == color &&
- piecesList.includes(this.getPiece(i, j))
- ) {
- this.board[i][j] = color + V.UNDEFINED;
+ move.reserve = JSON.parse(JSON.stringify(this.reserve));
+ V.PlayOnBoard(this.board, move);
+ for (
+ let cp of [{ c: color, pk: my_pk }, { c: oppCol, pk: opp_pk }]
+ ) {
+ if (cp.pk.length >= 2 || (cp.c == oppCol && oppRevert)) {
+ for (let p of cp.pk)
+ this.reserve[cp.c][p] += definedPieces[cp.c][p];
+ for (let i=0; i<8; i++) {
+ for (let j=0; j<8; j++) {
+ if (
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == cp.c &&
+ piecesList.includes(this.getPiece(i, j))
+ ) {
+ this.board[i][j] = cp.c + V.UNDEFINED;
+ }
+ }
}
}
}
undo(move) {
const toPrevPlayer = () => {
V.UndoOnBoard(this.board, move);
- this.turn = V.GetOppCol(this.turn);
+ [this.turn, this.subTurn] = move.turn;
this.movesCount--;
this.postUndo(move);
};
- if (move.vanish.length == 0) {
- toPrevPlayer();
- return;
+ if (this.movesCount <= 2 && move.appear[0].p == V.KING) toPrevPlayer();
+ else if (move.vanish.length == 0) {
+ this.reserve[this.turn][move.start.p]++;
+ this.subTurn = move.turn[1];
}
- const start = { x: move.vanish[0].x, y: move.vanish[0].y };
- const end = { x: move.appear[0].x, y: move.appear[0].y };
- if (start.x == end.x && start.y == end.y) {
- this.reserve[this.turn][move.appear[0].p]++;
- if (move.appear[0].c == move.vanish[0].c) {
+ else {
+ const start = { x: move.vanish[0].x, y: move.vanish[0].y };
+ const end = { x: move.appear[0].x, y: move.appear[0].y };
+ if (start.x == end.x && start.y == end.y) {
+ this.reserve[this.turn][move.appear[0].p]++;
V.UndoOnBoard(this.board, move);
this.definitions.pop();
+ this.subTurn = move.turn[1];
+ }
+ else {
+ this.epSquares.pop();
+ toPrevPlayer();
}
- this.subTurn--;
- }
- else {
- this.epSquares.pop();
- toPrevPlayer();
}
}
postUndo(move) {
const color = this.turn;
- if (move.vanish.length == 0) {
- this.kingPos[color] = [-1, -1];
- const firstRank = (color == 'w' ? 7 : 0);
- for (let j = 0; j < 8; j++) this.board[firstRank][j] = "";
- }
+ if (this.movesCount <= 1) this.kingPos[color] = [-1, -1];
else {
- if (move.vanish.length == 2 && move.vanish[1].p == V.UNDEFINED)
- this.captureUndefined.pop();
+ this.captureUndefined.pop();
if (move.appear[0].p == V.KING) super.postUndo(move);
else {
if (!!move.position) {
this.board = V.GetBoard(move.position);
- this.reserve[color] = {
- [V.ROOK]: 0,
- [V.KNIGHT]: 0,
- [V.BISHOP]: 0,
- [V.QUEEN]: 0
- }
+ this.reserve = move.reserve;
}
}
}
}
getComputerMove() {
+ let initMoves = this.getAllValidMoves();
+ if (initMoves.length == 0) return null;
+ // Loop until valid move is found (no un-specifiable piece...)
const color = this.turn;
- // Just play at random for now...
- let mvArray = [];
- while (this.turn == color) {
- const moves = this.getAllValidMoves();
- const choice = moves[randInt(moves.length)];
- mvArray.push(choice);
- this.play(choice);
+ while (true) {
+ let moves = JSON.parse(JSON.stringify(initMoves));
+ let mvArray = [];
+ let mv = null;
+ // Just play random moves (for now at least. TODO?)
+ while (moves.length > 0) {
+ mv = moves[randInt(moves.length)];
+ mvArray.push(mv);
+ this.play(mv);
+ if (this.turn == color) {
+ if (this.subTurn == 1) moves = this.getAllValidMoves();
+ else {
+ // subTurn == 2
+ moves = this.filterValid(
+ this.getPotentialMovesFrom([mv.end.x, mv.end.y]));
+ }
+ }
+ else break;
+ }
+ const thisIsTheEnd = (this.turn != color);
+ for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
+ if (thisIsTheEnd) return (mvArray.length > 1 ? mvArray : mvArray[0]);
}
- for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
- return (mvArray.length == 1? mvArray[0] : mvArray);
+ return null; //never reached
}
static get VALUES() {
// NOTE: evalPosition is wrong, but unused (random mover)
getNotation(move) {
- const end = { x: move.appear[0].x, y: move.appear[0].y };
+ const end = { x: move.end.x, y: move.end.y };
const endSquare = V.CoordsToSquare(end);
+ if (move.appear.length == 0)
+ // Removal
+ return move.start.p.toUpperCase() + endSquare + "X";
if (move.vanish.length == 0) return "K@" + endSquare;
const start = { x: move.vanish[0].x, y: move.vanish[0].y };
- if (start.x == end.x && start.y == end.y) {
- // Something is specialized, or removed
- const symbol = move.appear[0].p.toUpperCase();
- if (move.appear[0].c == move.vanish[0].c)
- // Specialisation
- return symbol + "@" + endSquare;
- // Removal:
- return symbol + endSquare + "X";
- }
+ if (start.x == end.x && start.y == end.y)
+ // Something is specialized
+ return move.appear[0].p.toUpperCase() + "@" + endSquare;
// Normal move
return super.getNotation(move);
}