From bfd0267d6e170cd5d83a8af86b184672e2eaecd5 Mon Sep 17 00:00:00 2001 From: Laurent Mazet Date: Mon, 26 Apr 2021 08:44:59 +0200 Subject: [PATCH] implement bag factor --- public/trivabble.js | 100 ++++++++++++++++--------------------- server/trivabble-server.js | 30 +++++++++-- 2 files changed, 67 insertions(+), 63 deletions(-) diff --git a/public/trivabble.js b/public/trivabble.js index 64589e8..d333b69 100644 --- a/public/trivabble.js +++ b/public/trivabble.js @@ -187,6 +187,7 @@ let boardLangSelect; let boardLabelSelect; const downloadedDictionaries = {}; + const boardDef = {nbRows: 0, nbColumns: 0, rackLength: 0}; const playerLetters = []; let currentPlayer = ""; @@ -239,15 +240,13 @@ } function setRack(rack) { - const rackLength = getBoardDimensions(getSetting("BoardLabel"))[2]; - for (let i = 0; i < rackLength; i++) { + for (let i = 0; i < boardDef.rackLength; i++) { setTileParent(playerLetters[i], rack[i] || ""); } } function getFreeRackSpaceIndex() { - const rackLength = getBoardDimensions(getSetting("BoardLabel"))[2]; - for (let i = 0; i < rackLength; i++) { + for (let i = 0; i < boardDef.rackLength; i++) { if (!playerLetters[i].getElementsByClassName("tile")[0]) { return i; } @@ -456,8 +455,7 @@ } } } else if (tileInitDest.getElementsByClassName("tile")[0]) { - const rackLength = getBoardDimensions(getSetting("BoardLabel"))[2]; - for (let i = 0; i < rackLength; i++) { + for (let i = 0; i < boardDef.rackLength; i++) { if (!playerLetters[i].getElementsByClassName("tile")[0]) { playerLetters[i].appendChild(movingTile); break; @@ -762,17 +760,8 @@ 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) { - const [nbRows, nbColumns] = getBoardDimensions(getSetting("BoardLabel")); - - for (let i = 0; i < nbRows * nbColumns; i++) { + for (let i = 0; i < boardDef.nbRows * boardDef.nbColumns; i++) { setCell(i, board[i]); } } @@ -795,7 +784,12 @@ case "boardLabel": document.getElementById("board-label").value = 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; } } @@ -1238,8 +1232,13 @@ } if (data.boardLabel) { - set("boardLabel", data.boardLabel); - (boardLabelSelect || {}).value = data.boardLabel; + if (Object.keys(BoardList).indexOf(data.boardLabel) === -1) { + 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) { @@ -1531,6 +1530,7 @@ playerName: getSetting("PlayerName"), boardLang: getSetting("BoardLang"), boardLabel: getSetting("BoardLabel"), + bagFactor: BoardList[getSetting("BoardLabel")].factor, version: VERSION, cmds: cmds }; @@ -1680,7 +1680,7 @@ 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)), function () { - sendCmds([{cmd: "changeBoard", lang: code, label: getSetting("BoardLabel")}]); + sendCmds([{cmd: "changeBoard", lang: code, label: getSetting("BoardLabel"), factor: BoardList[getSetting("BoardLabel")].factor}]); }, function () { boardLangSelect.value = getSetting("BoardLang"); @@ -1694,7 +1694,7 @@ 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), function () { - sendCmds([{cmd: "changeBoard", lang: getSetting("BoardLang"), label: label}]); + sendCmds([{cmd: "changeBoard", lang: getSetting("BoardLang"), label: label, factor: BoardList[label].factor}]); }, function () { boardLabelSelect.value = getSetting("BoardLabel"); @@ -1764,13 +1764,11 @@ } function getLetterFromBoard(row, col) { - const [nbRows, nbColumns] = getBoardDimensions(getSetting("BoardLabel")); - - if ((row < 0) || (row >= nbRows) || (col < 0) || (col >= nbColumns)) { + if ((row < 0) || (row >= boardDef.nbRows) || (col < 0) || (col >= boardDef.nbColumns)) { return ""; } try { - const index = (row * nbColumns) + col; + const index = (row * boardDef.nbColumns) + col; const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0]; const tile = tilePlaceholder.getElementsByClassName("tile")[0]; return tile.firstChild.textContent; @@ -1816,14 +1814,12 @@ } function searchNewWords() { - const nbColumns = getBoardDimensions(getSetting("BoardLabel"))[1]; - const words = []; for (const i of Object.keys(currentTilePlayed)) { /* Get board position */ - const row = Math.floor(i / nbColumns); - const col = i % nbColumns; + const row = Math.floor(i / boardDef.nbColumns); + const col = i % boardDef.nbColumns; /* Look for word in column */ const newWordInCol = searchWordInLine(row, col, 1, 0); @@ -1849,7 +1845,6 @@ } function modifyTileClass(word, value, action) { - const [nbRows, nbColumns] = getBoardDimensions(getSetting("BoardLabel")); /* action: true for add, false for remove */ @@ -1857,7 +1852,7 @@ /* only for new words */ 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 tile = tilePlaceholder.getElementsByClassName("tile")[0]; @@ -1867,7 +1862,7 @@ } else { /* 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 tile = tilePlaceholder.getElementsByClassName("tile")[0]; if (typeof tile !== "undefined") { @@ -1948,7 +1943,6 @@ } function scoreWords() { - const [nbColumns, rackLength] = getBoardDimensions(getSetting("BoardLabel")).slice(1); let totalScore = 0; const newWords = searchNewWords(); @@ -1958,7 +1952,7 @@ const word = newWords[k]; 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 */ const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0]; @@ -1997,7 +1991,7 @@ } /* Check for trivabble (the premium score) */ - if (Object.keys(currentTilePlayed).length === rackLength) { + if (Object.keys(currentTilePlayed).length === boardDef.rackLength) { totalScore += getSetting("PREMIUM_SEVEN_TILES"); } @@ -2031,7 +2025,6 @@ return; } - const nbColumns = getBoardDimensions(getSetting("BoardLabel"))[1]; const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const col = cell.cellIndex - 1; const row = getRowIndex(cell.parentNode) - 1; @@ -2041,7 +2034,7 @@ msg: _("Look at:") + " " + letters[row] + (cell.cellIndex + 1), specialMsg: { type: "highlightCell", - cell: (row * nbColumns) + col + cell: (row * boardDef.nbColumns) + col } }]); } @@ -2539,35 +2532,26 @@ startGame(getSetting("GameNumber")); } - function initBoard(boardLabel) { - + function initBoard() { const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - - 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); + const def = BoardList[getSetting("BoardLabel")]; let cell; let row; board.innerHTML = ""; - 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].lastChild.textContent = j + 1; } - for (let i = 0; i < nbRows; i++) { + for (let i = 0; i < boardDef.nbRows; i++) { row = board.insertRow(-1); row.appendChild(document.createElement("th")); row.lastChild.textContent = letters[i]; - for (let j = 0; j < nbColumns; j++) { + for (let j = 0; j < boardDef.nbColumns; j++) { cell = document.createElement("td"); boardCells.push(cell); row.appendChild(cell); @@ -2576,18 +2560,18 @@ cell.lastChild.className = "tile-placeholder"; const cellName = letters[i] + (j + 1); - if (boardDef.CS.indexOf(cellName) !== -1) { + if (def.CS.indexOf(cellName) !== -1) { specialCell("doubleWord", row.lastChild); cell = row.lastChild.getElementsByClassName("special-cell-label")[0]; cell.textContent = "★"; row.lastChild.id = "center-cell"; - } else if (boardDef.TW.indexOf(cellName) !== -1) { + } else if (def.TW.indexOf(cellName) !== -1) { specialCell("tripleWord", row.lastChild); - } else if (boardDef.DW.indexOf(cellName) !== -1) { + } else if (def.DW.indexOf(cellName) !== -1) { specialCell("doubleWord", row.lastChild); - } else if (boardDef.TL.indexOf(cellName) !== -1) { + } else if (def.TL.indexOf(cellName) !== -1) { specialCell("tripleLetter", row.lastChild); - } else if (boardDef.DL.indexOf(cellName) !== -1) { + } else if (def.DL.indexOf(cellName) !== -1) { specialCell("doubleLetter", row.lastChild); } } @@ -2601,7 +2585,7 @@ row = board.insertRow(-1); 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.lastChild.textContent = i + 1; } @@ -2611,7 +2595,7 @@ rack.innerHTML = ""; playerLetters.length = 0; - for (let i = 0; i < rackLength; i++) { + for (let i = 0; i < boardDef.rackLength; i++) { const span = document.createElement("span"); span.className = "tile-placeholder"; rack.appendChild(span); diff --git a/server/trivabble-server.js b/server/trivabble-server.js index 61fb576..a07fafe 100644 --- a/server/trivabble-server.js +++ b/server/trivabble-server.js @@ -36,6 +36,7 @@ const KEEP_ALIVE = 30000; 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_LABEL = process.env.TRIVABBLE_DEFAULT_BOARD_LABEL || "15x15-7"; +const DEFAULT_BAG_FACTOR = process.env.TRIVABBLE_DEFAULT_BAG_FACTOR || 1; const VERSION = 202005070100; @@ -226,17 +227,28 @@ function newBoard(label) { return res; } -Game.prototype.init = function (lang, label) { +Game.prototype.init = function (lang, label, factor) { this.label = label || DEFAULT_BOARD_LABEL; + this.factor = factor || DEFAULT_BAG_FACTOR; this.board = newBoard(this.label); 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.racks = {}; this.scores = {}; this.lastUpdated = new Date(); 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); }; @@ -245,6 +257,7 @@ Game.prototype.toJSON = function () { board: this.board, lang: this.lang, label: this.label, + factor: this.factor, bag: this.bag, letterValues: this.letterValues, racks: this.racks, @@ -257,6 +270,7 @@ Game.prototype.toJSON = function () { Game.fromJSON = function (obj) { const game = new Game(); game.label = obj.label || DEFAULT_BOARD_LABEL; + game.factor = obj.factor || DEFAULT_BAG_FACTOR; game.board = obj.board || newBoard(game.label); game.lang = obj.lang || DEFAULT_BOARD_LANG; game.bag = boardTilesPerLang[game.lang].bag.slice(); @@ -415,12 +429,13 @@ Game.prototype.bagPushLetter = function (letter, player) { }; Game.prototype.reset = function (player) { - this.init(this.lang, this.label); + this.init(this.lang, this.label, this.factor); this.pendingEvents.push({ player: player, action: "reset", board: this.board, label: this.label, + factor: this.factor, remainingLetters: this.bag.length, rack: [] }); @@ -499,6 +514,7 @@ function handleCommand(cmdNumber, message, response) { playerName: playerName, boardLang: game.lang, boardLabel: game.label, + bagFactor: game.factor, availableBoardLangs: availableBoardLangs, currentPlayer: game.currentPlayer, rack: game.getPlayerRack(playerName), @@ -512,7 +528,7 @@ function handleCommand(cmdNumber, message, response) { case "hello": { 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; } @@ -692,12 +708,14 @@ function handleCommand(cmdNumber, message, response) { case "changeBoard": { game.lang = cmd.lang || DEFAULT_BOARD_LANG; game.label = cmd.label || DEFAULT_BOARD_LABEL; + game.factor = cmd.factor || DEFAULT_BAG_FACTOR; game.reset(); reply(message, response, cmdNumber, { error: 0, boardLang: game.lang, boardLabel: game.label, + bagFactor: game.factor, letterValues: game.letterValues }); @@ -708,7 +726,8 @@ function handleCommand(cmdNumber, message, response) { specialMsg: { type: "changeBoardDef", newBoardLang: game.lang, - newBoardLabel: game.label + newBoardLabel: game.label, + newBagFactor: game.factor } } }); @@ -750,6 +769,7 @@ function handleCommands(message, responseAndType) { gameNumber: gameNumber, boardLang: game.lang, boardLabel: game.label, + bagFactor: game.factor, availableBoardLangs: availableBoardLangs, letterValues: game.letterValues, rack: game.getPlayerRack(message.playerName),