Merge branch 'automatic_scoring' into 'develop'

Automatic scoring

See merge request raphj/trivabble!13
This commit is contained in:
Raphaël Jakse 2020-12-06 16:31:39 +00:00
commit 0bd24691d3
7 changed files with 234 additions and 17 deletions

View File

@ -196,6 +196,12 @@ msgstr "La vérification orthographique nécessite que Trivabble télécharge un
msgid "Spell checking is based on:" msgid "Spell checking is based on:"
msgstr "La vérification orthographique est basée sur :" msgstr "La vérification orthographique est basée sur :"
msgid "{0} points will be added to:"
msgstr "{0} points vont être ajoutés à:"
msgid "There are no points to add."
msgstr "Il ny a pas de point à ajouter."
msgid "Settings" msgid "Settings"
msgstr "Paramètres" msgstr "Paramètres"
@ -231,3 +237,6 @@ msgstr "Désactiver la vérification orthographique"
msgid "Spell checking is not available for this language." msgid "Spell checking is not available for this language."
msgstr "La vérification orthographique n'est pas disponible dans cette langue." msgstr "La vérification orthographique n'est pas disponible dans cette langue."
msgid "Score new words"
msgstr "Compter les points"

View File

@ -196,6 +196,12 @@ msgstr ""
msgid "Spell checking is based on:" msgid "Spell checking is based on:"
msgstr "" msgstr ""
msgid "{0} points will be added to:"
msgstr ""
msgid "There are no points to add."
msgstr: ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
@ -236,3 +242,6 @@ msgstr ""
msgid "Spell checking is not available for this language." msgid "Spell checking is not available for this language."
msgstr "" msgstr ""
msgid "Score new words"
msgstr ""

View File

@ -40,8 +40,8 @@
margin-top:1em; margin-top:1em;
} }
.alert-content { .alert-content-and-input {
padding:1ex padding:1ex;
} }
.alert input[type=text], .alert input[type=number], .alert input[type=password] { .alert input[type=text], .alert input[type=number], .alert input[type=password] {
@ -81,3 +81,18 @@
border-radius:3px border-radius:3px
} }
.alert-prompt-buttons {
display: none;
}
.alert.choice .alert-prompt-buttons, .alert.prompt .alert-prompt-buttons {
display: block;
}
.alert.choice.choice-inline .alert-content, .choice.choice-inline .alert-prompt {
display:inline-block;
}
.alert.choice.choice-inline .alert-prompt {
padding-left: 1ex;
}

View File

