import { randInt, sample } from "@/utils/alea";
export class CoregalRules extends ChessRules {
+
static IsGoodPosition(position) {
- if (!super.IsGoodPosition(position)) return false;
+ if (!ChessRules.IsGoodPosition(position)) return false;
const rows = position.split("/");
// Check that at least one queen of each color is there:
let queens = {};
return !!flags.match(/^[a-z]{8,8}$/);
}
- // Scanning king position for faster updates is still interesting,
- // but no need for INIT_COL_KING because it's given in castle flags.
+ // Scanning king position for faster updates is still interesting.
scanKings(fen) {
this.kingPos = { w: [-1, -1], b: [-1, -1] };
const fenRows = V.ParseFen(fen).position.split("/");
this.kingPos["w"] = [i, k];
break;
default: {
- const num = parseInt(fenRows[i].charAt(j));
+ const num = parseInt(fenRows[i].charAt(j), 10);
if (!isNaN(num)) k += num - 1;
}
}
}
}
- getCheckSquares(color) {
+ getPPpath(m) {
+ if (
+ m.vanish.length == 2 &&
+ m.appear.length == 2 &&
+ m.vanish[0].p == V.QUEEN
+ ) {
+ // Large castle: show castle symbol
+ return "Coregal/castle";
+ }
+ return super.getPPpath(m);
+ }
+
+ getCheckSquares() {
+ const color = this.turn;
let squares = [];
const oppCol = V.GetOppCol(color);
if (this.isAttacked(this.kingPos[color], oppCol))
}
// Get random squares for king and queen between b and g files
- let randIndex = randInt(6);
- let kingPos = randIndex + 1;
- randIndex = randInt(5);
+ let randIndex = randInt(6) + 1;
+ let kingPos = randIndex;
+ randIndex = randInt(5) + 1;
if (randIndex >= kingPos) randIndex++;
- let queenPos = randIndex + 1;
+ let queenPos = randIndex;
// Get random squares for rooks to the left and right of the queen
// and king: not all squares of the same colors (for bishops).
if (!!bishop1Options[pos]) delete bishop1Options[pos];
else if (!!bishop2Options[pos]) delete bishop2Options[pos];
});
- const bishop1Pos = parseInt(sample(Object.keys(bishop1Options), 1)[0]);
- const bishop2Pos = parseInt(sample(Object.keys(bishop2Options), 1)[0]);
+ const bishop1Pos =
+ parseInt(sample(Object.keys(bishop1Options), 1)[0], 10);
+ const bishop2Pos =
+ parseInt(sample(Object.keys(bishop2Options), 1)[0], 10);
// Knights' positions are now determined
const forbidden = [
pieces[c][bishop2Pos] = "b";
pieces[c][knight2Pos] = "n";
pieces[c][rook2Pos] = "r";
- flags +=
- [rook1Pos, queenPos, kingPos, rook2Pos].sort().map(V.CoordToColumn).join("");
+ flags += [rook1Pos, queenPos, kingPos, rook2Pos]
+ .sort().map(V.CoordToColumn).join("");
}
// Add turn + flags + enpassant
return (
}
}
- getPotentialQueenMoves(sq) {
- return super.getPotentialQueenMoves(sq).concat(this.getCastleMoves(sq));
+ getPotentialQueenMoves([x, y]) {
+ let moves = super.getPotentialQueenMoves([x, y]);
+ const c = this.getColor(x, y);
+ if (this.castleFlags[c].slice(1, 3).includes(y))
+ moves = moves.concat(this.getCastleMoves([x, y]));
+ return moves;
}
- getCastleMoves([x, y]) {
+ getPotentialKingMoves([x, y]) {
+ let moves = this.getSlideNJumpMoves(
+ [x, y],
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
const c = this.getColor(x, y);
- if (
- x != (c == "w" ? V.size.x - 1 : 0) ||
- !this.castleFlags[c].slice(1, 3).includes(y)
- ) {
- // x isn't first rank, or piece moved
- return [];
- }
- const castlingPiece = this.getPiece(x, y);
+ if (this.castleFlags[c].slice(1, 3).includes(y))
+ moves = moves.concat(this.getCastleMoves([x, y]));
+ return moves;
+ }
+ getCastleMoves([x, y]) {
// Relative position of the selected piece: left or right ?
// If left: small castle left, large castle right.
// If right: usual situation.
+ const c = this.getColor(x, y);
const relPos = (this.castleFlags[c][1] == y ? "left" : "right");
- // Castling ?
- const oppCol = V.GetOppCol(c);
- let moves = [];
- let i = 0;
- // Castling piece, then rook:
- const finalSquares = {
- 0: (relPos == "left" ? [1, 2] : [2, 3]),
- 3: (relPos == "right" ? [6, 5] : [5, 4])
- };
-
- // Left, then right castle:
- castlingCheck: for (let castleSide of [0, 3]) {
- if (this.castleFlags[c][castleSide] >= 8) continue;
-
- // Rook and castling piece are on initial position
- const rookPos = this.castleFlags[c][castleSide];
-
- // Nothing on the path of the king ? (and no checks)
- const finDist = finalSquares[castleSide][0] - y;
- let step = finDist / Math.max(1, Math.abs(finDist));
- i = y;
- do {
- if (
- this.isAttacked([x, i], oppCol) ||
- (this.board[x][i] != V.EMPTY &&
- // NOTE: next check is enough, because of chessboard constraints
- (this.getColor(x, i) != c ||
- ![castlingPiece, V.ROOK].includes(this.getPiece(x, i))))
- ) {
- continue castlingCheck;
- }
- i += step;
- } while (i != finalSquares[castleSide][0]);
-
- // Nothing on the path to the rook?
- step = castleSide == 0 ? -1 : 1;
- for (i = y + step; i != rookPos; i += step) {
- if (this.board[x][i] != V.EMPTY) continue castlingCheck;
- }
-
- // Nothing on final squares, except maybe castling piece and rook?
- for (i = 0; i < 2; i++) {
- if (
- this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- ![y, rookPos].includes(finalSquares[castleSide][i])
- ) {
- continue castlingCheck;
- }
- }
-
- // If this code is reached, castle is valid
- moves.push(
- new Move({
- appear: [
- new PiPo({ x: x, y: finalSquares[castleSide][0], p: castlingPiece, c: c }),
- new PiPo({ x: x, y: finalSquares[castleSide][1], p: V.ROOK, c: c })
- ],
- vanish: [
- new PiPo({ x: x, y: y, p: castlingPiece, c: c }),
- new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
- ],
- // In this variant, always castle by playing onto the rook
- end: { x: x, y: rookPos }
- })
- );
- }
-
+ const finalSquares = [
+ relPos == "left" ? [1, 2] : [2, 3],
+ relPos == "right" ? [6, 5] : [5, 4]
+ ];
+ const saveFlags = JSON.stringify(this.castleFlags[c]);
+ // Alter flags to follow base_rules semantic
+ this.castleFlags[c] = [0, 3].map(i => this.castleFlags[c][i]);
+ const moves = super.getCastleMoves([x, y], finalSquares);
+ this.castleFlags[c] = JSON.parse(saveFlags);
return moves;
}
return false;
}
- updateCastleFlags(move, piece) {
+ // "twoKings" arg for the similar Twokings variant.
+ updateCastleFlags(move, piece, twoKings) {
const c = V.GetOppCol(this.turn);
const firstRank = (c == "w" ? V.size.x - 1 : 0);
// Update castling flags if castling pieces moved or were captured
const oppCol = V.GetOppCol(c);
const oppFirstRank = V.size.x - 1 - firstRank;
- if (move.start.x == firstRank && [V.KING, V.QUEEN].includes(piece)) {
- if (this.castleFlags[c][1] == move.start.y)
- this.castleFlags[c][1] = 8;
- else if (this.castleFlags[c][2] == move.start.y)
- this.castleFlags[c][2] = 8;
- // Else: the flag is already turned off
+ if (move.start.x == firstRank) {
+ if (piece == V.KING || (!twoKings && piece == V.QUEEN)) {
+ if (this.castleFlags[c][1] == move.start.y)
+ this.castleFlags[c][1] = 8;
+ else if (this.castleFlags[c][2] == move.start.y)
+ this.castleFlags[c][2] = 8;
+ // Else: the flag is already turned off
+ }
}
else if (
move.start.x == firstRank && //our rook moves?
this.castleFlags[c][flagIdx] = 8;
} else if (
move.end.x == oppFirstRank && //we took opponent rook?
- [this.castleFlags[oppCol][0], this.castleFlags[oppCol][3]].includes(move.end.y)
+ [this.castleFlags[oppCol][0], this.castleFlags[oppCol][3]]
+ .includes(move.end.y)
) {
const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 3);
this.castleFlags[oppCol][flagIdx] = 8;
// NOTE: do not set queen value to 1000 or so, because there may be several.
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
+
getNotation(move) {
if (move.appear.length == 2) {
// Castle: determine the right notation
}
return super.getNotation(move);
}
+
};