New option to check if first word cover central star (#13)
authorLaurent Mazet <mazet@softndesign.org>
Fri, 7 Mar 2025 10:07:52 +0000 (11:07 +0100)
committerRaphaël Jakse <raph@jakse.fr>
Fri, 7 Mar 2025 10:07:52 +0000 (11:07 +0100)
Reviewed-on: https://git.jakse.fr/trivabble/trivabble/pulls/13
Co-authored-by: Laurent Mazet <mazet@softndesign.org>
Co-committed-by: Laurent Mazet <mazet@softndesign.org>
bin/check-l10n-file.sh [new file with mode: 0755]
l10n/po/fr/trivabble.po
public/index.html
public/trivabble.js
server/trivabble-server.js

diff --git a/bin/check-l10n-file.sh b/bin/check-l10n-file.sh
new file mode 100755 (executable)
index 0000000..8b91565
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+PODIR="l10n/po/fr"
+
+PUBDIR="public"
+
+[ "$1" ] && PODIR=$1
+[ "$2" ] && PUBDIRF=$2
+
+IFS=$'\n'
+
+function extract_from_html() {
+    awk -F '>' \
+        '/l10n/ {
+            for (i=1; i<NF; i++) {
+                if ($(i) ~ /l10n/) {
+                    sub(/<.*/, "", $(i+1))
+                    if ($(i+1)) {
+                        printf ("\"%s\"\n", $(i+1))
+                    }
+                }
+            }
+        }' $@
+}
+
+function extract_from_js() {
+    awk '/_\("/ {
+            sub(/.*_\(/, "")
+            sub (/").*/, "\"")
+            print
+        }' $@
+}
+
+for l in $(extract_from_html $PUBDIR/*.html); do
+    grep -q "$l" $PODIR/*.po || echo "$l"
+done
+
+for l in $(extract_from_js $PUBDIR/*.js); do
+    grep -q "$l" $PODIR/*.po || echo "$l"
+done
index 1a5e8d1c7d69a59d79bf24aa5aaccefbcd70e874..acf756e635e819974dd172e68cd4743d8c66543a 100644 (file)
@@ -297,3 +297,9 @@ msgstr "Quitter cette partie"
 
 msgid  "You are about to leave this game. Your letters will be put back in bag. Are you sure?"
 msgstr "Vous allez quitter la partie. Vos lettres seront remises dans le sac. Êtes-vous sur.e ?"
+
+msgid  "Enable first word verification"
+msgstr "Activer la vérification du premier mot"
+
+msgid  "First word must cover center box."
+msgstr "Le premier mot doit couvrir la case centrale."
index 51ace2f5d0c90cec20fbe5608d879513e5c33a0a..0659b753b7684f1235363ba305d52935ab22e118 100644 (file)
@@ -70,6 +70,9 @@
                     <p><label><input type="checkbox" id="tiles-sound" /><span data-l10n="text-content">Sound of the tiles</span></label></p>
                     <p><label><input type="checkbox" id="msg-sound" /><span data-l10n="text-content">Sound of messages</span></label></p>
                 </div>
+                <div>
+                    <p><label><input type="checkbox" id="check-first-word" /> <span data-l10n="text-content">Enable first word verification</span></label></p>
+                </div>
                 <div>
                     <p id="disable-spell-checker-p" hidden="true">
                         <label>
index 2cdd6ad36dd9bd05482f24bb864ec02222637642..ac455f903f8c1225a0c9804db0abc1e8871ca429 100644 (file)
         BoardLabel: "string",
         Lang: "string",
         PlayerName: "string",
+        CheckFirstWord: "number",
         SpellCheckerEnabledOnce: "boolean",
         TimerGameDate: "number",
         TimerTurnDate: "number"
                         .map(function (x) {return parseInt(x);});
                 initBoard();
                 break;
+            case "checkFirstWord":
+                setSetting("CheckFirstWord", value);
+                break;
         }
     }
 
                 row.lastChild.lastChild.onclick = (
                     function (row) {
                         return function () {
+                            if (getSetting("CheckFirstWord") === 1) {
+                                const centerCell = document.getElementById("center-cell");
+                                if (!centerCell.getElementsByClassName("tile")[0]) {
+                                    myAlert(_("First word must cover center box."));
+                                    return;
+                                }
+                            }
+
                             if (!pollingReady) {
                                 showConnectionLost();
                                 return;
                 row.lastChild.lastChild.onclick = (
                     function (row) {
                         return function () {
+                            if (getSetting("CheckFirstWord") === 1) {
+                                const centerCell = document.getElementById("center-cell");
+                                if (!centerCell.getElementsByClassName("tile")[0]) {
+                                    myAlert(_("First word must cover center box."));
+                                    return;
+                                }
+                            }
+
                             if (!pollingReady) {
                                 showConnectionLost();
                                 return;
             setCurrentPlayer(data.currentPlayer);
         }
 
+        if (data.checkFirstWord) {
+            set("checkFirstWord", data.checkFirstWord);
+            document.getElementById("check-first-word").checked = (getSetting("CheckFirstWord") === 1);
+        }
+
         if (data.players) {
             setPlayers(data.players);
         }
             boardLang: getSetting("BoardLang"),
             boardLabel: getSetting("BoardLabel"),
             bagFactor: (typeof getSetting("BoardLabel") === "undefined") ? 1 : BoardList[getSetting("BoardLabel")].factor,
+            checkFirstWord: getSetting("CheckFirstWord"),
             version: VERSION,
             cmds: cmds
         };
     function scoreWords() {
         let totalScore = 0;
 
+        if (getSetting("CheckFirstWord") === 1) {
+            const centerCell = document.getElementById("center-cell");
+            if (!centerCell.getElementsByClassName("tile")[0]) {
+                myAlert(_("First word must cover center box."));
+                return;
+            }
+        }
+
         const newWords = searchNewWords();
         for (const k of Object.keys(newWords)) {
             let wordFactor = 1;
         }
     }
 
+    function initCheckFirstWord() {
+        document.getElementById("check-first-word").checked = (getSetting("CheckFirstWord") === 1);
+        document.getElementById("check-first-word").onclick = function () {
+            const checkFirstWord = (document.getElementById("check-first-word").checked) ? 1 : -1;
+            set("checkFirstCheck", checkFirstWord);
+            sendCmds([{cmd: "checkFirstWord", checkFirstWord: checkFirstWord}]);
+        };
+    }
+
     function toggleSpellChecker(e) {
         const disabled = document.getElementById("disable-spell-checker").checked;
         checkDictionaryExistance();
         initChat();
         initGame();
         initSound();
+        initCheckFirstWord();
         initSpellChecker();
         initFlashLight();
         nextHelpMessage();
index 0f90f91d74e83edfbaaffa08611695bf2341e4e1..c745e1658fc2728d6ce78a44d47375e97302f1fa 100644 (file)
@@ -36,6 +36,7 @@ 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 = 1;
+const CHECK_FIRST_WORD = 1; // 1: check, 0: not set, -1: no check
 
 const VERSION = 202005070100;
 
@@ -240,7 +241,7 @@ function newBoard(label) {
     return res;
 }
 
-Game.prototype.init = function (lang, label, factor) {
+Game.prototype.init = function (lang, label, factor, checkFirstWord) {
     this.label = label || DEFAULT_BOARD_LABEL;
     this.factor = factor || DEFAULT_BAG_FACTOR;
     this.board = newBoard(this.label);
@@ -249,6 +250,7 @@ Game.prototype.init = function (lang, label, factor) {
     this.letterValues = boardTilesPerLang[this.lang].letterValues;
     this.racks = {};
     this.scores = {};
+    this.checkFirstWord = checkFirstWord || CHECK_FIRST_WORD;
     this.lastUpdated = new Date();
     this.currentPlayer = "";
 
@@ -265,6 +267,7 @@ Game.prototype.toJSON = function () {
         letterValues:     this.letterValues,
         racks:            this.racks,
         scores:           this.scores,
+        checkFirstWord:   this.checkFirstWord,
         lastUpdated:      this.lastUpdated.toISOString(),
         currentPlayer:    this.currentPlayer
     };
@@ -280,6 +283,7 @@ Game.fromJSON = function (obj) {
     game.letterValues     = boardTilesPerLang[game.lang].letterValues;
     game.racks            = obj.racks  || {};
     game.scores           = obj.scores || {};
+    game.checkFirstWord   = obj.checkFirstWord || CHECK_FIRST_WORD;
     game.lastUpdated      = obj.lastUpdated ? new Date(obj.lastUpdated) : new Date();
     game.currentPlayer    = obj.currentPlayer || "";
     return game;
@@ -317,6 +321,11 @@ Game.prototype.setCurrentPlayer = function (player) {
     this.pendingEvents.push({currentPlayer: player});
 };
 
+Game.prototype.setCheckFirstWord = function (onOff) {
+    this.checkFirstWord = onOff;
+    this.pendingEvents.push({checkFirstWord: onOff});
+};
+
 Game.prototype.playerJoined = function (playerName) {
     if (playerName) {
         this.getPlayerRack(playerName); // Create the player's rack
@@ -451,7 +460,7 @@ Game.prototype.bagPushLetter = function (letter, player) {
 };
 
 Game.prototype.reset = function (player) {
-    this.init(this.lang, this.label, this.factor);
+    this.init(this.lang, this.label, this.factor, this.checkFirstWord);
     this.pendingEvents.push({
         player: player,
         action: "reset",
@@ -550,6 +559,7 @@ function handleCommand(cmdNumber, message, response) {
                 availableBoardLangs: availableBoardLangs,
                 currentPlayer: game.currentPlayer,
                 rack: game.getPlayerRack(playerName),
+                checkFirstWord: game.checkFirstWord,
                 board: game.board,
                 remainingLetters: game.bag.length,
                 letterletterValues: game.letterValues,
@@ -570,6 +580,12 @@ function handleCommand(cmdNumber, message, response) {
             break;
         }
 
+        case "checkFirstWord": {
+            game.setCheckFirstWord(cmd.checkFirstWord);
+            reply(message, response, cmdNumber, {error: 0});
+            break;
+        }
+
         case "currentPlayer": {
             game.setCurrentPlayer(cmd.player);
             reply(message, response, cmdNumber, {error: 0});
@@ -747,6 +763,7 @@ function handleCommand(cmdNumber, message, response) {
             game.lang = cmd.lang || DEFAULT_BOARD_LANG;
             game.label = cmd.label || DEFAULT_BOARD_LABEL;
             game.factor = cmd.factor || DEFAULT_BAG_FACTOR;
+            game.checkFirstWord = cmd.checkFirstWord || CHECK_FIRST_WORD;
             game.reset();
 
             reply(message, response, cmdNumber, {
@@ -754,6 +771,7 @@ function handleCommand(cmdNumber, message, response) {
                 boardLang: game.lang,
                 boardLabel: game.label,
                 bagFactor: game.factor,
+                checkFirstWord: game.checkFirstWord,
                 letterValues: game.letterValues
             });
 
@@ -813,6 +831,7 @@ function handleCommands(message, responseAndType) {
                 rack:                game.getPlayerRack(message.playerName),
                 board:               game.board,
                 remainingLetters:    game.bag.length,
+                checkFirstWord:      game.checkFirstWord,
                 version:             VERSION
             })
         );