// Graphical (can use variables defined above)
this.containerId = o.element;
+ this.isDiagram = o.diagram;
+ this.marks = o.marks;
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);
// 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() {
const board = this.getSvgChessboard();
const oppCol = C.GetOppCol(this.playerColor);
- let chessboard =
- document.getElementById(this.containerId).querySelector(".chessboard");
+ const container = document.getElementById(this.containerId);
+ const rc = container.getBoundingClientRect();
+ let chessboard = container.querySelector(".chessboard");
chessboard.innerHTML = "";
chessboard.insertAdjacentHTML('beforeend', board);
// Compare window ratio width / height to aspectRatio:
- const windowRatio = window.innerWidth / window.innerHeight;
+ const windowRatio = rc.width / rc.height;
let cbWidth, cbHeight;
const vRatio = this.size.ratio || 1;
if (windowRatio <= vRatio) {
// Limiting dimension is width:
- cbWidth = Math.min(window.innerWidth, 767);
+ cbWidth = Math.min(rc.width, 767);
cbHeight = cbWidth / vRatio;
}
else {
// Limiting dimension is height:
- cbHeight = Math.min(window.innerHeight, 767);
+ 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.
- if ((window.innerHeight - cbHeight) / 2 < sqSize + 5) {
- cbHeight = window.innerHeight - 2 * (sqSize + 5);
+ if ((rc.height - cbHeight) / 2 < sqSize + 5) {
+ cbHeight = rc.height - 2 * (sqSize + 5);
cbWidth = cbHeight * vRatio;
}
}
chessboard.style.width = cbWidth + "px";
chessboard.style.height = cbHeight + "px";
// Center chessboard:
- const spaceLeft = (window.innerWidth - cbWidth) / 2,
- spaceTop = (window.innerHeight - cbHeight) / 2;
+ const spaceLeft = (rc.width - cbWidth) / 2,
+ spaceTop = (rc.height - cbHeight) / 2;
chessboard.style.left = spaceLeft + "px";
chessboard.style.top = spaceTop + "px";
// Give sizes instead of recomputing them,
}
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);
}
// Resize board: no need to destroy/recreate pieces
rescale(mode) {
- let chessboard =
- document.getElementById(this.containerId).querySelector(".chessboard");
- const r = chessboard.getBoundingClientRect();
+ const container = document.getElementById(this.containerId);
+ let chessboard = container.querySelector(".chessboard");
+ const rc = container.getBoundingClientRect(),
+ r = chessboard.getBoundingClientRect();
const multFact = (mode == "up" ? 1.05 : 0.95);
let [newWidth, newHeight] = [multFact * r.width, multFact * r.height];
// Stay in window:
const vRatio = this.size.ratio || 1;
- if (newWidth > window.innerWidth) {
- newWidth = window.innerWidth;
+ if (newWidth > rc.width) {
+ newWidth = rc.width;
newHeight = newWidth / vRatio;
}
- if (newHeight > window.innerHeight) {
- newHeight = window.innerHeight;
+ if (newHeight > rc.height) {
+ newHeight = rc.height;
newWidth = newHeight * vRatio;
}
chessboard.style.width = newWidth + "px";
chessboard.style.height = newHeight + "px";
- const newX = (window.innerWidth - newWidth) / 2;
+ const newX = (rc.width - newWidth) / 2;
chessboard.style.left = newX + "px";
- const newY = (window.innerHeight - newHeight) / 2;
+ const newY = (rc.height - newHeight) / 2;
chessboard.style.top = newY + "px";
const newR = {x: newX, y: newY, width: newWidth, height: newHeight};
const pieceWidth = this.getPieceWidth(newWidth);
curPiece.remove();
};
+ const resize = (e) => this.rescale(e.deltaY < 0 ? "up" : "down");
+
if ('onmousedown' in window) {
- document.addEventListener("mousedown", mousedown);
- document.addEventListener("mousemove", mousemove);
- document.addEventListener("mouseup", mouseup);
- document.addEventListener("wheel",
- (e) => this.rescale(e.deltaY < 0 ? "up" : "down"));
+ this.mouseListeners = [
+ {type: "mousedown", listener: mousedown},
+ {type: "mousemove", listener: mousemove},
+ {type: "mouseup", listener: mouseup},
+ {type: "wheel", listener: resize}
+ ];
+ this.mouseListeners.forEach(ml => {
+ document.addEventListener(ml.type, ml.listener);
+ });
}
if ('ontouchstart' in window) {
- // https://stackoverflow.com/a/42509310/12660887
- document.addEventListener("touchstart", mousedown, {passive: false});
- document.addEventListener("touchmove", mousemove, {passive: false});
- document.addEventListener("touchend", mouseup, {passive: false});
+ this.touchListeners = [
+ {type: "touchstart", listener: mousedown},
+ {type: "touchmove", listener: mousemove},
+ {type: "touchend", listener: mouseup}
+ ];
+ this.touchListeners.forEach(tl => {
+ // https://stackoverflow.com/a/42509310/12660887
+ document.addEventListener(tl.type, tl.listener, {passive: false});
+ });
}
// TODO: onpointerdown/move/up ? See reveal.js /controllers/touch.js
}
+ 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);
+ });
+ }
+ if ('ontouchstart' in window) {
+ this.touchListeners.forEach(tl => {
+ // https://stackoverflow.com/a/42509310/12660887
+ document.removeEventListener(tl.type, tl.listener);
+ });
+ }
+ }
+
showChoices(moves, r) {
let container = document.getElementById(this.containerId);
let chessboard = container.querySelector(".chessboard");
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]));
);
}
- underCheck([x, y], oppCol) {
+ underCheck(square_s, oppCol) {
if (this.options["taking"] || this.options["dark"])
return false;
- return this.underAttack([x, y], oppCol);
+ if (!Array.isArray(square_s))
+ 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(square_s, oppCol);
+ if (oldKingPP && newKingPP)
+ kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
this.undoOnBoard(m);
filtered[key] = res;
return res;
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 (
(
getCurrentScore(move) {
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");