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:"
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"
msgstr "Paramètres"
@ -231,3 +237,6 @@ msgstr "Désactiver la vérification orthographique"
msgid "Spell checking is not available for this language."
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:"
msgstr ""
msgid "{0} points will be added to:"
msgstr ""
msgid "There are no points to add."
msgstr: ""
msgid "Settings"
msgstr ""
@ -236,3 +242,6 @@ msgstr ""
msgid "Spell checking is not available for this language."
msgstr ""
msgid "Score new words"
msgstr ""

View File

@ -40,8 +40,8 @@
margin-top:1em;
}
.alert-content {
padding:1ex
.alert-content-and-input {
padding:1ex;
}
.alert input[type=text], .alert input[type=number], .alert input[type=password] {
@ -81,3 +81,18 @@
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 divAlertCallbackNo;
let alertInput;
let alertSelect;
let divAlertContent;
const _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {
@ -85,14 +86,26 @@
divAlertInput.appendChild(alertInput);
divAlertInput.appendChild(document.createElement("div"));
divAlertInput.lastChild.className = "alert-prompt-buttons";
divAlertInput.lastChild.appendChild(document.createElement("button"));
divAlertInput.lastChild.lastChild.textContent = _("OK");
divAlertInput.lastChild.lastChild.onclick = promptOK;
divAlertInput.lastChild.appendChild(document.createElement("button"));
divAlertInput.lastChild.lastChild.textContent = _("Annuler");
divAlertInput.lastChild.lastChild.onclick = promptCancel;
alertSelect = document.createElement("select");
alertSelect.onchange = function () {
alertInput.value = alertSelect.value;
};
divAlertInput.appendChild(alertSelect);
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.className = _("alert-confirm");
@ -111,8 +124,8 @@
const divAlertOuter = document.createElement("div");
divAlertOuter.className = "alert-outer";
divAlertOuter.appendChild(divAlertContent);
divAlertOuter.appendChild(divAlertInput);
divAlertOuter.appendChild(divAlertContentAndInput);
divAlertOuter.appendChild(divAlerPromptButton);
divAlertOuter.appendChild(divAlertConfirm);
divAlertOuter.appendChild(divAlertButton);
divAlert.appendChild(divAlertOuter);
@ -131,6 +144,8 @@
}
divAlert.classList.remove("prompt");
divAlert.classList.remove("choice");
divAlert.classList.remove("choice-inline");
divAlertContent.textContent = msg;
divAlertInput.style.display = "none";
@ -149,11 +164,15 @@
}
divAlert.classList.add("prompt");
divAlert.classList.remove("choice");
divAlert.classList.remove("choice-inline");
divAlertContent.textContent = msg;
divAlertInput.style.display = "";
alertInput.style.display = "";
alertInput.value = defaultText || "";
alertInput.type = (options && options.type) || "text";
alertSelect.style.display = "none";
divAlertConfirm.style.display = "none";
divAlertButton.style.display = "none";
divAlertCallback = callback;
@ -170,6 +189,8 @@
}
divAlert.classList.remove("prompt");
divAlert.classList.remove("choice");
divAlert.classList.remove("choice-inline");
divAlertContent.textContent = msg;
divAlertInput.style.display = "none";
@ -180,4 +201,35 @@
divAlert.style.display = "";
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));

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.
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 :-)
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="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="score-words" class="minibutton" data-l10n="text-content">Score new words</button>
</div>
<div id="help-box">
<p id="help-box-title" data-l10n="text-content">Tip!</p>

View File

@ -22,7 +22,7 @@
* @source: https://gitlab.com/raphj/trivabble/
*/
/*global libD, myConfirm, myAlert, myPrompt*/
/*global libD, myConfirm, myAlert, myPrompt, myChoice*/
(function () {
"use strict";
@ -60,6 +60,8 @@
setConf("FLASH_LIGHT_DURATION", middle("FLASH_LIGHT_DURATIONS"));
setConf("FLASH_LIGHT_COLOR", "#ee6633");
setConf("API_ENTRY_POINT", Conf.APP_PATH + "/:trivabble");
setConf("PREMIUM_SEVEN_TILES", 50);
setConf("SCORE_LAST_PLAYER", true);
function getSetting(key) {
let type;
@ -193,6 +195,8 @@
let currentMessageId = 1;
const waitingMsgs = [];
let lastPlayer = null;
let serverVersion = 0;
function mouseDown(ele, fun, stop) {
@ -720,6 +724,14 @@
}
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);
}
@ -911,11 +923,14 @@
tablePlayers[currentPlayer].classList.remove("current-player");
}
currentTilePlayed = {};
currentPlayer = player;
refreshCurrentPlayer();
}
function getScoreCell(playerName) {
return tablePlayers[playerName].childNodes[2].childNodes[0];
}
function setPlayers(players) {
if (participantPlaceholder) {
participantPlaceholder.parentNode.removeChild(participantPlaceholder);
@ -1027,7 +1042,10 @@
}
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;
blink(scoreCell);
}
@ -1042,6 +1060,15 @@
refreshCurrentPlayer();
}
function handleLastPlayer(data) {
if (data.player) {
if (lastPlayer !== data.player) {
currentTilePlayed = {};
}
lastPlayer = data.player;
}
}
function applyAction(data) {
switch (data.action) {
case "pushBag": //TODO
@ -1056,9 +1083,14 @@
participants.removeChild(participants.rows[1]);
}
sendCmds([{cmd: "hello"}]);
lastPlayer = null;
currentTilePlayed = {};
break;
case "moveLetter":
handleLastPlayer(data);
if (data.from === "board") {
setCell(data.indexFrom, "");
} else if (data.from === "rack") {
@ -1066,17 +1098,32 @@
}
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") {
setRackCell(data.indexTo, data.letter);
}
break;
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] === "-")) {
currentTilePlayed[data.indexTo] = data.letter;
}
break;
case "setRackCell":
setRackCell(data.indexTo, data.letter);
}
@ -1824,6 +1871,84 @@
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) {
if (!cell) {
return;
@ -2192,6 +2317,7 @@
document.getElementById("btn-settings-close").onclick = hideSettings;
document.getElementById("next-help-msg").onclick = nextHelpMessage;
document.getElementById("disable-spell-checker").onclick = toggleSpellChecker;
document.getElementById("score-words").onclick = scoreWords;
window.addEventListener("keydown", function (e) {
if (e.key === "Escape") {
document.querySelector(".modal").classList.remove("show-modal");