}
getRankInReserve(c, p) {
- const pieces = Object.keys(this.pieces());
+ const pieces = Object.keys(this.pieces(c, c, p));
const lastIndex = pieces.findIndex(pp => pp == p)
let toTest = pieces.slice(0, lastIndex);
return toTest.reduce(
re_drawBoardElements() {
const board = this.getSvgChessboard();
- const oppCol = C.GetOppCol(this.playerColor);
const container = document.getElementById(this.containerId);
const rc = container.getBoundingClientRect();
let chessboard = container.querySelector(".chessboard");
// Include square of the en-passant capturing square:
enlightEnpassant() {
// NOTE: shortcut, pawn has only one attack type, doesn't depend on square
- const steps = this.pieces(this.playerColor)["p"].attack[0].steps;
+ // TODO: (0, 0) is wrong, would need to place an attacker here...
+ const steps = this.pieces(this.playerColor, 0, 0)["p"].attack[0].steps;
for (let step of steps) {
const x = this.epSquare.x - step[0],
y = this.getY(this.epSquare.y - step[1]);
getPieceType(x, y, p) {
if (!p)
p = this.getPiece(x, y);
- return this.pieces()[p].moveas || p;
+ return this.pieces(this.getColor(x, y), x, y)[p].moveas || p;
}
isKing(x, y, p) {
return !!C.CannibalKings[p];
}
- // Get opponent color
- static GetOppCol(color) {
+ static GetOppTurn(color) {
+ return (color == 'w' ? 'b' : 'w');
+ }
+
+ // Get opponent color(s): may differ from turn (e.g. Checkered)
+ getOppCols(color) {
return (color == "w" ? "b" : "w");
}
if (!this.options["madrasi"])
return false;
const color = this.getColor(x, y);
- const oppCol = C.GetOppCol(color);
+ const oppCols = this.getOppCols(color);
const piece = this.getPieceType(x, y);
const stepSpec = this.getStepSpec(color, x, y, piece);
const attacks = stepSpec.both.concat(stepSpec.attack);
}
if (
this.onBoard(i, j) &&
- this.getColor(i, j) == oppCol &&
+ oppCols.includes(this.getColor(i, j)) &&
this.getPieceType(i, j) == piece
) {
return true;
// Stop at the first capture found
atLeastOneCapture(color) {
- const oppCol = C.GetOppCol(color);
const allowed = (sq1, sq2) => {
return (
// NOTE: canTake is reversed for Zen.
if (moves.length == 0)
return [];
const color = this.getColor(moves[0].start.x, moves[0].start.y);
- const oppCol = C.GetOppCol(color);
+ const oppCols = this.getOppCols(color);
if (this.options["capture"] && this.atLeastOneCapture(color))
- moves = this.capturePostProcess(moves, oppCol);
+ moves = this.capturePostProcess(moves, oppCols);
if (this.options["atomic"])
- moves = this.atomicPostProcess(moves, color, oppCol);
+ moves = this.atomicPostProcess(moves, color, oppCols);
if (
moves.length > 0 &&
this.getPieceType(moves[0].start.x, moves[0].start.y) == "p"
) {
- moves = this.pawnPostProcess(moves, color, oppCol);
+ moves = this.pawnPostProcess(moves, color, oppCols);
}
if (this.options["cannibal"] && this.options["rifle"])
return moves;
}
- capturePostProcess(moves, oppCol) {
+ capturePostProcess(moves, oppCols) {
// Filter out non-capturing moves (not using m.vanish because of
// self captures of Recycle and Teleport).
return moves.filter(m => {
return (
this.board[m.end.x][m.end.y] != "" &&
- this.getColor(m.end.x, m.end.y) == oppCol
+ oppCols.includes(this.getColor(m.end.x, m.end.y))
);
});
}
- atomicPostProcess(moves, color, oppCol) {
+ atomicPostProcess(moves, color, oppCols) {
moves.forEach(m => {
if (
this.board[m.end.x][m.end.y] != "" &&
- this.getColor(m.end.x, m.end.y) == oppCol
+ oppCols.includes(this.getColor(m.end.x, m.end.y))
) {
// Explosion!
let steps = [
return moves;
}
- pawnPostProcess(moves, color, oppCol) {
+ pawnPostProcess(moves, color, oppCols) {
let moreMoves = [];
const lastRank = (color == "w" ? 0 : this.size.x - 1);
const initPiece = this.getPiece(moves[0].start.x, moves[0].start.y);
if (
this.options["cannibal"] &&
this.board[x2][y2] != "" &&
- this.getColor(x2, y2) == oppCol
+ oppCols.includes(this.getColor(x2, y2))
) {
finalPieces = [this.getPieceType(x2, y2)];
}
// Search for enemy (or not) pieces attacking [x, y]
findCapturesOn([x, y], o, allowed) {
if (!o.byCol)
- o.byCol = [C.GetOppCol(this.getColor(x, y) || this.turn)];
+ o.byCol = this.getOppCols(this.getColor(x, y) || this.turn);
let res = [];
for (let i=0; i<this.size.x; i++) {
for (let j=0; j<this.size.y; j++) {
getEnpassantCaptures([x, y]) {
const color = this.getColor(x, y);
const shiftX = (color == 'w' ? -1 : 1);
- const oppCol = C.GetOppCol(color);
+ const oppCols = this.getOppCols(color);
if (
this.epSquare &&
this.epSquare.x == x + shiftX &&
Math.abs(this.getY(this.epSquare.y - y)) == 1 &&
// Doublemove (and Progressive?) guards:
this.board[this.epSquare.x][this.epSquare.y] == "" &&
- this.getColor(x, this.epSquare.y) == oppCol
+ oppCols.includes(this.getColor(x, this.epSquare.y))
) {
const [epx, epy] = [this.epSquare.x, this.epSquare.y];
- this.board[epx][epy] = oppCol + 'p';
+ this.board[epx][epy] = this.board[x][this.epSquares.y];
let enpassantMove = this.getBasicMove([x, y], [epx, epy]);
this.board[epx][epy] = "";
const lastIdx = enpassantMove.vanish.length - 1; //think Rifle
const c = this.getColor(x, y);
// Castling ?
- const oppCol = C.GetOppCol(c);
+ const oppCols = this.getOppCols(c);
let moves = [];
// King, then rook:
finalSquares =
// will be executed in filterValid() later.
(
i != finalSquares[castleSide][0] &&
- this.underCheck([[x, i]], oppCol)
+ this.underCheck([[x, i]], oppCols)
)
||
(
// MOVES VALIDATION
// Is piece (or square) at given position attacked by "oppCol(s)" ?
- underAttack([x, y], oppCol) {
- if (!Array.isArray(oppCol))
- oppCol = [oppCol];
+ underAttack([x, y], oppCols) {
// An empty square is considered as king,
// since it's used only in getCastleMoves (TODO?)
const king = this.board[x][y] == "" || this.isKing(x, y);
this.findCapturesOn(
[x, y],
{
- byCol: oppCol,
+ byCol: oppCols,
segments: this.options["cylinder"],
one: true
}
segments: this.options["cylinder"],
one: true
},
- ([i1, j1], [i2, j2]) => oppCol.includes(this.getColor(i2, j2))
+ ([i1, j1], [i2, j2]) => oppCols.includes(this.getColor(i2, j2))
)
)
);
}
// Argument is (very generally) an array of squares (= arrays)
- underCheck(square_s, oppCol) {
+ underCheck(square_s, oppCols) {
if (this.options["taking"] || this.options["dark"])
return false;
- return square_s.some(sq => this.underAttack(sq, oppCol));
+ return square_s.some(sq => this.underAttack(sq, oppCols));
}
// Scan board for king(s)
filterValid(moves, color) {
if (!color)
color = this.turn;
- const oppCol = C.GetOppCol(color);
+ const oppCols = this.getOppCols(color);
let kingPos = this.searchKingPos(color);
let filtered = {}; //avoid re-checking similar moves (promotions...)
return moves.filter(m => {
else
res = false; //king vanished
}
- res &&= !this.underCheck(kingPos, oppCol);
+ res &&= !this.underCheck(kingPos, oppCols);
if (oldKingPP && newKingPP)
kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
this.undoOnBoard(m);
tryChangeTurn(move) {
if (this.isLastMove(move)) {
- this.turn = C.GetOppCol(this.turn);
+ this.turn = (this.turn == 'w' ? 'b' : 'w');
this.movesCount++;
this.subTurn = 1;
}
if (move.next)
return false;
const color = this.turn;
- const oppKingPos = this.searchKingPos(C.GetOppCol(color));
- if (oppKingPos.length == 0 || this.underCheck(oppKingPos, color))
+ const oppKingPos = this.searchKingPos(C.GetOppTurn(color));
+ if (oppKingPos.length == 0 || this.underCheck(oppKingPos, [color]))
return true;
return (
(
// Shortcut in case the score was computed before:
if (move.result)
return move.result;
- const color = this.turn;
- const oppCol = C.GetOppCol(color);
+ const oppTurn = C.GetOppTurn(this.turn);
const kingPos = {
- [color]: this.searchKingPos(color),
- [oppCol]: this.searchKingPos(oppCol)
+ w: this.searchKingPos('w'),
+ b: this.searchKingPos('b')
};
- if (kingPos[color].length == 0 && kingPos[oppCol].length == 0)
+ if (kingPos[this.turn].length == 0 && kingPos[oppTurn].length == 0)
return "1/2";
- if (kingPos[color].length == 0)
+ if (kingPos[this.turn].length == 0)
return (color == "w" ? "0-1" : "1-0");
- if (kingPos[oppCol].length == 0)
+ if (kingPos[oppTurn].length == 0)
return (color == "w" ? "1-0" : "0-1");
- if (this.atLeastOneMove(color))
+ if (this.atLeastOneMove(this.turn))
return "*";
// No valid move: stalemate or checkmate?
- if (!this.underCheck(kingPos[color], oppCol))
+ if (!this.underCheck(kingPos[this.turn], this.getOppCols(this.turn)))
return "1/2";
// OK, checkmate
- return (color == "w" ? "0-1" : "1-0");
+ return (this.turn == "w" ? "0-1" : "1-0");
}
playVisual(move, r) {
}
getPartFen(o) {
- let parts = super.getPartFen(o);
- parts["cmove"] = this.getCmoveFen();
- parts["stage"] = this.getStageFen();
- return parts;
+ return Object.assign(
+ {
+ "cmove": o.init ? "-" : this.getCmoveFen(),
+ "stage": o.init ? "1" : this.getStageFen()
+ },
+ super.getPartFen(o)
+ );
}
getCmoveFen() {
getFlagsFen() {
let fen = super.getFlagsFen();
// Add pawns flags
- for (let c of ["w", "b"])
+ for (let c of ["w", "b"]) {
for (let i = 0; i < 8; i++)
fen += (this.pawnFlags[c][i] ? "1" : "0");
+ }
return fen;
}
return super.getPawnShift(color == 'c' ? this.turn : color);
}
+ getOppCols(color) {
+ if (this.stage == 1)
+ return super.getOppCols(color).concat(['c']);
+ // Stage 2: depends if color is w+b or checkered
+ if (color == this.sideCheckered)
+ return ['w', 'b'];
+ return ['c'];
+ }
+
pieces(color, x, y) {
let baseRes = super.pieces(color, x, y);
if (
}
}
- aggregateFlags() {
- return [this.castleFlags, this.pawnFlags];
- }
-
- disaggregateFlags(flags) {
- this.castleFlags = flags[0];
- this.pawnFlags = flags[1];
- }
-
getEpSquare(moveOrSquare) {
// At stage 2, all pawns can be captured en-passant
if (
// Apply "checkerization" of standard moves
const lastRank = (color == "w" ? 0 : 7);
const [x, y] = [moves[0].start.x, moves[0].start.y];
- let moves = [];
const piece = this.getPiece(x, y);
// King is treated differently: it never turn checkered
- if (piece == 'k') {
+ if (piece == 'k' && this.stage == 1) {
// If at least one checkered piece, allow switching:
if (
this.options["withswitch"] &&
this.board.some(b => b.some(cell => cell[0] == 'c'))
) {
- const oppCol = C.GetOppCol(color);
- const oppKingPos = this.searchKingPos(oppCol)[0];
+ const oppKingPos = this.searchKingPos(C.GetOppTurn(this.turn))[0];
moves.push(
new Move({
start: { x: x, y: y },
- end: { x: oppKingPos[0], y: oppKingPos[1] },
+ end: {x: oppKingPos[0], y: oppKingPos[1]},
appear: [],
vanish: []
})
if (piece == 'p') {
// Filter out forbidden pawn moves
moves = moves.filter(m => {
- if (m.vanish[0].p == 'p') {
+ if (m.vanish.length > 0 && m.vanish[0].p == 'p') {
if (
Math.abs(m.end.x - m.start.x) == 2 &&
!this.pawnFlags[this.turn][m.start.y]
}
let extraMoves = [];
moves.forEach(m => {
- if (m.vanish.length == 2 && m.appear.length == 1)
+ if (m.vanish.length == 2 && m.appear.length == 1) {
// A capture occured
m.appear[0].c = "c";
if (
return moves.concat(extraMoves);
}
- canIplay(side, [x, y]) {
+ canIplay(x, y) {
if (this.stage == 2) {
const color = this.getColor(x, y);
return (
: ['w', 'b'].includes(color)
);
}
- return side == this.turn && [side, "c"].includes(this.getColor(x, y));
+ return (
+ this.playerColor == this.turn &&
+ [this.turn, "c"].includes(this.getColor(x, y))
+ );
}
// Does m2 un-do m1 ? (to disallow undoing checkered moves)
// Checkered cannot be under check (no king)
return moves;
let kingPos = super.searchKingPos(color);
- const oppCol = C.GetOppCol(color);
if (this.stage == 2)
// Must consider both kings (attacked by checkered side)
- kingPos = [kingPos, super.searchKingPos(oppCol)];
+ kingPos = [kingPos, super.searchKingPos(C.GetOppTurn(this.turn))];
+ const oppCols = this.getOppCols(color);
return moves.filter(m => {
this.playOnBoard(m);
let res = true;
if (stage == 1)
res = !this.oppositeMoves(this.cmove, m);
if (res && m.appear.length > 0)
- // NOTE: oppCol might be inaccurate; fixed in underCheck()
- res = !this.underCheck(kingPos, oppCol);
+ res = !this.underCheck(kingPos, oppCols);
this.undoOnBoard(m);
return res;
});
}
atLeastOneMove(color) {
- const oppCol = C.GetOppCol(color);
+ const myCols = [color, 'c'];
for (let i = 0; i < this.size.x; i++) {
for (let j = 0; j < this.size.y; j++) {
const colIJ = this.getColor(i, j);
if (
this.board[i][j] != "" &&
(
- (this.stage == 1 && colIJ != oppCol) ||
+ (this.stage == 1 && myCols.includes(colIJ)) ||
(this.stage == 2 &&
(
(this.sideCheckered == color && colIJ == 'c') ||
return false;
}
- underCheck(square_s, oppCol) {
- if (this.stage == 1)
- return super.underAttack(square_s, [oppCol, 'c']);
- if (oppCol != this.sideCheckered)
+ underCheck(square_s, oppCols) {
+ if (this.stage == 2 && oppCol != this.sideCheckered)
return false; //checkered pieces is me, I'm not under check
- return super.underAttack(square_s, 'c');
+ return super.underAttack(square_s, oppCols);
}
prePlay(move) {
if (this.atLeastOneMove(color))
return "*";
// Artifically change turn, for checkered pawns
- const oppCol = C.GetOppCol(color);
- this.turn = oppCol;
+ const oppTurn = C.GetOppTurn(color);
+ this.turn = oppTurn;
const kingPos = super.searchKingPos(color)[0];
let res = "1/2";
- if (super.underAttack(kingPos, [oppCol, 'c']))
+ if (super.underAttack(kingPos, [oppTurn, 'c']))
res = (color == "w" ? "0-1" : "1-0");
this.turn = color;
return res;
}
if (this.atLeastOneMove(color))
return "*";
- let res = super.underAttack(super.searchKingPos(color)[0], 'c');
+ let res = super.underAttack(super.searchKingPos(color)[0], ['c']);
if (!res)
- res = super.underAttack(super.searchKingPos(oppCol)[0], 'c');
+ res = super.underAttack(super.searchKingPos(oppCol)[0], ['c']);
if (res)
return (color == 'w' ? "0-1" : "1-0");
return "1/2";