implement bag factor

This commit is contained in:
Laurent Mazet 2021-04-26 08:44:59 +02:00
parent 6c520e63ce
commit bfd0267d6e
2 changed files with 67 additions and 63 deletions

View File

@ -187,6 +187,7 @@
let boardLangSelect; let boardLangSelect;
let boardLabelSelect; let boardLabelSelect;
const downloadedDictionaries = {}; const downloadedDictionaries = {};
const boardDef = {nbRows: 0, nbColumns: 0, rackLength: 0};
const playerLetters = []; const playerLetters = [];
let currentPlayer = ""; let currentPlayer = "";
@ -239,15 +240,13 @@
} }
function setRack(rack) { function setRack(rack) {
const rackLength = getBoardDimensions(getSetting("BoardLabel"))[2]; for (let i = 0; i < boardDef.rackLength; i++) {
for (let i = 0; i < rackLength; i++) {
setTileParent(playerLetters[i], rack[i] || ""); setTileParent(playerLetters[i], rack[i] || "");
} }
} }
function getFreeRackSpaceIndex() { function getFreeRackSpaceIndex() {
const rackLength = getBoardDimensions(getSetting("BoardLabel"))[2]; for (let i = 0; i < boardDef.rackLength; i++) {
for (let i = 0; i < rackLength; i++) {
if (!playerLetters[i].getElementsByClassName("tile")[0]) { if (!playerLetters[i].getElementsByClassName("tile")[0]) {
return i; return i;
} }
@ -456,8 +455,7 @@
} }
} }
} else if (tileInitDest.getElementsByClassName("tile")[0]) { } else if (tileInitDest.getElementsByClassName("tile")[0]) {
const rackLength = getBoardDimensions(getSetting("BoardLabel"))[2]; for (let i = 0; i < boardDef.rackLength; i++) {
for (let i = 0; i < rackLength; i++) {
if (!playerLetters[i].getElementsByClassName("tile")[0]) { if (!playerLetters[i].getElementsByClassName("tile")[0]) {
playerLetters[i].appendChild(movingTile); playerLetters[i].appendChild(movingTile);
break; break;
@ -762,17 +760,8 @@
setTileParent(boardCells[index].getElementsByClassName("tile-placeholder")[0], letter, highlight); setTileParent(boardCells[index].getElementsByClassName("tile-placeholder")[0], letter, highlight);
} }
function getBoardDimensions(boardLabel) {
return (typeof boardLabel === "undefined") ? [] : boardLabel
.match(/[0-9]*/gu)
.filter(function (x) {return (x.length !== 0);})
.map(function (x) {return parseInt(x);});
}
function setBoard(board) { function setBoard(board) {
const [nbRows, nbColumns] = getBoardDimensions(getSetting("BoardLabel")); for (let i = 0; i < boardDef.nbRows * boardDef.nbColumns; i++) {
for (let i = 0; i < nbRows * nbColumns; i++) {
setCell(i, board[i]); setCell(i, board[i]);
} }
} }
@ -795,7 +784,12 @@
case "boardLabel": case "boardLabel":
document.getElementById("board-label").value = value; document.getElementById("board-label").value = value;
setSetting("BoardLabel", value); setSetting("BoardLabel", value);
initBoard(value); [boardDef.nbRows, boardDef.nbColumns, boardDef.rackLength] =
(typeof value === "undefined") ? [0, 0, 0] : value
.match(/[0-9]*/gu)
.filter(function (x) {return (x.length !== 0);})
.map(function (x) {return parseInt(x);});
initBoard();
break; break;
} }
} }
@ -1238,8 +1232,13 @@
} }
if (data.boardLabel) { if (data.boardLabel) {
set("boardLabel", data.boardLabel); if (Object.keys(BoardList).indexOf(data.boardLabel) === -1) {
(boardLabelSelect || {}).value = data.boardLabel; myAlert("Can't find board '" + data.boardLabel + "'. Change board or start a new game.");
(boardLabelSelect || {}).value = "";
} else {
set("boardLabel", data.boardLabel);
(boardLabelSelect || {}).value = data.boardLabel;
}
} }
if (data.letterValues) { if (data.letterValues) {
@ -1531,6 +1530,7 @@
playerName: getSetting("PlayerName"), playerName: getSetting("PlayerName"),
boardLang: getSetting("BoardLang"), boardLang: getSetting("BoardLang"),
boardLabel: getSetting("BoardLabel"), boardLabel: getSetting("BoardLabel"),
bagFactor: BoardList[getSetting("BoardLabel")].factor,
version: VERSION, version: VERSION,
cmds: cmds cmds: cmds
}; };
@ -1680,7 +1680,7 @@
myConfirm( myConfirm(
format(_("Are you sure you want to change board to '{0}'? This will put all the tiles back in the bag and start another game."), _(lang)), format(_("Are you sure you want to change board to '{0}'? This will put all the tiles back in the bag and start another game."), _(lang)),
function () { function () {
sendCmds([{cmd: "changeBoard", lang: code, label: getSetting("BoardLabel")}]); sendCmds([{cmd: "changeBoard", lang: code, label: getSetting("BoardLabel"), factor: BoardList[getSetting("BoardLabel")].factor}]);
}, },
function () { function () {
boardLangSelect.value = getSetting("BoardLang"); boardLangSelect.value = getSetting("BoardLang");
@ -1694,7 +1694,7 @@
myConfirm( myConfirm(
format(_("Are you sure you want to change board to {0}? This will put all the tiles back in the bag and start another game."), label), format(_("Are you sure you want to change board to {0}? This will put all the tiles back in the bag and start another game."), label),
function () { function () {
sendCmds([{cmd: "changeBoard", lang: getSetting("BoardLang"), label: label}]); sendCmds([{cmd: "changeBoard", lang: getSetting("BoardLang"), label: label, factor: BoardList[label].factor}]);
}, },
function () { function () {
boardLabelSelect.value = getSetting("BoardLabel"); boardLabelSelect.value = getSetting("BoardLabel");
@ -1764,13 +1764,11 @@
} }
function getLetterFromBoard(row, col) { function getLetterFromBoard(row, col) {
const [nbRows, nbColumns] = getBoardDimensions(getSetting("BoardLabel")); if ((row < 0) || (row >= boardDef.nbRows) || (col < 0) || (col >= boardDef.nbColumns)) {
if ((row < 0) || (row >= nbRows) || (col < 0) || (col >= nbColumns)) {
return ""; return "";
} }
try { try {
const index = (row * nbColumns) + col; const index = (row * boardDef.nbColumns) + col;
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0]; const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
const tile = tilePlaceholder.getElementsByClassName("tile")[0]; const tile = tilePlaceholder.getElementsByClassName("tile")[0];
return tile.firstChild.textContent; return tile.firstChild.textContent;
@ -1816,14 +1814,12 @@
} }
function searchNewWords() { function searchNewWords() {
const nbColumns = getBoardDimensions(getSetting("BoardLabel"))[1];
const words = []; const words = [];
for (const i of Object.keys(currentTilePlayed)) { for (const i of Object.keys(currentTilePlayed)) {
/* Get board position */ /* Get board position */
const row = Math.floor(i / nbColumns); const row = Math.floor(i / boardDef.nbColumns);
const col = i % nbColumns; const col = i % boardDef.nbColumns;
/* Look for word in column */ /* Look for word in column */
const newWordInCol = searchWordInLine(row, col, 1, 0); const newWordInCol = searchWordInLine(row, col, 1, 0);
@ -1849,7 +1845,6 @@
} }
function modifyTileClass(word, value, action) { function modifyTileClass(word, value, action) {
const [nbRows, nbColumns] = getBoardDimensions(getSetting("BoardLabel"));
/* action: true for add, false for remove */ /* action: true for add, false for remove */
@ -1857,7 +1852,7 @@
/* only for new words */ /* only for new words */
for (let l = 0; l < word.letters.length; l++) { for (let l = 0; l < word.letters.length; l++) {
const index = ((word.row + (l * word.incRow)) * nbColumns) + word.col + (l * word.incCol); const index = ((word.row + (l * word.incRow)) * boardDef.nbColumns) + word.col + (l * word.incCol);
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0]; const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
const tile = tilePlaceholder.getElementsByClassName("tile")[0]; const tile = tilePlaceholder.getElementsByClassName("tile")[0];
@ -1867,7 +1862,7 @@
} else { } else {
/* tiles on board */ /* tiles on board */
for (let index = 0; index < nbRows * nbColumns; index++) { for (let index = 0; index < boardDef.nbRows * boardDef.nbColumns; index++) {
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0]; const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
const tile = tilePlaceholder.getElementsByClassName("tile")[0]; const tile = tilePlaceholder.getElementsByClassName("tile")[0];
if (typeof tile !== "undefined") { if (typeof tile !== "undefined") {
@ -1948,7 +1943,6 @@
} }
function scoreWords() { function scoreWords() {
const [nbColumns, rackLength] = getBoardDimensions(getSetting("BoardLabel")).slice(1);
let totalScore = 0; let totalScore = 0;
const newWords = searchNewWords(); const newWords = searchNewWords();
@ -1958,7 +1952,7 @@
const word = newWords[k]; const word = newWords[k];
for (let l = 0; l < word.letters.length; l++) { for (let l = 0; l < word.letters.length; l++) {
const index = ((word.row + (l * word.incRow)) * nbColumns) + word.col + (l * word.incCol); const index = ((word.row + (l * word.incRow)) * boardDef.nbColumns) + word.col + (l * word.incCol);
/* Letter score */ /* Letter score */
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0]; const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
@ -1997,7 +1991,7 @@
} }
/* Check for trivabble (the premium score) */ /* Check for trivabble (the premium score) */
if (Object.keys(currentTilePlayed).length === rackLength) { if (Object.keys(currentTilePlayed).length === boardDef.rackLength) {
totalScore += getSetting("PREMIUM_SEVEN_TILES"); totalScore += getSetting("PREMIUM_SEVEN_TILES");
} }
@ -2031,7 +2025,6 @@
return; return;
} }
const nbColumns = getBoardDimensions(getSetting("BoardLabel"))[1];
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const col = cell.cellIndex - 1; const col = cell.cellIndex - 1;
const row = getRowIndex(cell.parentNode) - 1; const row = getRowIndex(cell.parentNode) - 1;
@ -2041,7 +2034,7 @@
msg: _("Look at:") + " " + letters[row] + (cell.cellIndex + 1), msg: _("Look at:") + " " + letters[row] + (cell.cellIndex + 1),
specialMsg: { specialMsg: {
type: "highlightCell", type: "highlightCell",
cell: (row * nbColumns) + col cell: (row * boardDef.nbColumns) + col
} }
}]); }]);
} }
@ -2539,35 +2532,26 @@
startGame(getSetting("GameNumber")); startGame(getSetting("GameNumber"));
} }
function initBoard(boardLabel) { function initBoard() {
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const def = BoardList[getSetting("BoardLabel")];
if (Object.keys(BoardList).indexOf(boardLabel) === -1) {
myAlert("Can't find board '" + boardLabel + "'. Change board or start a new game.");
return;
}
const boardDef = BoardList[boardLabel];
const [nbRows, nbColumns, rackLength] = getBoardDimensions(boardLabel);
let cell; let cell;
let row; let row;
board.innerHTML = "<tr><td class=\"corner\"></td></tr>"; board.innerHTML = "<tr><td class=\"corner\"></td></tr>";
for (let j = 0; j < nbColumns; j++) { for (let j = 0; j < boardDef.nbColumns; j++) {
board.rows[0].appendChild(document.createElement("th")); board.rows[0].appendChild(document.createElement("th"));
board.rows[0].lastChild.textContent = j + 1; board.rows[0].lastChild.textContent = j + 1;
} }
for (let i = 0; i < nbRows; i++) { for (let i = 0; i < boardDef.nbRows; i++) {
row = board.insertRow(-1); row = board.insertRow(-1);
row.appendChild(document.createElement("th")); row.appendChild(document.createElement("th"));
row.lastChild.textContent = letters[i]; row.lastChild.textContent = letters[i];
for (let j = 0; j < nbColumns; j++) { for (let j = 0; j < boardDef.nbColumns; j++) {
cell = document.createElement("td"); cell = document.createElement("td");
boardCells.push(cell); boardCells.push(cell);
row.appendChild(cell); row.appendChild(cell);
@ -2576,18 +2560,18 @@
cell.lastChild.className = "tile-placeholder"; cell.lastChild.className = "tile-placeholder";
const cellName = letters[i] + (j + 1); const cellName = letters[i] + (j + 1);
if (boardDef.CS.indexOf(cellName) !== -1) { if (def.CS.indexOf(cellName) !== -1) {
specialCell("doubleWord", row.lastChild); specialCell("doubleWord", row.lastChild);
cell = row.lastChild.getElementsByClassName("special-cell-label")[0]; cell = row.lastChild.getElementsByClassName("special-cell-label")[0];
cell.textContent = "★"; cell.textContent = "★";
row.lastChild.id = "center-cell"; row.lastChild.id = "center-cell";
} else if (boardDef.TW.indexOf(cellName) !== -1) { } else if (def.TW.indexOf(cellName) !== -1) {
specialCell("tripleWord", row.lastChild); specialCell("tripleWord", row.lastChild);
} else if (boardDef.DW.indexOf(cellName) !== -1) { } else if (def.DW.indexOf(cellName) !== -1) {
specialCell("doubleWord", row.lastChild); specialCell("doubleWord", row.lastChild);
} else if (boardDef.TL.indexOf(cellName) !== -1) { } else if (def.TL.indexOf(cellName) !== -1) {
specialCell("tripleLetter", row.lastChild); specialCell("tripleLetter", row.lastChild);
} else if (boardDef.DL.indexOf(cellName) !== -1) { } else if (def.DL.indexOf(cellName) !== -1) {
specialCell("doubleLetter", row.lastChild); specialCell("doubleLetter", row.lastChild);
} }
} }
@ -2601,7 +2585,7 @@
row = board.insertRow(-1); row = board.insertRow(-1);
row.appendChild(board.rows[0].cells[0].cloneNode(false)); row.appendChild(board.rows[0].cells[0].cloneNode(false));
for (let i = 0; i < nbColumns; i++) { for (let i = 0; i < boardDef.nbColumns; i++) {
row.appendChild(document.createElement("th")); row.appendChild(document.createElement("th"));
row.lastChild.textContent = i + 1; row.lastChild.textContent = i + 1;
} }
@ -2611,7 +2595,7 @@
rack.innerHTML = ""; rack.innerHTML = "";
playerLetters.length = 0; playerLetters.length = 0;
for (let i = 0; i < rackLength; i++) { for (let i = 0; i < boardDef.rackLength; i++) {
const span = document.createElement("span"); const span = document.createElement("span");
span.className = "tile-placeholder"; span.className = "tile-placeholder";
rack.appendChild(span); rack.appendChild(span);

View File

@ -36,6 +36,7 @@ const KEEP_ALIVE = 30000;
const GAMES_BACKUP = process.env.TRIVABBLE_GAMES_BACKUP || "games.backup.json"; const GAMES_BACKUP = process.env.TRIVABBLE_GAMES_BACKUP || "games.backup.json";
const DEFAULT_BOARD_LANG = process.env.TRIVABBLE_DEFAULT_BOARD_LANG || "fr"; const DEFAULT_BOARD_LANG = process.env.TRIVABBLE_DEFAULT_BOARD_LANG || "fr";
const DEFAULT_BOARD_LABEL = process.env.TRIVABBLE_DEFAULT_BOARD_LABEL || "15x15-7"; const DEFAULT_BOARD_LABEL = process.env.TRIVABBLE_DEFAULT_BOARD_LABEL || "15x15-7";
const DEFAULT_BAG_FACTOR = process.env.TRIVABBLE_DEFAULT_BAG_FACTOR || 1;
const VERSION = 202005070100; const VERSION = 202005070100;
@ -226,17 +227,28 @@ function newBoard(label) {
return res; return res;
} }
Game.prototype.init = function (lang, label) { Game.prototype.init = function (lang, label, factor) {
this.label = label || DEFAULT_BOARD_LABEL; this.label = label || DEFAULT_BOARD_LABEL;
this.factor = factor || DEFAULT_BAG_FACTOR;
this.board = newBoard(this.label); this.board = newBoard(this.label);
this.lang = lang || DEFAULT_BOARD_LANG; this.lang = lang || DEFAULT_BOARD_LANG;
this.bag = boardTilesPerLang[this.lang].bag.slice(); const bag = boardTilesPerLang[this.lang].bag.slice();
this.letterValues = boardTilesPerLang[this.lang].letterValues; this.letterValues = boardTilesPerLang[this.lang].letterValues;
this.racks = {}; this.racks = {};
this.scores = {}; this.scores = {};
this.lastUpdated = new Date(); this.lastUpdated = new Date();
this.currentPlayer = ""; this.currentPlayer = "";
factor = this.factor;
this.bag = [];
while (factor > 1) {
this.bag = this.bag.concat(bag.slice());
factor--;
}
if (Math.round(bag.length * factor) > 0) {
shuffleInPlace(bag);
this.bag = this.bag.concat(bag.slice(0, Math.round(bag.length * factor)));
}
shuffleInPlace(this.bag); shuffleInPlace(this.bag);
}; };
@ -245,6 +257,7 @@ Game.prototype.toJSON = function () {
board: this.board, board: this.board,
lang: this.lang, lang: this.lang,
label: this.label, label: this.label,
factor: this.factor,
bag: this.bag, bag: this.bag,
letterValues: this.letterValues, letterValues: this.letterValues,
racks: this.racks, racks: this.racks,
@ -257,6 +270,7 @@ Game.prototype.toJSON = function () {
Game.fromJSON = function (obj) { Game.fromJSON = function (obj) {
const game = new Game(); const game = new Game();
game.label = obj.label || DEFAULT_BOARD_LABEL; game.label = obj.label || DEFAULT_BOARD_LABEL;
game.factor = obj.factor || DEFAULT_BAG_FACTOR;
game.board = obj.board || newBoard(game.label); game.board = obj.board || newBoard(game.label);
game.lang = obj.lang || DEFAULT_BOARD_LANG; game.lang = obj.lang || DEFAULT_BOARD_LANG;
game.bag = boardTilesPerLang[game.lang].bag.slice(); game.bag = boardTilesPerLang[game.lang].bag.slice();
@ -415,12 +429,13 @@ Game.prototype.bagPushLetter = function (letter, player) {
}; };
Game.prototype.reset = function (player) { Game.prototype.reset = function (player) {
this.init(this.lang, this.label); this.init(this.lang, this.label, this.factor);
this.pendingEvents.push({ this.pendingEvents.push({
player: player, player: player,
action: "reset", action: "reset",
board: this.board, board: this.board,
label: this.label, label: this.label,
factor: this.factor,
remainingLetters: this.bag.length, remainingLetters: this.bag.length,
rack: [] rack: []
}); });
@ -499,6 +514,7 @@ function handleCommand(cmdNumber, message, response) {
playerName: playerName, playerName: playerName,
boardLang: game.lang, boardLang: game.lang,
boardLabel: game.label, boardLabel: game.label,
bagFactor: game.factor,
availableBoardLangs: availableBoardLangs, availableBoardLangs: availableBoardLangs,
currentPlayer: game.currentPlayer, currentPlayer: game.currentPlayer,
rack: game.getPlayerRack(playerName), rack: game.getPlayerRack(playerName),
@ -512,7 +528,7 @@ function handleCommand(cmdNumber, message, response) {
case "hello": { case "hello": {
game.playerJoined(playerName); game.playerJoined(playerName);
reply(message, response, cmdNumber, {error: 0, boardLang: game.lang, boardLabel: game.label, version: VERSION}); reply(message, response, cmdNumber, {error: 0, boardLang: game.lang, boardLabel: game.label, bagFactor: game.factor, version: VERSION});
break; break;
} }
@ -692,12 +708,14 @@ function handleCommand(cmdNumber, message, response) {
case "changeBoard": { case "changeBoard": {
game.lang = cmd.lang || DEFAULT_BOARD_LANG; game.lang = cmd.lang || DEFAULT_BOARD_LANG;
game.label = cmd.label || DEFAULT_BOARD_LABEL; game.label = cmd.label || DEFAULT_BOARD_LABEL;
game.factor = cmd.factor || DEFAULT_BAG_FACTOR;
game.reset(); game.reset();
reply(message, response, cmdNumber, { reply(message, response, cmdNumber, {
error: 0, error: 0,
boardLang: game.lang, boardLang: game.lang,
boardLabel: game.label, boardLabel: game.label,
bagFactor: game.factor,
letterValues: game.letterValues letterValues: game.letterValues
}); });
@ -708,7 +726,8 @@ function handleCommand(cmdNumber, message, response) {
specialMsg: { specialMsg: {
type: "changeBoardDef", type: "changeBoardDef",
newBoardLang: game.lang, newBoardLang: game.lang,
newBoardLabel: game.label newBoardLabel: game.label,
newBagFactor: game.factor
} }
} }
}); });
@ -750,6 +769,7 @@ function handleCommands(message, responseAndType) {
gameNumber: gameNumber, gameNumber: gameNumber,
boardLang: game.lang, boardLang: game.lang,
boardLabel: game.label, boardLabel: game.label,
bagFactor: game.factor,
availableBoardLangs: availableBoardLangs, availableBoardLangs: availableBoardLangs,
letterValues: game.letterValues, letterValues: game.letterValues,
rack: game.getPlayerRack(message.playerName), rack: game.getPlayerRack(message.playerName),