-import { Random } from "/utils/alea.js";
-import { ArrayFun } from "/utils/array.js";
+import {Random} from "/utils/alea.js";
+import {ArrayFun} from "/utils/array.js";
import PiPo from "/utils/PiPo.js";
import Move from "/utils/Move.js";
return false;
}
+ // Some variants reveal moves only after both players played
+ get hideMoves() {
+ return false;
+ }
+
// Some variants use click infos:
doClick(coords) {
if (typeof coords.x != "number")
return (f.charCodeAt(0) <= 90 ? "w" + f.toLowerCase() : "b" + f);
}
- // Setup the initial random-or-not (asymmetric-or-not) position
genRandInitFen(seed) {
+ Random.setSeed(seed); //may be unused
+ let baseFen = this.genRandInitBaseFen();
+ baseFen.o = Object.assign({init: true}, baseFen.o);
+ const parts = this.getPartFen(baseFen.o);
+ return (
+ baseFen.fen + " w 0" +
+ (Object.keys(parts).length > 0 ? (" " + JSON.stringify(parts)) : "")
+ );
+ }
+
+ // Setup the initial random-or-not (asymmetric-or-not) position
+ genRandInitBaseFen() {
let fen, flags = "0707";
if (!this.options.randomness)
// Deterministic:
- fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0";
+ fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
else {
// Randomize
- Random.setSeed(seed);
let pieces = {w: new Array(8), b: new Array(8)};
flags = "";
// Shuffle pieces on first (and last rank if randomness == 2)
flags += flags;
break;
}
-
let positions = ArrayFun.range(8);
-
// Get random squares for bishops
let randIndex = 2 * Random.randInt(4);
const bishop1Pos = positions[randIndex];
// Remove chosen squares
positions.splice(Math.max(randIndex, randIndex_tmp), 1);
positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
// Get random squares for knights
randIndex = Random.randInt(6);
const knight1Pos = positions[randIndex];
randIndex = Random.randInt(5);
const knight2Pos = positions[randIndex];
positions.splice(randIndex, 1);
-
// Get random square for queen
randIndex = Random.randInt(4);
const queenPos = positions[randIndex];
positions.splice(randIndex, 1);
-
// Rooks and king positions are now fixed,
// because of the ordering rook-king-rook
const rook1Pos = positions[0];
const kingPos = positions[1];
const rook2Pos = positions[2];
-
// Finally put the shuffled pieces in the board array
pieces[c][rook1Pos] = "r";
pieces[c][knight1Pos] = "n";
fen = (
pieces["b"].join("") +
"/pppppppp/8/8/8/8/PPPPPPPP/" +
- pieces["w"].join("").toUpperCase() +
- " w 0"
+ pieces["w"].join("").toUpperCase()
);
}
- // Add turn + flags + enpassant (+ reserve)
- let parts = [];
- if (this.hasFlags)
- parts.push(`"flags":"${flags}"`);
- if (this.hasEnpassant)
- parts.push('"enpassant":"-"');
- if (this.hasReserveFen)
- parts.push('"reserve":"000000000000"');
- if (this.options["crazyhouse"])
- parts.push('"ispawn":"-"');
- if (parts.length >= 1)
- fen += " {" + parts.join(",") + "}";
- return fen;
+ return { fen: fen, o: {flags: flags} };
}
// "Parse" FEN: just return untransformed string data
// Return current fen (game state)
getFen() {
- let fen = (
- this.getPosition() + " " +
- this.getTurnFen() + " " +
- this.movesCount
+ const parts = this.getPartFen({});
+ return (
+ this.getBaseFen() +
+ (Object.keys(parts).length > 0 ? (" " + JSON.stringify(parts)) : "")
);
- let parts = [];
+ }
+
+ getBaseFen() {
+ return this.getPosition() + " " + this.turn + " " + this.movesCount;
+ }
+
+ getPartFen(o) {
+ let parts = {};
if (this.hasFlags)
- parts.push(`"flags":"${this.getFlagsFen()}"`);
+ parts["flags"] = o.init ? o.flags : this.getFlagsFen();
if (this.hasEnpassant)
- parts.push(`"enpassant":"${this.getEnpassantFen()}"`);
+ parts["enpassant"] = o.init ? "-" : this.getEnpassantFen();
if (this.hasReserveFen)
- parts.push(`"reserve":"${this.getReserveFen()}"`);
+ parts["reserve"] = this.getReserveFen(o);
if (this.options["crazyhouse"])
- parts.push(`"ispawn":"${this.getIspawnFen()}"`);
- if (parts.length >= 1)
- fen += " {" + parts.join(",") + "}";
- return fen;
+ parts["ispawn"] = this.getIspawnFen(o);
+ return parts;
}
static FenEmptySquares(count) {
return position;
}
- getTurnFen() {
- return this.turn;
- }
-
// Flags part of the FEN string
getFlagsFen() {
return ["w", "b"].map(c => {
// Enpassant part of the FEN string
getEnpassantFen() {
if (!this.epSquare)
- return "-"; //no en-passant
+ return "-";
return C.CoordsToSquare(this.epSquare);
}
- getReserveFen() {
+ getReserveFen(o) {
+ if (o.init)
+ return "000000000000";
return (
["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("")
);
}
- getIspawnFen() {
+ getIspawnFen(o) {
+ if (o.init)
+ // NOTE: cannot merge because this.ispawn doesn't exist yet
+ return "-";
const squares = Object.keys(this.ispawn);
if (squares.length == 0)
return "-";
if (o.genFenOnly)
// This object will be used only for initial FEN generation
return;
+
+ // Some variables
this.playerColor = o.color;
this.afterPlay = o.afterPlay; //trigger some actions after playing a move
+ this.containerId = o.element;
+ this.isDiagram = o.diagram;
+ this.marks = o.marks;
- // Fen string fully describes the game state
+ // Initializations
if (!o.fen)
o.fen = this.genRandInitFen(o.seed);
this.re_initFromFen(o.fen);
-
- // Graphical (can use variables defined above)
- this.containerId = o.element;
this.graphicalInit();
}
this.setFlags(fenParsed.flags);
if (this.hasEnpassant)
this.epSquare = this.getEpSquare(fenParsed.enpassant);
- if (this.hasReserve)
+ if (this.hasReserve && !this.isDiagram)
this.initReserves(fenParsed.reserve);
if (this.options["crazyhouse"])
this.initIspawn(fenParsed.ispawn);
}
// ordering as in pieces() p,r,n,b,q,k
- initReserves(reserveStr) {
+ initReserves(reserveStr, pieceArray) {
+ if (!pieceArray)
+ pieceArray = ['p', 'r', 'n', 'b', 'q', 'k'];
const counts = reserveStr.split("").map(c => parseInt(c, 36));
- this.reserve = { w: {}, b: {} };
- const pieceName = ['p', 'r', 'n', 'b', 'q', 'k'];
- const L = pieceName.length;
- for (let i of ArrayFun.range(2 * L)) {
- if (i < L)
- this.reserve['w'][pieceName[i]] = counts[i];
- else
- this.reserve['b'][pieceName[i-L]] = counts[i];
- }
+ const L = pieceArray.length;
+ this.reserve = {
+ w: ArrayFun.toObject(pieceArray, counts.slice(0, L)),
+ b: ArrayFun.toObject(pieceArray, counts.slice(L, 2 * L))
+ };
}
initIspawn(ispawnStr) {
(oldV,newV) => oldV + (this.reserve[c][newV] > 0 ? 1 : 0), 0);
}
- static AddClass_es(piece, class_es) {
+ static AddClass_es(elt, class_es) {
if (!Array.isArray(class_es))
class_es = [class_es];
- class_es.forEach(cl => {
- piece.classList.add(cl);
- });
+ class_es.forEach(cl => elt.classList.add(cl));
}
- static RemoveClass_es(piece, class_es) {
+ static RemoveClass_es(elt, class_es) {
if (!Array.isArray(class_es))
class_es = [class_es];
- class_es.forEach(cl => {
- piece.classList.remove(cl);
- });
+ class_es.forEach(cl => elt.classList.remove(cl));
}
// Generally light square bottom-right
// VISUAL METHODS
graphicalInit() {
- // NOTE: not window.onresize = this.re_drawBoardElts because scope (this)
- window.onresize = () => this.re_drawBoardElements();
- this.re_drawBoardElements();
- this.initMouseEvents();
- const chessboard =
- document.getElementById(this.containerId).querySelector(".chessboard");
+ const g_init = () => {
+ this.re_drawBoardElements();
+ if (!this.isDiagram && !this.mouseListeners && !this.touchListeners)
+ this.initMouseEvents();
+ };
+ let container = document.getElementById(this.containerId);
+ this.windowResizeObs = new ResizeObserver(g_init);
+ this.windowResizeObs.observe(container);
}
re_drawBoardElements() {
cbHeight = Math.min(rc.height, 767);
cbWidth = cbHeight * vRatio;
}
- if (this.hasReserve) {
+ if (this.hasReserve && !this.isDiagram) {
const sqSize = cbWidth / this.size.y;
// NOTE: allocate space for reserves (up/down) even if they are empty
// Cannot use getReserveSquareSize() here, but sqSize is an upper bound.
class="chessboard_SVG">`;
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
+ if (!this.onBoard(i, j))
+ continue;
const ii = (flipped ? this.size.x - 1 - i : i);
const jj = (flipped ? this.size.y - 1 - j : j);
let classes = this.getSquareColorClass(ii, jj);
}
setupPieces(r) {
- if (this.g_pieces) {
- // Refreshing: delete old pieces first
- for (let i=0; i<this.size.x; i++) {
- for (let j=0; j<this.size.y; j++) {
- if (this.g_pieces[i][j]) {
- this.g_pieces[i][j].remove();
- this.g_pieces[i][j] = null;
- }
- }
- }
- }
- else
- this.g_pieces = ArrayFun.init(this.size.x, this.size.y, null);
let chessboard =
document.getElementById(this.containerId).querySelector(".chessboard");
if (!r)
r = chessboard.getBoundingClientRect();
const pieceWidth = this.getPieceWidth(r.width);
+ const addPiece = (i, j, arrName, classes) => {
+ this[arrName][i][j] = document.createElement("piece");
+ C.AddClass_es(this[arrName][i][j], classes);
+ this[arrName][i][j].style.width = pieceWidth + "px";
+ this[arrName][i][j].style.height = pieceWidth + "px";
+ let [ip, jp] = this.getPixelPosition(i, j, r);
+ // Translate coordinates to use chessboard as reference:
+ this[arrName][i][j].style.transform =
+ `translate(${ip - r.x}px,${jp - r.y}px)`;
+ chessboard.appendChild(this[arrName][i][j]);
+ };
+ const conditionalReset = (arrName) => {
+ if (this[arrName]) {
+ // Refreshing: delete old pieces first. This isn't necessary,
+ // but simpler (this method isn't called many times)
+ for (let i=0; i<this.size.x; i++) {
+ for (let j=0; j<this.size.y; j++) {
+ if (this[arrName][i][j]) {
+ this[arrName][i][j].remove();
+ this[arrName][i][j] = null;
+ }
+ }
+ }
+ }
+ else
+ this[arrName] = ArrayFun.init(this.size.x, this.size.y, null);
+ if (arrName == "d_pieces")
+ this.marks.forEach(([i, j]) => addPiece(i, j, arrName, "mark"));
+ };
+ if (this.marks)
+ conditionalReset("d_pieces");
+ conditionalReset("g_pieces");
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
if (this.board[i][j] != "") {
const color = this.getColor(i, j);
const piece = this.getPiece(i, j);
- this.g_pieces[i][j] = document.createElement("piece");
- C.AddClass_es(this.g_pieces[i][j],
- this.pieces(color, i, j)[piece]["class"]);
+ addPiece(i, j, "g_pieces", this.pieces(color, i, j)[piece]["class"]);
this.g_pieces[i][j].classList.add(C.GetColorClass(color));
- this.g_pieces[i][j].style.width = pieceWidth + "px";
- this.g_pieces[i][j].style.height = pieceWidth + "px";
- let [ip, jp] = this.getPixelPosition(i, j, r);
- // Translate coordinates to use chessboard as reference:
- this.g_pieces[i][j].style.transform =
- `translate(${ip - r.x}px,${jp - r.y}px)`;
if (this.enlightened && !this.enlightened[i][j])
this.g_pieces[i][j].classList.add("hidden");
- chessboard.appendChild(this.g_pieces[i][j]);
+ }
+ if (this.marks && this.d_pieces[i][j]) {
+ let classes = ["mark"];
+ if (this.board[i][j] != "")
+ classes.push("transparent");
+ addPiece(i, j, "d_pieces", classes);
}
}
}
- if (this.hasReserve)
+ if (this.hasReserve && !this.isDiagram)
this.re_drawReserve(['w', 'b'], r);
}
piece = "k"; //capturing cannibal king: back to king form
const oldCount = this.reserve[color][piece];
this.reserve[color][piece] = count;
- // Redrawing is much easier if count==0
- if ([oldCount, count].includes(0))
+ // Redrawing is much easier if count==0 (or undefined)
+ if ([oldCount, count].some(item => !item))
this.re_drawReserve([color]);
else {
const numId = this.getReserveNumId(color, piece);
}
removeListeners() {
+ let container = document.getElementById(this.containerId);
+ this.windowResizeObs.unobserve(container);
+ if (this.isDiagram)
+ return; //no listeners in this case
if ('onmousedown' in window) {
this.mouseListeners.forEach(ml => {
document.removeEventListener(ml.type, ml.listener);
}
}
+ displayMessage(elt, msg, classe_s, timeout) {
+ if (elt)
+ // Fixed element, e.g. for Dice Chess
+ elt.innerHTML = msg;
+ else {
+ // Temporary div (Chakart, Apocalypse...)
+ let divMsg = document.createElement("div");
+ C.AddClass_es(divMsg, classe_s);
+ divMsg.innerHTML = msg;
+ let container = document.getElementById(this.containerId);
+ container.appendChild(divMsg);
+ setTimeout(() => container.removeChild(divMsg), timeout);
+ }
+ }
+
////////////////
// DARK METHODS
}
getStepSpec(color, x, y, piece) {
- return this.pieces(color, x, y)[piece || this.getPieceType(x, y)];
+ let pieceType = piece;
+ const allSpecs = this.pieces(color, x, y);
+ if (!piece)
+ pieceType = this.getPieceType(x, y);
+ else if (allSpecs[piece].moveas)
+ pieceType = allSpecs[piece].moveas;
+ return allSpecs[pieceType];
}
// Can thing on square1 capture thing on square2?
let moves = [];
for (let i=0; i<this.size.x; i++) {
for (let j=0; j<this.size.y; j++) {
- if (this.canDrop([c, p], [i, j])) {
+ if (this.onBoard(i, j) && this.canDrop([c, p], [i, j])) {
let mv = new Move({
start: {x: c, y: p},
end: {x: i, y: j},
if (piece == "p" && this.hasEnpassant && this.epSquare)
Array.prototype.push.apply(moves, this.getEnpassantCaptures([x, y]));
if (
- piece == "k" && this.hasCastle &&
+ this.isKing(0, 0, piece) && this.hasCastle &&
this.castleFlags[color || this.turn].some(v => v < this.size.y)
) {
Array.prototype.push.apply(moves, this.getCastleMoves([x, y]));
moves = this.capturePostProcess(moves, oppCol);
if (this.options["atomic"])
- this.atomicPostProcess(moves, color, oppCol);
+ moves = this.atomicPostProcess(moves, color, oppCol);
if (
moves.length > 0 &&
this.getPieceType(moves[0].start.x, moves[0].start.y) == "p"
) {
- this.pawnPostProcess(moves, color, oppCol);
+ moves = this.pawnPostProcess(moves, color, oppCol);
}
if (this.options["cannibal"] && this.options["rifle"])
// In this case a rifle-capture from last rank may promote a pawn
- this.riflePromotePostProcess(moves, color);
+ moves = this.riflePromotePostProcess(moves, color);
return moves;
}
m.next = mNext;
}
});
+ return moves;
}
pawnPostProcess(moves, color, oppCol) {
m.appear.shift();
return;
}
- let finalPieces = ["p"];
+ let finalPieces;
if (
this.options["cannibal"] &&
this.board[x2][y2] != "" &&
if (initPiece == "!") //cannibal king-pawn
m.appear[0].p = C.CannibalKingCode[finalPieces[0]];
for (let i=1; i<finalPieces.length; i++) {
+ let newMove = JSON.parse(JSON.stringify(m));
const piece = finalPieces[i];
- const tr = {
- c: color,
- p: (initPiece != "!" ? piece : C.CannibalKingCode[piece])
- };
- let newMove = this.getBasicMove([x1, y1], [x2, y2], tr);
+ m.appear[0].p = (initPiece != "!" ? piece : C.CannibalKingCode[piece]);
moreMoves.push(newMove);
}
});
- Array.prototype.push.apply(moves, moreMoves);
+ return moves.concat(moreMoves);
}
riflePromotePostProcess(moves, color) {
}
}
});
- Array.prototype.push.apply(moves, newMoves);
+ return moves.concat(newMoves);
}
// Generic method to find possible moves of "sliding or jumping" pieces
);
}
- underCheck([x, y], oppCol) {
+ // Argument is (very generally) an array of squares (= arrays)
+ underCheck(square_s, oppCol) {
if (this.options["taking"] || this.options["dark"])
return false;
- return this.underAttack([x, y], oppCol);
+ if (!Array.isArray(square_s[0]))
+ square_s = [square_s];
+ return square_s.some(sq => this.underAttack(sq, oppCol));
}
- // Stop at first king found (TODO: multi-kings)
+ // Scan board for king(s)
searchKingPos(color) {
+ let res = [];
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
if (this.getColor(i, j) == color && this.isKing(i, j))
- return [i, j];
+ res.push([i, j]);
}
}
- return [-1, -1]; //king not found
+ return res;
}
// 'color' arg because some variants (e.g. Refusal) check opponent moves
if (!color)
color = this.turn;
const oppCol = C.GetOppCol(color);
- const kingPos = this.searchKingPos(color);
+ let kingPos = this.searchKingPos(color);
let filtered = {}; //avoid re-checking similar moves (promotions...)
return moves.filter(m => {
const key = m.start.x + m.start.y + '.' + m.end.x + m.end.y;
if (!filtered[key]) {
this.playOnBoard(m);
- let square = kingPos,
+ let newKingPP = null,
+ sqIdx = 0,
res = true; //a priori valid
- if (m.vanish.some(v => this.isKing(0, 0, v.p) && v.c == color)) {
+ const oldKingPP =
+ m.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
+ if (oldKingPP) {
// Search king in appear array:
- const newKingIdx =
- m.appear.findIndex(a => this.isKing(0, 0, a.p) && a.c == color);
- if (newKingIdx >= 0)
- square = [m.appear[newKingIdx].x, m.appear[newKingIdx].y];
+ newKingPP =
+ m.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
+ if (newKingPP) {
+ sqIdx = kingPos.findIndex(kp =>
+ kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
+ kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
+ }
else
- res = false;
+ res = false; //king vanished
}
- res &&= !this.underCheck(square, oppCol);
+ res &&= !this.underCheck(kingPos, oppCol);
+ if (oldKingPP && newKingPP)
+ kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
this.undoOnBoard(m);
filtered[key] = res;
return res;
}
postPlay(move) {
- const color = this.turn;
if (this.options["dark"])
this.updateEnlightened();
if (this.options["teleport"]) {
if (
this.subTurnTeleport == 1 &&
move.vanish.length > move.appear.length &&
- move.vanish[1].c == color
+ move.vanish[1].c == this.turn
) {
const v = move.vanish[move.vanish.length - 1];
this.captured = {x: v.x, y: v.y, c: v.c, p: v.p};
this.subTurnTeleport = 1;
this.captured = null;
}
+ this.tryChangeTurn(move);
+ }
+
+ tryChangeTurn(move) {
if (this.isLastMove(move)) {
- this.turn = C.GetOppCol(color);
+ this.turn = C.GetOppCol(this.turn);
this.movesCount++;
this.subTurn = 1;
}
return false;
const color = this.turn;
const oppKingPos = this.searchKingPos(C.GetOppCol(color));
- if (oppKingPos[0] < 0 || this.underCheck(oppKingPos, color))
+ if (oppKingPos.length == 0 || this.underCheck(oppKingPos, color))
return true;
return (
(
}
// What is the score ? (Interesting if game is over)
- getCurrentScore(move) {
+ getCurrentScore(move_s) {
+ const move = move_s[move_s.length - 1];
+ // Shortcut in case the score was computed before:
+ if (move.result)
+ return move.result;
const color = this.turn;
const oppCol = C.GetOppCol(color);
- const kingPos = [this.searchKingPos(color), this.searchKingPos(oppCol)];
- if (kingPos[0][0] < 0 && kingPos[1][0] < 0)
+ const kingPos = {
+ [color]: this.searchKingPos(color),
+ [oppCol]: this.searchKingPos(oppCol)
+ };
+ if (kingPos[color].length == 0 && kingPos[oppCol].length == 0)
return "1/2";
- if (kingPos[0][0] < 0)
+ if (kingPos[color].length == 0)
return (color == "w" ? "0-1" : "1-0");
- if (kingPos[1][0] < 0)
+ if (kingPos[oppCol].length == 0)
return (color == "w" ? "1-0" : "0-1");
if (this.atLeastOneMove(color))
return "*";
// No valid move: stalemate or checkmate?
- if (!this.underCheck(kingPos[0], oppCol))
+ if (!this.underCheck(kingPos[color], oppCol))
return "1/2";
// OK, checkmate
return (color == "w" ? "0-1" : "1-0");
buildMoveStack(move, r) {
this.moveStack.push(move);
this.computeNextMove(move);
- this.play(move);
- const newTurn = this.turn;
- if (this.moveStack.length == 1)
- this.playVisual(move, r);
- if (move.next) {
- this.gameState = {
- fen: this.getFen(),
- board: JSON.parse(JSON.stringify(this.board)) //easier
- };
- this.buildMoveStack(move.next, r);
- }
- else {
- if (this.moveStack.length == 1) {
- // Usual case (one normal move)
- this.afterPlay(this.moveStack, newTurn, {send: true, res: true});
- this.moveStack = []
+ const then = () => {
+ const newTurn = this.turn;
+ if (this.moveStack.length == 1 && !this.hideMoves)
+ this.playVisual(move, r);
+ if (move.next) {
+ this.gameState = {
+ fen: this.getFen(),
+ board: JSON.parse(JSON.stringify(this.board)) //easier
+ };
+ this.buildMoveStack(move.next, r);
}
else {
- this.afterPlay(this.moveStack, newTurn, {send: true, res: false});
- this.re_initFromFen(this.gameState.fen, this.gameState.board);
- this.playReceivedMove(this.moveStack.slice(1), () => {
- this.afterPlay(this.moveStack, newTurn, {send: false, res: true});
- this.moveStack = []
- });
+ if (this.moveStack.length == 1) {
+ // Usual case (one normal move)
+ this.afterPlay(this.moveStack, newTurn, {send: true, res: true});
+ this.moveStack = [];
+ }
+ else {
+ this.afterPlay(this.moveStack, newTurn, {send: true, res: false});
+ this.re_initFromFen(this.gameState.fen, this.gameState.board);
+ this.playReceivedMove(this.moveStack.slice(1), () => {
+ this.afterPlay(this.moveStack, newTurn, {send: false, res: true});
+ this.moveStack = [];
+ });
+ }
}
- }
+ };
+ // If hiding moves, then they are revealed in play() with callback
+ this.play(move, this.hideMoves ? then : null);
+ if (!this.hideMoves)
+ then();
}
// Implemented in variants using (automatic) moveStack
this.animateFading(arr, () => targetObj.increment());
}
}
+ targetObj.target +=
+ this.tryAnimateCastle(move, () => targetObj.increment());
targetObj.target +=
this.customAnimate(move, segments, () => targetObj.increment());
if (targetObj.target == 0)
callback();
}
+ tryAnimateCastle(move, cb) {
+ if (
+ this.hasCastle &&
+ move.vanish.length == 2 &&
+ move.appear.length == 2 &&
+ this.isKing(0, 0, move.vanish[0].p) &&
+ this.isKing(0, 0, move.appear[0].p)
+ ) {
+ const start = {x: move.vanish[1].x, y: move.vanish[1].y},
+ end = {x: move.appear[1].x, y: move.appear[1].y};
+ const segments = [ [[start.x, start.y], [end.x, end.y]] ];
+ this.animateMoving(start, end, null, segments, cb);
+ return 1;
+ }
+ return 0;
+ }
+
// Potential other animations (e.g. for Suction variant)
customAnimate(move, segments, cb) {
return 0; //nb of targets
}
- playReceivedMove(moves, callback) {
- const launchAnimation = () => {
- const r = container.querySelector(".chessboard").getBoundingClientRect();
- const animateRec = i => {
- this.animate(moves[i], () => {
- this.play(moves[i]);
- this.playVisual(moves[i], r);
- if (i < moves.length - 1)
- setTimeout(() => animateRec(i+1), 300);
- else
- callback();
- });
- };
- animateRec(0);
+ launchAnimation(moves, container, callback) {
+ if (this.hideMoves) {
+ for (let i=0; i<moves.length; i++)
+ // If hiding moves, they are revealed into play():
+ this.play(moves[i], i == moves.length - 1 ? callback : () => {});
+ return;
+ }
+ const r = container.querySelector(".chessboard").getBoundingClientRect();
+ const animateRec = i => {
+ this.animate(moves[i], () => {
+ this.play(moves[i]);
+ this.playVisual(moves[i], r);
+ if (i < moves.length - 1)
+ setTimeout(() => animateRec(i+1), 300);
+ else
+ callback();
+ });
};
+ animateRec(0);
+ }
+
+ playReceivedMove(moves, callback) {
// Delay if user wasn't focused:
const checkDisplayThenAnimate = (delay) => {
if (container.style.display == "none") {
alert("New move! Let's go back to game...");
document.getElementById("gameInfos").style.display = "none";
container.style.display = "block";
- setTimeout(launchAnimation, 700);
+ setTimeout(
+ () => this.launchAnimation(moves, container, callback),
+ 700
+ );
+ }
+ else {
+ setTimeout(
+ () => this.launchAnimation(moves, container, callback),
+ delay || 0
+ );
}
- else
- setTimeout(launchAnimation, delay || 0);
};
let container = document.getElementById(this.containerId);
if (document.hidden) {