@ -12,6 +12,7 @@
let divAlertCallbackYes; let divAlertCallbackYes;
let divAlertCallbackNo; let divAlertCallbackNo;
let alertInput; let alertInput;
let alertSelect;
let divAlertContent; let divAlertContent;
const _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) { const _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {
@ -85,14 +86,26 @@
divAlertInput.appendChild(alertInput); divAlertInput.appendChild(alertInput);
divAlertInput.appendChild(document.createElement("div")); alertSelect = document.createElement("select");
divAlertInput.lastChild.className = "alert-prompt-buttons"; alertSelect.onchange = function () {
divAlertInput.lastChild.appendChild(document.createElement("button")); alertInput.value = alertSelect.value;
divAlertInput.lastChild.lastChild.textContent = _("OK"); };
divAlertInput.lastChild.lastChild.onclick = promptOK;
divAlertInput.lastChild.appendChild(document.createElement("button")); divAlertInput.appendChild(alertSelect);
divAlertInput.lastChild.lastChild.textContent = _("Annuler");
divAlertInput.lastChild.lastChild.onclick = promptCancel; const divAlertContentAndInput = document.createElement("div");
divAlertContentAndInput.className = "alert-content-and-input";
divAlertContentAndInput.appendChild(divAlertContent);
divAlertContentAndInput.appendChild(divAlertInput);
const divAlerPromptButton = document.createElement("div");
divAlerPromptButton.className = "alert-prompt-buttons";
divAlerPromptButton.appendChild(document.createElement("button"));
divAlerPromptButton.lastChild.textContent = _("OK");
divAlerPromptButton.lastChild.onclick = promptOK;
divAlerPromptButton.appendChild(document.createElement("button"));
divAlerPromptButton.lastChild.textContent = _("Cancel");
divAlerPromptButton.lastChild.onclick = promptCancel;
divAlertConfirm = document.createElement("div"); divAlertConfirm = document.createElement("div");
divAlertConfirm.className = _("alert-confirm"); divAlertConfirm.className = _("alert-confirm");
@ -111,8 +124,8 @@
const divAlertOuter = document.createElement("div"); const divAlertOuter = document.createElement("div");
divAlertOuter.className = "alert-outer"; divAlertOuter.className = "alert-outer";
divAlertOuter.appendChild(divAlertContent); divAlertOuter.appendChild(divAlertContentAndInput);
divAlertOuter.appendChild(divAlertInput); divAlertOuter.appendChild(divAlerPromptButton);
divAlertOuter.appendChild(divAlertConfirm); divAlertOuter.appendChild(divAlertConfirm);
divAlertOuter.appendChild(divAlertButton); divAlertOuter.appendChild(divAlertButton);
divAlert.appendChild(divAlertOuter); divAlert.appendChild(divAlertOuter);
@ -131,6 +144,8 @@
} }
divAlert.classList.remove("prompt"); divAlert.classList.remove("prompt");
divAlert.classList.remove("choice");
divAlert.classList.remove("choice-inline");
divAlertContent.textContent = msg; divAlertContent.textContent = msg;
divAlertInput.style.display = "none"; divAlertInput.style.display = "none";
@ -149,11 +164,15 @@
} }
divAlert.classList.add("prompt"); divAlert.classList.add("prompt");
divAlert.classList.remove("choice");
divAlert.classList.remove("choice-inline");
divAlertContent.textContent = msg; divAlertContent.textContent = msg;
divAlertInput.style.display = ""; divAlertInput.style.display = "";
alertInput.style.display = "";
alertInput.value = defaultText || ""; alertInput.value = defaultText || "";
alertInput.type = (options && options.type) || "text"; alertInput.type = (options && options.type) || "text";
alertSelect.style.display = "none";
divAlertConfirm.style.display = "none"; divAlertConfirm.style.display = "none";
divAlertButton.style.display = "none"; divAlertButton.style.display = "none";
divAlertCallback = callback; divAlertCallback = callback;
@ -170,6 +189,8 @@
} }
divAlert.classList.remove("prompt"); divAlert.classList.remove("prompt");
divAlert.classList.remove("choice");
divAlert.classList.remove("choice-inline");
divAlertContent.textContent = msg; divAlertContent.textContent = msg;
divAlertInput.style.display = "none"; divAlertInput.style.display = "none";
@ -180,4 +201,35 @@
divAlert.style.display = ""; divAlert.style.display = "";
divAlertConfirm.getElementsByTagName("button")[0].focus(); divAlertConfirm.getElementsByTagName("button")[0].focus();
}; };
global.myChoice = function (msg, options, callback, defaultValue) {
if (!divAlert) {
prepare();
}
divAlert.classList.remove("prompt");
divAlert.classList.add("choice");
if (options.dispositionInline) {
divAlert.classList.add("choice-inline");
} else {
divAlert.classList.remove("choice-inline");
}
alertSelect.options.length = 0;
for (const key of options.choices) {
[].push.call(alertSelect.options, new Option(key, key));
}
alertSelect.value = alertInput.value = defaultValue;
divAlertContent.textContent = msg;
divAlertInput.style.display = "";
alertInput.style.display = "none";
alertSelect.style.display = "";
divAlertConfirm.style.display = "none";
divAlertButton.style.display = "none";
divAlertCallback = callback;
divAlert.style.display = "";
divAlertConfirm.getElementsByTagName("button")[0].focus();
};
}(this)); }(this));

View File

