let inputName = document.getElementById("myName");
let formField = document.getElementById("ng-name");
const setActive = (active) => {
- if (active) formField.classList.add("form-field--is-active");
+ if (active)
+ formField.classList.add("form-field--is-active");
else {
formField.classList.remove("form-field--is-active");
inputName.value == ''
// Turn a "tab" on, and "close" all others
function toggleVisible(element) {
for (elt of document.querySelectorAll("main > div")) {
- if (elt.id != element) elt.style.display = "none";
- else elt.style.display = "block";
+ if (elt.id != element)
+ elt.style.display = "none";
+ else
+ elt.style.display = "block";
}
if (element == "boardContainer") {
// Avoid smartphone scrolling effects (TODO?)
document.querySelector("html").style.overflow = "visible";
document.body.style.overflow = "visible";
// Workaround "superposed texts" effect:
- if (element == "newGame") setActive(false);
+ if (element == "newGame")
+ setActive(false);
}
}
}
}
function cancelSeek() {
- if (send("cancelseek", {vname: seek_vname})) toggleVisible("newGame");
+ if (send("cancelseek", {vname: seek_vname}))
+ toggleVisible("newGame");
}
function sendRematch(random) {
toggleVisible("pendingRematch");
}
function cancelRematch() {
- if (send("norematch", {gid: gid})) toggleVisible("newGame");
+ if (send("norematch", {gid: gid}))
+ toggleVisible("newGame");
}
// Play with a friend (or not ^^)
function showNewGameForm() {
const vname = $.getElementById("selectVariant").value;
- if (vname == "_random") alert("Select a variant first");
+ if (vname == "_random")
+ alert("Select a variant first");
else {
$.getElementById("gameLink").innerHTML = "";
$.getElementById("selectColor").selectedIndex = 0;
toggleVisible("newGameForm");
import(`/variants/${vname}/class.js`).then(module => {
window.V = module.default;
- for (const [k, v] of Object.entries(V.Aliases)) window[k] = v;
+ for (const [k, v] of Object.entries(V.Aliases))
+ window[k] = v;
prepareOptions();
});
}
while (i < stylesLength) {
optHtml += '<div class="row">';
for (let j=i; j<i+4; j++) {
- if (j == stylesLength) break;
+ if (j == stylesLength)
+ break;
const style = V.Options.styles[j];
optHtml += `<span onClick="toggleStyle(event, this)">${style}</span>`;
}
const color = $.getElementById("selectColor").value;
for (const select of $.querySelectorAll("#gameOptions select")) {
let value = select.value;
- if (select.attributes["data-numeric"]) value = parseInt(value, 10);
- if (value) options[ select.id.split("_")[1] ] = value;
+ if (select.attributes["data-numeric"])
+ value = parseInt(value, 10);
+ if (value)
+ options[ select.id.split("_")[1] ] = value;
}
for (const check of $.querySelectorAll("#gameOptions input")) {
- if (check.checked) options[ check.id.split("_")[1] ] = check.checked;
+ if (check.checked)
+ options[ check.id.split("_")[1] ] = check.checked;
}
send("creategame", {
vname: vname,
while (i < options.length) {
htmlContent += '<div class="row">';
for (let j=i; j<i+4; j++) {
- if (j == options.length) break;
+ if (j == options.length)
+ break;
const opt = options[j];
- if (!opt[1]) continue;
+ if (!opt[1])
+ continue;
htmlContent +=
'<span class="option">' +
(opt[1] === true ? opt[0] : `${opt[0]}:${opt[1]}`) + " " +
const trySend = () => {
if (socket.readyState == 1) {
socket.send(JSON.stringify(Object.assign({code: code}, data)));
- if (opts.success) opts.success();
+ if (opts.success)
+ opts.success();
return true;
}
return false;
let sendAttempt = 1;
const retryLoop = setInterval(
() => {
- if (trySend() || ++sendAttempt >= 3) clearInterval(retryLoop);
- if (sendAttempt >= 3 && opts.error) opts.error();
+ if (trySend() || ++sendAttempt >= 3)
+ clearInterval(retryLoop);
+ if (sendAttempt >= 3 && opts.error)
+ opts.error();
},
1000
);
}
- else if (opt.error) opts.error();
+ else if (opts.error)
+ opts.error();
}
return firstTry;
}
switch (obj.code) {
// Start new game:
case "gamestart": {
- if (document.hidden) notifyMe("game");
+ if (document.hidden)
+ notifyMe("game");
gid = obj.gid;
initializeGame(obj);
break;
// Receive opponent's move:
case "newmove":
// Basic check: was it really opponent's turn?
- if (vr.turn == playerColor) break;
- if (document.hidden) notifyMe("move");
+ if (vr.turn == playerColor)
+ break;
+ if (document.hidden)
+ notifyMe("move");
vr.playReceivedMove(obj.moves, () => {
if (vr.getCurrentScore(obj.moves[obj.moves.length-1]) != "*") {
localStorage.removeItem("gid");
setTimeout( () => toggleVisible("gameStopped"), 2000 );
}
- else toggleTurnIndicator(true);
+ else
+ toggleTurnIndicator(true);
});
break;
// Opponent stopped game (draw, abort, resign...)
function toggleTurnIndicator(myTurn) {
let indicator =
$.getElementById("boardContainer").querySelector(".chessboard");
- if (myTurn) indicator.style.outline = "thick solid green";
- else indicator.style.outline = "thick solid lightgrey";
+ if (myTurn)
+ indicator.style.outline = "thick solid green";
+ else
+ indicator.style.outline = "thick solid lightgrey";
}
function notifyMe(code) {
new Notification("New " + code, { vibrate: [200, 100, 200] });
new Audio("/assets/new_" + code + ".mp3").play();
}
- if (Notification.permission === "granted") doNotify();
+ if (Notification.permission === "granted")
+ doNotify();
else if (Notification.permission !== "denied") {
Notification.requestPermission().then(permission => {
- if (permission === "granted") doNotify();
+ if (permission === "granted")
+ doNotify();
});
}
}
const options = obj.options || {};
import(`/variants/${obj.vname}/class.js`).then(module => {
window.V = module.default;
- for (const [k, v] of Object.entries(V.Aliases)) window[k] = v;
+ for (const [k, v] of Object.entries(V.Aliases))
+ window[k] = v;
// Load CSS. Avoid loading twice the same stylesheet:
const allIds = [].slice.call($.styleSheets).map(s => s.id);
const newId = obj.vname + "_css";
}
}
fillGameInfos(obj, playerColor == "w" ? 1 : 0);
- if (obj.randvar) toggleVisible("gameInfos");
- else toggleVisible("boardContainer");
+ if (obj.randvar)
+ toggleVisible("gameInfos");
+ else
+ toggleVisible("boardContainer");
toggleTurnIndicator(vr.turn == playerColor);
});
}
function toggleGameInfos() {
if ($.getElementById("gameInfos").style.display == "none")
toggleVisible("gameInfos");
- else toggleVisible("boardContainer");
+ else
+ toggleVisible("boardContainer");
}
$.body.addEventListener("keydown", (e) => {
- if (!localStorage.getItem("gid")) return;
- if (e.keyCode == 27) confirmStopGame();
+ if (!localStorage.getItem("gid"))
+ return;
+ if (e.keyCode == 27)
+ confirmStopGame();
else if (e.keyCode == 32) {
e.preventDefault();
toggleGameInfos();
variable: "randomness",
defaut: 0,
options: [
- { label: "Deterministic", value: 0 },
- { label: "Symmetric random", value: 1 },
- { label: "Asymmetric random", value: 2 }
+ {label: "Deterministic", value: 0},
+ {label: "Symmetric random", value: 1},
+ {label: "Asymmetric random", value: 2}
]
}],
check: [
// Pawns specifications
get pawnSpecs() {
return {
- directions: { 'w': -1, 'b': 1 },
- initShift: { w: 1, b: 1 },
+ directions: {w: -1, b: 1},
+ initShift: {w: 1, b: 1},
twoSquares: true,
threeSquares: false,
canCapture: true,
// Some variants use click infos:
doClick([x, y]) {
- if (typeof x != "number") return null; //click on reserves
+ if (typeof x != "number")
+ return null; //click on reserves
if (
this.options["teleport"] && this.subTurnTeleport == 2 &&
this.board[x][y] == ""
}
idToCoords(targetId) {
- if (!targetId) return null; //outside page, maybe...
+ if (!targetId)
+ return null; //outside page, maybe...
const idParts = targetId.split('|'); //prefix|sq-2-3 (start at 0 => 3,4)
if (
idParts.length < 2 ||
}
// Add turn + flags + enpassant (+ reserve)
let parts = [];
- if (this.hasFlags) parts.push(`"flags":"${flags}"`);
- if (this.hasEnpassant) parts.push('"enpassant":"-"');
- if (this.hasReserve) parts.push('"reserve":"000000000000"');
- if (this.options["crazyhouse"]) parts.push('"ispawn":"-"');
- if (parts.length >= 1) fen += " {" + parts.join(",") + "}";
+ if (this.hasFlags)
+ parts.push(`"flags":"${flags}"`);
+ if (this.hasEnpassant)
+ parts.push('"enpassant":"-"');
+ if (this.hasReserve)
+ parts.push('"reserve":"000000000000"');
+ if (this.options["crazyhouse"])
+ parts.push('"ispawn":"-"');
+ if (parts.length >= 1)
+ fen += " {" + parts.join(",") + "}";
return fen;
}
turn: fenParts[1],
movesCount: fenParts[2]
};
- if (fenParts.length > 3) res = Object.assign(res, JSON.parse(fenParts[3]));
+ if (fenParts.length > 3)
+ res = Object.assign(res, JSON.parse(fenParts[3]));
return res;
}
this.movesCount
);
let parts = [];
- if (this.hasFlags) parts.push(`"flags":"${this.getFlagsFen()}"`);
+ if (this.hasFlags)
+ parts.push(`"flags":"${this.getFlagsFen()}"`);
if (this.hasEnpassant)
parts.push(`"enpassant":"${this.getEnpassantFen()}"`);
- if (this.hasReserve) parts.push(`"reserve":"${this.getReserveFen()}"`);
+ if (this.hasReserve)
+ parts.push(`"reserve":"${this.getReserveFen()}"`);
if (this.options["crazyhouse"])
parts.push(`"ispawn":"${this.getIspawnFen()}"`);
- if (parts.length >= 1) fen += " {" + parts.join(",") + "}";
+ if (parts.length >= 1)
+ fen += " {" + parts.join(",") + "}";
return fen;
}
const format = (count) => {
// if more than 9 consecutive free spaces, break the integer,
// otherwise FEN parsing will fail.
- if (count <= 9) return count;
+ if (count <= 9)
+ return count;
// Most boards of size < 18:
- if (count <= 18) return "9" + (count - 9);
+ if (count <= 18)
+ return "9" + (count - 9);
// Except Gomoku:
return "99" + (count - 18);
};
for (let i = 0; i < this.size.y; i++) {
let emptyCount = 0;
for (let j = 0; j < this.size.x; j++) {
- if (this.board[i][j] == "") emptyCount++;
+ if (this.board[i][j] == "")
+ emptyCount++;
else {
if (emptyCount > 0) {
// Add empty squares in-between
if (emptyCount > 0)
// "Flush remainder"
position += format(emptyCount);
- if (i < this.size.y - 1) position += "/"; //separate rows
+ if (i < this.size.y - 1)
+ position += "/"; //separate rows
}
return position;
}
// Enpassant part of the FEN string
getEnpassantFen() {
- if (!this.epSquare) return "-"; //no en-passant
+ if (!this.epSquare)
+ return "-"; //no en-passant
return C.CoordsToSquare(this.epSquare);
}
getIspawnFen() {
const coords = Object.keys(this.ispawn);
- if (coords.length == 0) return "-";
+ if (coords.length == 0)
+ return "-";
return coords.map(C.CoordsToSquare).join(",");
}
this.afterPlay = o.afterPlay;
// FEN-related:
- if (!o.fen) o.fen = this.genRandInitFen(o.seed);
+ if (!o.fen)
+ o.fen = this.genRandInitFen(o.seed);
const fenParsed = this.parseFen(o.fen);
this.board = this.getBoard(fenParsed.position);
this.turn = fenParsed.turn;
const character = rows[i][indexInRow];
const num = parseInt(character, 10);
// If num is a number, just shift j:
- if (!isNaN(num)) j += num;
+ if (!isNaN(num))
+ j += num;
// Else: something at position i,j
- else board[i][j++] = this.fen2board(character);
+ else
+ board[i][j++] = this.fen2board(character);
}
}
return board;
// Some additional variables from FEN (variant dependant)
setOtherVariables(fenParsed) {
// Set flags and enpassant:
- if (this.hasFlags) this.setFlags(fenParsed.flags);
+ if (this.hasFlags)
+ this.setFlags(fenParsed.flags);
if (this.hasEnpassant)
this.epSquare = this.getEpSquare(fenParsed.enpassant);
- if (this.hasReserve) this.initReserves(fenParsed.reserve);
- if (this.options["crazyhouse"]) this.initIspawn(fenParsed.ispawn);
+ if (this.hasReserve)
+ this.initReserves(fenParsed.reserve);
+ if (this.options["crazyhouse"])
+ this.initIspawn(fenParsed.ispawn);
this.subTurn = 1; //may be unused
if (this.options["teleport"]) {
this.subTurnTeleport = 1;
}
}
}
- if (this.epSquare) this.enlightEnpassant(newEnlightened);
- if (withGraphics) this.graphUpdateEnlightened(newEnlightened);
+ if (this.epSquare)
+ this.enlightEnpassant(newEnlightened);
+ if (withGraphics)
+ this.graphUpdateEnlightened(newEnlightened);
this.enlightened = newEnlightened;
}
this.reserve = { w: {}, b: {} };
const pieceName = Object.keys(this.pieces());
for (let i of ArrayFun.range(12)) {
- if (i < 6) this.reserve['w'][pieceName[i]] = counts[i];
- else this.reserve['b'][pieceName[i-6]] = counts[i];
+ if (i < 6)
+ this.reserve['w'][pieceName[i]] = counts[i];
+ else
+ this.reserve['b'][pieceName[i-6]] = counts[i];
}
}
this.ispawn = ispawnStr.split(",").map(C.SquareToCoords)
.reduce((o, key) => ({ ...o, [key]: true}), {});
}
- else this.ispawn = {};
+ else
+ this.ispawn = {};
}
getNbReservePieces(color) {
}
}
}
- else this.g_pieces = ArrayFun.init(this.size.x, this.size.y, 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();
+ if (!r)
+ r = chessboard.getBoundingClientRect();
const pieceWidth = this.getPieceWidth(r.width);
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
}
}
}
- if (this.reserve) this.re_drawReserve(['w', 'b'], r);
+ if (this.reserve)
+ this.re_drawReserve(['w', 'b'], r);
}
// NOTE: assume !!this.reserve
if (this.r_pieces) {
// Remove (old) reserve pieces
for (let c of colors) {
- if (!this.reserve[c]) continue;
+ if (!this.reserve[c])
+ continue;
Object.keys(this.reserve[c]).forEach(p => {
if (this.r_pieces[c][p]) {
this.r_pieces[c][p].remove();
}
});
let reservesDiv = document.getElementById("reserves_" + c);
- if (reservesDiv) reservesDiv.remove();
+ if (reservesDiv)
+ reservesDiv.remove();
}
}
- else this.r_pieces = { 'w': {}, 'b': {} };
+ else
+ this.r_pieces = { 'w': {}, 'b': {} };
let chessboard =
document.getElementById(this.containerId).querySelector(".chessboard");
- if (!r) r = chessboard.getBoundingClientRect();
+ if (!r)
+ r = chessboard.getBoundingClientRect();
for (let c of colors) {
- if (!this.reserve[c]) continue;
+ if (!this.reserve[c])
+ continue;
const nbR = this.getNbReservePieces(c);
- if (nbR == 0) continue;
+ if (nbR == 0)
+ continue;
const sqResSize = this.getReserveSquareSize(r.width, nbR);
let ridx = 0;
const vShift = (c == this.playerColor ? r.height + 5 : -sqResSize - 5);
rcontainer.style.height = sqResSize + "px";
chessboard.appendChild(rcontainer);
for (let p of Object.keys(this.reserve[c])) {
- if (this.reserve[c][p] == 0) continue;
+ if (this.reserve[c][p] == 0)
+ continue;
let r_cell = document.createElement("div");
r_cell.id = this.coordsToId([c, p]);
r_cell.classList.add("reserve-cell");
const oldCount = this.reserve[color][piece];
this.reserve[color][piece] = count;
// Redrawing is much easier if count==0
- if ([oldCount, count].includes(0)) this.re_drawReserve([color]);
+ if ([oldCount, count].includes(0))
+ this.re_drawReserve([color]);
else {
const numId = this.getReserveNumId(color, piece);
document.getElementById(numId).textContent = count;
// After resize event: no need to destroy/recreate pieces
rescale() {
const container = document.getElementById(this.containerId);
- if (!container) return; //useful at initial loading
+ if (!container)
+ return; //useful at initial loading
let chessboard = container.querySelector(".chessboard");
const r = chessboard.getBoundingClientRect();
const newRatio = r.width / r.height;
}
}
}
- if (this.reserve) this.rescaleReserve(newR);
+ if (this.reserve)
+ this.rescaleReserve(newR);
}
rescaleReserve(r) {
for (let c of ['w','b']) {
- if (!this.reserve[c]) continue;
+ if (!this.reserve[c])
+ continue;
const nbR = this.getNbReservePieces(c);
- if (nbR == 0) continue;
+ if (nbR == 0)
+ continue;
// Resize container first
const sqResSize = this.getReserveSquareSize(r.width, nbR);
const vShift = (c == this.playerColor ? r.height + 5 : -sqResSize - 5);
// And then reserve cells:
const rpieceWidth = this.getReserveSquareSize(r.width, nbR);
Object.keys(this.reserve[c]).forEach(p => {
- if (this.reserve[c][p] == 0) return;
+ if (this.reserve[c][p] == 0)
+ return;
let r_cell = document.getElementById(this.coordsToId([c, p]));
r_cell.style.width = sqResSize + "px";
r_cell.style.height = sqResSize + "px";
// We return here the CSS coordinates (more useful).
getPixelPosition(i, j, r) {
const sqSize = this.getSquareWidth(r.width);
- if (i < 0 || j < 0) return [0, 0]; //piece vanishes
+ if (i < 0 || j < 0)
+ return [0, 0]; //piece vanishes
const flipped = (this.playerColor == 'b');
const x = (flipped ? this.size.y - 1 - j : j) * sqSize;
const y = (flipped ? this.size.x - 1 - i : i) * sqSize;
sqSize;
const mousedown = (e) => {
// Disable zoom on smartphones:
- if (e.touches && e.touches.length > 1) e.preventDefault();
+ if (e.touches && e.touches.length > 1)
+ e.preventDefault();
r = chessboard.getBoundingClientRect();
sqSize = this.getSquareWidth(r.width);
const square = this.idToCoords(e.target.id);
if (square) {
const [i, j] = square;
const move = this.doClick([i, j]);
- if (move) this.playPlusVisual(move);
+ if (move)
+ this.playPlusVisual(move);
else {
- if (typeof i != "number") startPiece = this.r_pieces[i][j];
- else if (this.g_pieces[i][j]) startPiece = this.g_pieces[i][j];
+ if (typeof i != "number")
+ startPiece = this.r_pieces[i][j];
+ else if (this.g_pieces[i][j])
+ startPiece = this.g_pieces[i][j];
if (startPiece && this.canIplay(i, j)) {
e.preventDefault();
start = { x: i, y: j };
this.rescale();
return;
}
- if (!start) return;
+ if (!start)
+ return;
const [x, y] = [start.x, start.y];
start = null;
e.preventDefault();
const potentialMoves = this.getPotentialMovesFrom([x, y])
.filter(m => m.end.x == i && m.end.y == j);
const moves = this.filterValid(potentialMoves);
- if (moves.length >= 2) this.showChoices(moves, r);
- else if (moves.length == 1) this.playPlusVisual(moves[0], r);
+ if (moves.length >= 2)
+ this.showChoices(moves, r);
+ else if (moves.length == 1)
+ this.playPlusVisual(moves[0], r);
}
curPiece.remove();
};
// For Cylinder: get Y coordinate
computeY(y) {
- if (!this.options["cylinder"]) return y;
+ if (!this.options["cylinder"])
+ return y;
let res = y % this.size.y;
- if (res < 0) res += this.size.y;
+ if (res < 0)
+ res += this.size.y;
return res;
}
let [ii, jj] = [i + step[0], this.computeY(j + step[1])];
let stepCounter = 1;
while (this.onBoard(ii, jj) && this.board[ii][jj] == "") {
- if (specs.range <= stepCounter++) continue outerLoop;
+ if (specs.range <= stepCounter++)
+ continue outerLoop;
ii += step[0];
jj = this.computeY(jj + step[1]);
}
getDropMovesFrom([c, p]) {
// NOTE: by design, this.reserve[c][p] >= 1 on user click
// (but not necessarily otherwise)
- if (this.reserve[c][p] == 0) return [];
+ if (this.reserve[c][p] == 0)
+ return [];
let moves = [];
for (let i=0; i<this.size.x; i++) {
for (let j=0; j<this.size.y; j++) {
// All possible moves from selected square
getPotentialMovesFrom(sq, color) {
- if (typeof sq[0] == "string") return this.getDropMovesFrom(sq);
- if (this.options["madrasi"] && this.isImmobilized(sq)) return [];
+ if (typeof sq[0] == "string")
+ return this.getDropMovesFrom(sq);
+ if (this.options["madrasi"] && this.isImmobilized(sq))
+ return [];
const piece = this.getPieceType(sq[0], sq[1]);
let moves;
- if (piece == "p") moves = this.getPotentialPawnMoves(sq);
- else moves = this.getPotentialMovesOf(piece, sq);
+ if (piece == "p")
+ moves = this.getPotentialPawnMoves(sq);
+ else
+ moves = this.getPotentialMovesOf(piece, sq);
if (
piece == "k" &&
this.hasCastle &&
}
postProcessPotentialMoves(moves) {
- if (moves.length == 0) return [];
+ if (moves.length == 0)
+ return [];
const color = this.getColor(moves[0].start.x, moves[0].start.y);
const oppCol = C.GetOppCol(color);
);
}
}
- if (!this.options["rifle"]) m.appear.pop(); //nothin appears
+ if (!this.options["rifle"])
+ m.appear.pop(); //nothin appears
}
});
}
let [i, j] = [x + step[0], y + step[1]];
let stepCounter = 1;
while (this.onBoard(i, j) && this.board[i][j] == "") {
- if (range <= stepCounter++) continue outerLoop;
+ if (range <= stepCounter++)
+ continue outerLoop;
i += step[0];
j = this.computeY(j + step[1]);
}
) {
explored[i + "." + j] = true;
moves.push(this.getBasicMove([x, y], [i, j]));
- if (range <= stepCounter++) continue outerLoop;
+ if (range <= stepCounter++)
+ continue outerLoop;
i += step[0];
j = this.computeY(j + step[1]);
}
return; //king isn't captured this way
}
const steps = pieces[p].attack || pieces[p].steps;
- if (!steps) return; //cannibal king for example (TODO...)
+ if (!steps)
+ return; //cannibal king for example (TODO...)
const range = pieces[p].range;
steps.forEach(s => {
// From x,y: revert step
let [i, j] = [x - s[0], this.computeY(y - s[1])];
let stepCounter = 1;
while (this.onBoard(i, j) && this.board[i][j] == "") {
- if (range <= stepCounter++) return;
+ if (range <= stepCounter++)
+ return;
i -= s[0];
j = this.computeY(j - s[1]);
}
this.getColor(i, j) == oppCol && //condition for Recycle & Teleport
this.canTake([i, j], [x, y])
) {
- if (pieceType != "p") moves.push(this.getBasicMove([x, y], [i, j]));
- else this.addPawnMoves([x, y], [i, j], moves);
+ if (pieceType != "p")
+ moves.push(this.getBasicMove([x, y], [i, j]));
+ else
+ this.addPawnMoves([x, y], [i, j], moves);
}
});
});
let trPiece = mv.vanish[lastIdx].p;
if (this.isKing(this.getPiece(sx, sy)))
trPiece = C.CannibalKingCode[trPiece];
- if (mv.appear.length >= 1) mv.appear[0].p = trPiece;
+ if (mv.appear.length >= 1)
+ mv.appear[0].p = trPiece;
else if (this.options["rifle"]) {
mv.appear.unshift(
new PiPo({
getEpSquare(moveOrSquare) {
if (typeof moveOrSquare === "string") {
const square = moveOrSquare;
- if (square == "-") return undefined;
+ if (square == "-")
+ return undefined;
return C.SquareToCoords(square);
}
// Argument is a move:
) {
finalPieces = [this.getPieceType(x2, y2)];
}
- else if (promotions) finalPieces = promotions;
+ else if (promotions)
+ finalPieces = promotions;
else if (this.pawnSpecs.promotions)
finalPieces = this.pawnSpecs.promotions;
}
castleSide < 2;
castleSide++ //large, then small
) {
- if (this.castleFlags[c][castleSide] >= this.size.y) continue;
+ if (this.castleFlags[c][castleSide] >= this.size.y)
+ continue;
// If this code is reached, rook and king are on initial position
// NOTE: in some variants this is not a rook
// 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] != "") continue castlingCheck;
+ if (this.board[x][i] != "")
+ continue castlingCheck;
}
// Nothing on final squares, except maybe king and castling rook?
// Is (king at) given position under check by "color" ?
underCheck([x, y], color) {
- if (this.options["taking"] || this.options["dark"]) return false;
+ if (this.options["taking"] || this.options["dark"])
+ return false;
color = color || C.GetOppCol(this.getColor(x, y));
const pieces = this.pieces(color);
return Object.keys(pieces).some(p => {
isAttackedBy([x, y], piece, color, stepSpec) {
const steps = stepSpec.attack || stepSpec.steps;
- if (!steps) return false; //cannibal king, for example
+ if (!steps)
+ return false; //cannibal king, for example
const range = stepSpec.range;
let explored = {}; //for Cylinder mode
outerLoop: for (let step of steps) {
!explored[rx + "." + ry]
) {
explored[rx + "." + ry] = true;
- if (range <= stepCounter++) continue outerLoop;
+ if (range <= stepCounter++)
+ continue outerLoop;
rx -= step[0];
ry = this.computeY(ry - step[1]);
}
}
filterValid(moves) {
- if (moves.length == 0) return [];
+ if (moves.length == 0)
+ return [];
const color = this.turn;
const oppCol = C.GetOppCol(color);
if (this.options["balance"] && [1, 3].includes(this.movesCount)) {
return res;
});
}
- if (this.options["taking"] || this.options["dark"]) return moves;
+ if (this.options["taking"] || this.options["dark"])
+ return moves;
const kingPos = this.searchKingPos(color);
let filtered = {}; //avoid re-checking similar moves (promotions...)
return moves.filter(m => {
});
if (newKingIdx >= 0)
square = [m.appear[newKingIdx].x, m.appear[newKingIdx].y];
- else res = false;
+ else
+ res = false;
}
res &&= !this.underCheck(square, oppCol);
this.undoOnBoard(m);
}
// NOTE: not "else if" because king can capture enemy rook...
let c = "";
- if (psq.x == 0) c = "b";
- else if (psq.x == this.size.x - 1) c = "w";
+ if (psq.x == 0)
+ c = "b";
+ else if (psq.x == this.size.x - 1)
+ c = "w";
if (c != "") {
const fidx = this.castleFlags[c].findIndex(f => f == psq.y);
- if (fidx >= 0) this.castleFlags[c][fidx] = this.size.y;
+ if (fidx >= 0)
+ this.castleFlags[c][fidx] = this.size.y;
}
});
}
play(move) {
this.prePlay(move);
- if (this.hasEnpassant) this.epSquare = this.getEpSquare(move);
+ if (this.hasEnpassant)
+ this.epSquare = this.getEpSquare(move);
this.playOnBoard(move);
this.postPlay(move);
}
postPlay(move) {
const color = this.turn;
const oppCol = C.GetOppCol(color);
- if (this.options["dark"]) this.updateEnlightened(true);
+ if (this.options["dark"])
+ this.updateEnlightened(true);
if (this.options["teleport"]) {
if (
this.subTurnTeleport == 1 &&
this.captured = null;
}
if (this.options["balance"]) {
- if (![1, 3].includes(this.movesCount)) this.turn = oppCol;
+ if (![1, 3].includes(this.movesCount))
+ this.turn = oppCol;
}
else {
if (
// NOTE: in fact searching for all potential moves from i,j.
// I don't believe this is an issue, for now at least.
const moves = this.getPotentialMovesFrom([i, j]);
- if (moves.some(m => this.filterValid([m]).length >= 1)) return true;
+ if (moves.some(m => this.filterValid([m]).length >= 1))
+ return true;
}
}
}
if (this.hasReserve && this.reserve[color]) {
for (let p of Object.keys(this.reserve[color])) {
const moves = this.getDropMovesFrom([color, p]);
- if (moves.some(m => this.filterValid([m]).length >= 1)) return true;
+ if (moves.some(m => this.filterValid([m]).length >= 1))
+ return true;
}
}
return false;
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) return "1/2";
- if (kingPos[0][0] < 0) return (color == "w" ? "0-1" : "1-0");
- if (kingPos[1][0] < 0) return (color == "w" ? "1-0" : "0-1");
- if (this.atLeastOneMove()) return "*";
+ if (kingPos[0][0] < 0 && kingPos[1][0] < 0)
+ return "1/2";
+ if (kingPos[0][0] < 0)
+ return (color == "w" ? "0-1" : "1-0");
+ if (kingPos[1][0] < 0)
+ return (color == "w" ? "1-0" : "0-1");
+ if (this.atLeastOneMove())
+ return "*";
// No valid move: stalemate or checkmate?
- if (!this.underCheck(kingPos, color)) return "1/2";
+ if (!this.underCheck(kingPos, color))
+ return "1/2";
// OK, checkmate
return (color == "w" ? "0-1" : "1-0");
}
});
let chessboard =
document.getElementById(this.containerId).querySelector(".chessboard");
- if (!r) r = chessboard.getBoundingClientRect();
+ if (!r)
+ r = chessboard.getBoundingClientRect();
const pieceWidth = this.getPieceWidth(r.width);
move.appear.forEach(a => {
- if (this.enlightened && !this.enlightened[a.x][a.y]) return;
+ if (this.enlightened && !this.enlightened[a.x][a.y])
+ return;
this.g_pieces[a.x][a.y] = document.createElement("piece");
this.g_pieces[a.x][a.y].classList.add(this.pieces()[a.p]["class"]);
this.g_pieces[a.x][a.y].classList.add(a.c == "w" ? "white" : "black");
let nbR = 0,
ridx = 0;
for (let pi of Object.keys(this.reserve[c])) {
- if (this.reserve[c][pi] == 0) continue;
- if (pi == p) ridx = nbR;
+ if (this.reserve[c][pi] == 0)
+ continue;
+ if (pi == p)
+ ridx = nbR;
nbR++;
}
const rsqSize = this.getReserveSquareSize(r.width, nbR);
);
if (clonePiece) {
startPiece = startPiece.cloneNode();
- if (this.options["rifle"]) startArray[i1][j1].style.opacity = "0";
+ if (this.options["rifle"])
+ startArray[i1][j1].style.opacity = "0";
if (this.options["teleport"] && this.subTurnTeleport == 2) {
const pieces = this.pieces();
const startCode = (dropMove ? j1 : this.getPiece(i1, j1));
this.size.y / 2 //not trying to be accurate here... (TODO?)
];
}
- else startCoords = [i1, j1];
+ else
+ startCoords = [i1, j1];
const r = chessboard.getBoundingClientRect();
const arrival = this.getPixelPosition(i2, j2, r); //TODO: arrival on drop?
let rs = [0, 0];
- if (dropMove) rs = this.getReserveShift(i1, j1, r);
+ if (dropMove)
+ rs = this.getReserveShift(i1, j1, r);
const distance =
Math.sqrt((startCoords[0] - i2) ** 2 + (startCoords[1] - j2) ** 2);
const maxDist = Math.sqrt((this.size.x - 1)** 2 + (this.size.y - 1) ** 2);
setTimeout(
() => {
if (clonePiece) {
- if (this.options["rifle"]) startArray[i1][j1].style.opacity = "1";
+ if (this.options["rifle"])
+ startArray[i1][j1].style.opacity = "1";
startPiece.remove();
}
else {
this.animate(moves[i], () => {
this.playVisual(moves[i], r);
this.play(moves[i]);
- if (i < moves.length - 1) setTimeout(() => animateRec(i+1), 300);
- else callback();
+ if (i < moves.length - 1)
+ setTimeout(() => animateRec(i+1), 300);
+ else
+ callback();
});
};
animateRec(0);
container.style.display = "block";
setTimeout(launchAnimation, 700);
}
- else setTimeout(launchAnimation, delay || 0);
+ else
+ setTimeout(launchAnimation, delay || 0);
};
let container = document.getElementById(this.containerId);
if (document.hidden) {
checkDisplayThenAnimate(700);
};
}
- else checkDisplayThenAnimate();
+ else
+ checkDisplayThenAnimate();
}
};
const params = require("./parameters.js");
const WebSocket = require("ws");
-const wss = new WebSocket.Server(
- {port: params.socket_port, path: params.socket_path});
+const wss = new WebSocket.Server({
+ port: params.socket_port,
+ path: params.socket_path
+});
let challenges = {}; //variantName --> socketId, name
let games = {}; //gameId --> gameInfo (vname, fen, players, options, time)
const socket = sockets[sid];
// If a player deletes local infos and then tries to resume a game,
// sockets[oppSid] will probably not exist anymore:
- if (socket) socket.send(JSON.stringify(Object.assign({ code: code }, data)));
+ if (socket)
+ socket.send(JSON.stringify(Object.assign({ code: code }, data)));
}
-function launchGame(vname, players, options) {
+function initializeGame(vname, players, options) {
const gid =
Crypto.randomBytes(randstrSize).toString("hex").slice(0, randstrSize);
games[gid] = {
options: options,
time: Date.now()
};
- if (players.every(p => p)) {
- const gameInfo = Object.assign(
- // Provide seed so that both players initialize with same FEN
- {seed: Math.floor(Math.random() * 1984), gid: gid},
- games[gid]);
- for (const p of players) {
- send(p.sid,
- "gamestart",
- Object.assign({randvar: p.randvar}, gameInfo));
- }
- }
- else {
- // Incomplete players array: do not start game yet
- send(sid, "gamecreated", {gid: gid});
- // If nobody joins within 5 minutes, delete game
- setTimeout(
- () => {
- if (games[gid] && games[gid].players.some(p => !p))
- delete games[gid];
- },
- 5 * 60000
- );
+ return gid;
+}
+
+// Provide seed in case of, so that both players initialize with same FEN
+function launchGame(gid) {
+ const gameInfo = Object.assign(
+ {seed: Math.floor(Math.random() * 1984), gid: gid},
+ games[gid]
+ );
+ // players array is supposed to be full:
+ for (const p of games[gid].players) {
+ send(p.sid,
+ "gamestart",
+ Object.assign({randvar: p.randvar}, gameInfo));
}
}
}
if (opponent) {
delete challenges[choice];
- if (choice == "_random") choice = getRandomVariant();
+ if (choice == "_random")
+ choice = getRandomVariant();
// Launch game
let players = [
{sid: sid, name: obj.name, randvar: randvar},
opponent
];
- if (Math.random() < 0.5) players = players.reverse();
- launchGame(choice, players, {}); //empty options => default
+ if (Math.random() < 0.5)
+ players = players.reverse();
+ // Empty options = default
+ launchGame( initializeGame(choice, players, {}) );
}
else
// Place challenge and wait. 'randvar' indicate if we play anything
games[obj.gid].fen = obj.fen;
break;
// Send back game informations
- case "getgame": {
- if (!games[obj.gid]) send(sid, "nogame");
- else send(sid, "gameinfo", games[obj.gid]);
+ case "getgame":
+ if (!games[obj.gid])
+ send(sid, "nogame");
+ else
+ send(sid, "gameinfo", games[obj.gid]);
break;
- }
// Cancel challenge
case "cancelseek":
delete challenges[obj.vname];
break;
// Receive rematch
case "rematch":
- if (!games[obj.gid]) send(sid, "closerematch");
+ if (!games[obj.gid])
+ send(sid, "closerematch");
else {
const myIndex = (games[obj.gid].players[0].sid == sid ? 0 : 1);
- if (!games[obj.gid].rematch) games[obj.gid].rematch = [0, 0];
+ if (!games[obj.gid].rematch)
+ games[obj.gid].rematch = [0, 0];
games[obj.gid].rematch[myIndex] = !obj.random ? 1 : 2;
if (games[obj.gid].rematch[1-myIndex]) {
// Launch new game, colors reversed
let vname = games[obj.gid].vname;
const allrand = games[obj.gid].rematch.every(r => r == 2);
- if (allrand) vname = getRandomVariant();
+ if (allrand)
+ vname = getRandomVariant();
games[obj.gid].players.forEach(p =>
p.randvar = allrand ? true : false);
- launchGame(vname,
- games[obj.gid].players.reverse(),
- games[obj.gid].options);
+ const gid = initializeGame(vname,
+ games[obj.gid].players.reverse(),
+ games[obj.gid].options);
+ launchGame(gid);
}
}
break;
) {
players = players.reverse();
}
- launchGame(obj.vname, players, obj.options);
+ // Incomplete players array: do not start game yet
+ const gid = initializeGame(obj.vname, players, obj.options);
+ send(sid, "gamecreated", {gid: gid});
+ // If nobody joins within 3 minutes, delete game
+ setTimeout(
+ () => {
+ if (games[gid] && games[gid].players.some(p => !p))
+ delete games[gid];
+ },
+ 3 * 60000
+ );
break;
}
// Join game vs. friend
case "joingame":
- if (!games[obj.gid]) send(sid, "jointoolate");
+ if (!games[obj.gid])
+ send(sid, "jointoolate");
else {
- // Join a game (started by some other player)
const emptySlot = games[obj.gid].players.findIndex(p => !p);
- if (emptySlot < 0) send(sid, "jointoolate");
- games[obj.gid].players[emptySlot] = {sid: sid, name: obj.name};
- const gameInfo = Object.assign(
- // Provide seed so that both players initialize with same FEN
- {seed: Math.floor(Math.random()*1984), gid: obj.gid},
- games[obj.gid]);
- for (const p of games[obj.gid].players)
- send(p.sid, "gamestart", gameInfo);
+ if (emptySlot < 0)
+ send(sid, "jointoolate");
+ else {
+ // Join a game (started by some other player)
+ games[obj.gid].players[emptySlot] = {sid: sid, name: obj.name};
+ launchGame(obj.gid);
+ }
}
break;
// Relay a move + update games object
}
}
for (let g of Object.values(games)) {
- const myIndex = g.players.findIndex(p => p.sid == sid);
+ const myIndex = g.players.findIndex(p => p && p.sid == sid);
if (myIndex >= 0) {
if (g.rematch && g.rematch[myIndex] > 0) g.rematch[myIndex] = 0;
break; //only one game per player
const heartbeat = setInterval(() => {
wss.clients.forEach((ws) => {
- if (ws.isAlive === false) return ws.terminate();
+ if (ws.isAlive === false)
+ return ws.terminate();
ws.isAlive = false;
ws.ping();
});
const killOldGames = setInterval(() => {
const now = Date.now();
Object.keys(games).forEach(gid => {
- if (now - games[gid].time >= dayInMillisecs) delete games[gid];
+ if (now - games[gid].time >= dayInMillisecs)
+ delete games[gid];
});
}, dayInMillisecs);