import { ChessRules, PiPo, Move } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
-import { randInt } from "@/utils/alea";
+import { shuffle } from "@/utils/alea";
+
+export class BaroqueRules extends ChessRules {
-export const VariantRules = class BaroqueRules extends ChessRules {
static get HasFlags() {
return false;
}
this.kingPos["w"] = [i, k];
break;
default: {
- const num = parseInt(position[i].charAt(j));
+ const num = parseInt(position[i].charAt(j), 10);
if (!isNaN(num)) k += num - 1;
}
}
}
return true; //immobilizer isn't neutralized
}
- // Chameleons can't be immobilized twice, because there is only one immobilizer
+ // Chameleons can't be immobilized twice,
+ // because there is only one immobilizer
if (oppPiece == V.BISHOP && piece == V.IMMOBILIZER) return true;
}
}
}
}
- getSlideNJumpMoves([x, y], steps, oneStep) {
- const piece = this.getPiece(x, y);
- let moves = [];
- outerLoop: for (let step of steps) {
- let i = x + step[0];
- let j = y + step[1];
- while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
- moves.push(this.getBasicMove([x, y], [i, j]));
- if (oneStep !== undefined) continue outerLoop;
- i += step[0];
- j += step[1];
- }
- // Only king can take on occupied square:
- if (piece == V.KING && V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
- moves.push(this.getBasicMove([x, y], [i, j]));
- }
- return moves;
+ canTake([x1, y1], [x2, y2]) {
+ return (
+ this.getPiece(x1, y1) == V.KING &&
+ this.getColor(x1, y1) != this.getColor(x2, y2)
+ );
}
// Modify capturing moves among listed pawn moves
const color = this.turn;
const oppCol = V.GetOppCol(color);
moves.forEach(m => {
- if (!!byChameleon && m.start.x != m.end.x && m.start.y != m.end.y) return; //chameleon not moving as pawn
+ if (!!byChameleon && m.start.x != m.end.x && m.start.y != m.end.y)
+ // Chameleon not moving as pawn
+ return;
// Try capturing in every direction
for (let step of steps) {
const sq2 = [m.end.x + 2 * step[0], m.end.y + 2 * step[1]];
) {
continue;
}
- // last(thing), cur(thing) : stop if "cur" is our color, or beyond board limits,
- // or if "last" isn't empty and cur neither. Otherwise, if cur is empty then
- // add move until cur square; if cur is occupied then stop if !!byChameleon and
- // the square not occupied by a leaper.
+ // last(thing), cur(thing) : stop if "cur" is our color,
+ // or beyond board limits, or if "last" isn't empty and cur neither.
+ // Otherwise, if cur is empty then add move until cur square;
+ // if cur is occupied then stop if !!byChameleon and the square not
+ // occupied by a leaper.
let last = [i, j];
let cur = [i + step[0], j + step[1]];
let vanished = [new PiPo({ x: x, y: y, c: color, p: piece })];
mergedMoves[key].vanish.push(m.vanish[i]);
}
});
- // Finally return an array
- moves = [];
- Object.keys(mergedMoves).forEach(k => {
- moves.push(mergedMoves[k]);
- });
- return moves;
+ return Object.values(mergedMoves);
}
addQueenCaptures(moves, byChameleon) {
return super.getPotentialQueenMoves(sq);
}
- getPotentialKingMoves(sq) {
- return this.getSlideNJumpMoves(
- sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
- "oneStep"
- );
- }
-
// isAttacked() is OK because the immobilizer doesn't take
- isAttackedByPawn([x, y], colors) {
+ isAttackedByPawn([x, y], color) {
// Square (x,y) must be surroundable by two enemy pieces,
// and one of them at least should be a pawn (moving).
const dirs = [
const [i2, j2] = [x + dir[0], y + dir[1]]; //"after"
if (V.OnBoard(i1, j1) && V.OnBoard(i2, j2)) {
if (
- (this.board[i1][j1] != V.EMPTY &&
- colors.includes(this.getColor(i1, j1)) &&
- this.board[i2][j2] == V.EMPTY) ||
- (this.board[i2][j2] != V.EMPTY &&
- colors.includes(this.getColor(i2, j2)) &&
- this.board[i1][j1] == V.EMPTY)
+ (
+ this.board[i1][j1] != V.EMPTY &&
+ this.getColor(i1, j1) == color &&
+ this.board[i2][j2] == V.EMPTY
+ )
+ ||
+ (
+ this.board[i2][j2] != V.EMPTY &&
+ this.getColor(i2, j2) == color &&
+ this.board[i1][j1] == V.EMPTY
+ )
) {
// Search a movable enemy pawn landing on the empty square
for (let step of steps) {
}
if (
V.OnBoard(i3, j3) &&
- colors.includes(this.getColor(i3, j3)) &&
+ this.getColor(i3, j3) == color &&
this.getPiece(i3, j3) == V.PAWN &&
!this.isImmobilized([i3, j3])
) {
return false;
}
- isAttackedByRook([x, y], colors) {
+ isAttackedByRook([x, y], color) {
// King must be on same column or row,
// and a rook should be able to reach a capturing square
- // colors contains only one element, giving the oppCol and thus king position
- const sameRow = x == this.kingPos[colors[0]][0];
- const sameColumn = y == this.kingPos[colors[0]][1];
+ const sameRow = x == this.kingPos[color][0];
+ const sameColumn = y == this.kingPos[color][1];
if (sameRow || sameColumn) {
// Look for the enemy rook (maximum 1)
for (let i = 0; i < V.size.x; i++) {
for (let j = 0; j < V.size.y; j++) {
if (
this.board[i][j] != V.EMPTY &&
- colors.includes(this.getColor(i, j)) &&
+ this.getColor(i, j) == color &&
this.getPiece(i, j) == V.ROOK
) {
- if (this.isImmobilized([i, j])) return false; //because only one rook
- // Can it reach a capturing square?
- // Easy but quite suboptimal way (TODO): generate all moves (turn is OK)
+ if (this.isImmobilized([i, j]))
+ // Because only one rook:
+ return false;
+ // Can it reach a capturing square? Easy but quite suboptimal way
+ // (TODO: generate all moves (turn is OK))
const moves = this.getPotentialMovesFrom([i, j]);
for (let move of moves) {
if (
(sameRow && move.end.y == y) ||
(sameColumn && move.end.x == x)
- )
+ ) {
return true;
+ }
}
}
}
return false;
}
- isAttackedByKnight([x, y], colors) {
+ isAttackedByKnight([x, y], color) {
// Square (x,y) must be on same line as a knight,
// and there must be empty square(s) behind.
const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
j -= step[1];
}
if (V.OnBoard(i, j)) {
- if (colors.includes(this.getColor(i, j))) {
+ if (this.getColor(i, j) == color) {
if (
this.getPiece(i, j) == V.KNIGHT &&
!this.isImmobilized([i, j])
- )
+ ) {
return true;
+ }
continue outerLoop;
}
- // [else] Our color, could be captured *if there was an empty space*
+ // [else] Our color,
+ // could be captured *if there was an empty space*
if (this.board[i + step[0]][j + step[1]] != V.EMPTY)
continue outerLoop;
i -= step[0];
return false;
}
- isAttackedByBishop([x, y], colors) {
+ isAttackedByBishop([x, y], color) {
// We cheat a little here: since this function is used exclusively for
// the king, it's enough to check the immediate surrounding of the square.
const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
if (
V.OnBoard(i, j) &&
this.board[i][j] != V.EMPTY &&
- colors.includes(this.getColor(i, j)) &&
- this.getPiece(i, j) == V.BISHOP
+ this.getColor(i, j) == color &&
+ this.getPiece(i, j) == V.BISHOP &&
+ !this.isImmobilized([i, j])
) {
- return true; //bishops are never immobilized
+ return true;
}
}
return false;
}
- isAttackedByQueen([x, y], colors) {
+ isAttackedByQueen([x, y], color) {
// Square (x,y) must be adjacent to a queen, and the queen must have
// some free space in the opposite direction from (x,y)
const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
const sq1 = [x + step[0], y + step[1]];
if (
this.board[sq1[0]][sq1[1]] != V.EMPTY &&
- colors.includes(this.getColor(sq1[0], sq1[1])) &&
+ this.getColor(sq1[0], sq1[1]) == color &&
this.getPiece(sq1[0], sq1[1]) == V.QUEEN &&
!this.isImmobilized(sq1)
) {
return false;
}
- isAttackedByKing([x, y], colors) {
+ isAttackedByKing([x, y], color) {
const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
for (let step of steps) {
let rx = x + step[0],
if (
V.OnBoard(rx, ry) &&
this.getPiece(rx, ry) === V.KING &&
- colors.includes(this.getColor(rx, ry)) &&
+ this.getColor(rx, ry) == color &&
!this.isImmobilized([rx, ry])
) {
return true;
return false;
}
+ static GenRandInitFen(options) {
+ if (options.randomness == 0)
+ // Deterministic:
+ return "rnbkqbnm/pppppppp/8/8/8/8/PPPPPPPP/MNBQKBNR w 0";
+
+ let pieces = { w: new Array(8), b: new Array(8) };
+ // Shuffle pieces on first and last rank
+ for (let c of ["w", "b"]) {
+ if (c == 'b' && options.randomness == 1) {
+ pieces['b'] = pieces['w'];
+ break;
+ }
+
+ // Get random squares for every piece, totally freely
+ let positions = shuffle(ArrayFun.range(8));
+ const composition = ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'm'];
+ for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i];
+ }
+ return (
+ pieces["b"].join("") +
+ "/pppppppp/8/8/8/8/PPPPPPPP/" +
+ pieces["w"].join("").toUpperCase() +
+ " w 0"
+ );
+ }
+
static get VALUES() {
return {
p: 1,
return 2;
}
- static GenRandInitFen() {
- let pieces = { w: new Array(8), b: new Array(8) };
- // Shuffle pieces on first and last rank
- for (let c of ["w", "b"]) {
- let positions = ArrayFun.range(8);
- // Get random squares for every piece, totally freely
-
- let randIndex = randInt(8);
- const bishop1Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(7);
- const bishop2Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(6);
- const knight1Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(5);
- const knight2Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(4);
- const queenPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(3);
- const kingPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(2);
- const rookPos = positions[randIndex];
- positions.splice(randIndex, 1);
- const immobilizerPos = positions[0];
-
- pieces[c][bishop1Pos] = "b";
- pieces[c][bishop2Pos] = "b";
- pieces[c][knight1Pos] = "n";
- pieces[c][knight2Pos] = "n";
- pieces[c][queenPos] = "q";
- pieces[c][kingPos] = "k";
- pieces[c][rookPos] = "r";
- pieces[c][immobilizerPos] = "m";
- }
- return (
- pieces["b"].join("") +
- "/pppppppp/8/8/8/8/PPPPPPPP/" +
- pieces["w"].join("").toUpperCase() +
- " w 0"
- );
- }
-
getNotation(move) {
const initialSquare = V.CoordsToSquare(move.start);
const finalSquare = V.CoordsToSquare(move.end);
if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
return notation;
}
+
};