@ -35,6 +35,11 @@ window.TrivabbleConf = {
// The defaut double tap duration. If not set, the value at the middle of the previous array is used. // The defaut double tap duration. If not set, the value at the middle of the previous array is used.
DOUBLE_TAP_DURATION: 1800, DOUBLE_TAP_DURATION: 1800,
// The default premium for playing seven tiles on a turn
PREMIUM_SEVEN_TILES: 50,
// Score is automically affected to last player. If false, score is automatically affected to the player who pressed the Score button
SCORE_LAST_PLAYER: true,
// I don't like trailing commas, here is a nice message for you reading this file :-) // I don't like trailing commas, here is a nice message for you reading this file :-)
HAVE_FUN: true HAVE_FUN: true

View File

@ -124,6 +124,7 @@
<button id="clear-rack" class="minibutton" data-l10n="text-content">Put back all the tiles of&#10;your rack in the bag</button> <button id="clear-rack" class="minibutton" data-l10n="text-content">Put back all the tiles of&#10;your rack in the bag</button>
<button id="show-rack" class="minibutton" data-l10n="text-content">Show my rack to other players</button> <button id="show-rack" class="minibutton" data-l10n="text-content">Show my rack to other players</button>
<button id="check-spelling" class="minibutton" data-l10n="text-content" hidden="true">Check spelling for new words</button> <button id="check-spelling" class="minibutton" data-l10n="text-content" hidden="true">Check spelling for new words</button>
<button id="score-words" class="minibutton" data-l10n="text-content">Score new words</button>
</div> </div>
<div id="help-box"> <div id="help-box">
<p id="help-box-title" data-l10n="text-content">Tip!</p> <p id="help-box-title" data-l10n="text-content">Tip!</p>

View File

@ -22,7 +22,7 @@
* @source: https://gitlab.com/raphj/trivabble/ * @source: https://gitlab.com/raphj/trivabble/
*/ */
/*global libD, myConfirm, myAlert, myPrompt*/ /*global libD, myConfirm, myAlert, myPrompt, myChoice*/
(function () { (function () {
"use strict"; "use strict";
@ -60,6 +60,8 @@
setConf("FLASH_LIGHT_DURATION", middle("FLASH_LIGHT_DURATIONS")); setConf("FLASH_LIGHT_DURATION", middle("FLASH_LIGHT_DURATIONS"));
setConf("FLASH_LIGHT_COLOR", "#ee6633"); setConf("FLASH_LIGHT_COLOR", "#ee6633");
setConf("API_ENTRY_POINT", Conf.APP_PATH + "/:trivabble"); setConf("API_ENTRY_POINT", Conf.APP_PATH + "/:trivabble");
setConf("PREMIUM_SEVEN_TILES", 50);
setConf("SCORE_LAST_PLAYER", true);
function getSetting(key) { function getSetting(key) {
let type; let type;
@ -193,6 +195,8 @@
let currentMessageId = 1; let currentMessageId = 1;
const waitingMsgs = []; const waitingMsgs = [];
let lastPlayer = null;
let serverVersion = 0; let serverVersion = 0;
function mouseDown(ele, fun, stop) { function mouseDown(ele, fun, stop) {
@ -720,6 +724,14 @@
} }
function setCell(index, letter, highlight) { function setCell(index, letter, highlight) {
if (lastPlayer) {
if (letter) {
currentTilePlayed[index] = letter;
} else {
delete currentTilePlayed[index];
}
}
setTileParent(boardCells[index].getElementsByClassName("tile-placeholder")[0], letter, highlight); setTileParent(boardCells[index].getElementsByClassName("tile-placeholder")[0], letter, highlight);
} }
@ -911,11 +923,14 @@
tablePlayers[currentPlayer].classList.remove("current-player"); tablePlayers[currentPlayer].classList.remove("current-player");
} }
currentTilePlayed = {};
currentPlayer = player; currentPlayer = player;
refreshCurrentPlayer(); refreshCurrentPlayer();
} }
function getScoreCell(playerName) {
return tablePlayers[playerName].childNodes[2].childNodes[0];
}
function setPlayers(players) { function setPlayers(players) {
if (participantPlaceholder) { if (participantPlaceholder) {
participantPlaceholder.parentNode.removeChild(participantPlaceholder); participantPlaceholder.parentNode.removeChild(participantPlaceholder);
@ -1027,7 +1042,10 @@
} }
if (Object.prototype.hasOwnProperty.call(player, "score")) { if (Object.prototype.hasOwnProperty.call(player, "score")) {
const scoreCell = tablePlayers[playerName].childNodes[2].childNodes[0]; const scoreCell = getScoreCell(playerName);
if (parseInt(scoreCell.textContent) !== player.score) {
currentTilePlayed = {};
}
scoreCell.textContent = player.score; scoreCell.textContent = player.score;
blink(scoreCell); blink(scoreCell);
} }
@ -1042,6 +1060,15 @@
refreshCurrentPlayer(); refreshCurrentPlayer();
} }
function handleLastPlayer(data) {
if (data.player) {
if (lastPlayer !== data.player) {
currentTilePlayed = {};
}
lastPlayer = data.player;
}
}
function applyAction(data) { function applyAction(data) {
switch (data.action) { switch (data.action) {
case "pushBag": //TODO case "pushBag": //TODO
@ -1056,9 +1083,14 @@
participants.removeChild(participants.rows[1]); participants.removeChild(participants.rows[1]);
} }
sendCmds([{cmd: "hello"}]); sendCmds([{cmd: "hello"}]);
lastPlayer = null;
currentTilePlayed = {};
break; break;
case "moveLetter": case "moveLetter":
handleLastPlayer(data);
if (data.from === "board") { if (data.from === "board") {
setCell(data.indexFrom, ""); setCell(data.indexFrom, "");
} else if (data.from === "rack") { } else if (data.from === "rack") {
@ -1066,17 +1098,32 @@
} }
if (data.to === "board") { if (data.to === "board") {
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== getSetting("PlayerName")); setCell(
data.indexTo,
data.letter,
data.player && data.player !== getSetting("PlayerName")
);
} else if (data.to === "rack") { } else if (data.to === "rack") {
setRackCell(data.indexTo, data.letter); setRackCell(data.indexTo, data.letter);
} }
break; break;
case "setCell": case "setCell":
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== getSetting("PlayerName")); handleLastPlayer(data);
setCell(
data.indexTo,
data.letter,
data.player && data.player !== getSetting("PlayerName")
);
if ((data.letter !== "") && (currentTilePlayed[data.indexTo] === "-")) { if ((data.letter !== "") && (currentTilePlayed[data.indexTo] === "-")) {
currentTilePlayed[data.indexTo] = data.letter; currentTilePlayed[data.indexTo] = data.letter;
} }
break; break;
case "setRackCell": case "setRackCell":
setRackCell(data.indexTo, data.letter); setRackCell(data.indexTo, data.letter);
} }
@ -1824,6 +1871,84 @@
return [].indexOf.call(tr.parentNode.rows, tr); return [].indexOf.call(tr.parentNode.rows, tr);
} }
function scoreWords() {
let totalScore = 0;
const newWords = searchNewWords();
for (const k of Object.keys(newWords)) {
let wordFactor = 1;
let wordScore = 0;
const word = newWords[k];
for (let l = 0; l < word.letters.length; l++) {
const index = ((word.row + (l * word.incRow)) * 15) + word.col + (l * word.incCol);
/* Letter score */
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
const tile = tilePlaceholder.getElementsByClassName("tile")[0];
const letterScore = tile.lastChild.textContent;
let letterFactor = 1;
/* Is a freshly played letter? */
if (currentTilePlayed[index] === word.letters[l]) {
/* Letter factor */
if (boardCells[index].classList.contains("special-cell-doubleLetter")) {
letterFactor = 2;
} else if (boardCells[index].classList.contains("special-cell-tripleLetter")) {
letterFactor = 3;
}
/* Word factor */
if (boardCells[index].classList.contains("special-cell-doubleWord")) {
wordFactor *= 2;
} else if (boardCells[index].classList.contains("special-cell-tripleWord")) {
wordFactor *= 3;
}
}
wordScore += letterScore * letterFactor;
}
totalScore += wordScore * wordFactor;
}
if (!totalScore) {
myAlert(_("There are no points to add."));
return;
}
/* Check for trivabble (the premium score) */
if (Object.keys(currentTilePlayed).length === 7) {
totalScore += getSetting("PREMIUM_SEVEN_TILES");
}
/* Ѕcore last player or the one who pressed the button */
const playerName = getSetting("SCORE_LAST_PLAYER") ? lastPlayer : getSetting("PlayerName");
myChoice(
format(_("{0} points will be added to:"), totalScore), {
choices: Object.keys(tablePlayers),
dispositionInline: true
},
function (name) {
if (!name) {
return;
}
currentTilePlayed = {};
sendCmds([{
cmd: "score",
player: name,
score: parseInt(getScoreCell(name).textContent) + totalScore
}]);
},
playerName || lastPlayer || currentPlayer || getSetting("PlayerName")
);
}
function triggerFlashLight(cell) { function triggerFlashLight(cell) {
if (!cell) { if (!cell) {
return; return;
@ -2192,6 +2317,7 @@
document.getElementById("btn-settings-close").onclick = hideSettings; document.getElementById("btn-settings-close").onclick = hideSettings;
document.getElementById("next-help-msg").onclick = nextHelpMessage; document.getElementById("next-help-msg").onclick = nextHelpMessage;
document.getElementById("disable-spell-checker").onclick = toggleSpellChecker; document.getElementById("disable-spell-checker").onclick = toggleSpellChecker;
document.getElementById("score-words").onclick = scoreWords;
window.addEventListener("keydown", function (e) { window.addEventListener("keydown", function (e) {
if (e.key === "Escape") { if (e.key === "Escape") {
document.querySelector(".modal").classList.remove("show-modal"); document.querySelector(".modal").classList.remove("show-modal");