diff --git a/Motus/CMakeLists.txt b/Motus/CMakeLists.txt index 6ca28d7ef294acd343fe987956a6e0c56dc0d975..ad0ea9c1dc0089b75c83a938fc03fb709581e0df 100644 --- a/Motus/CMakeLists.txt +++ b/Motus/CMakeLists.txt @@ -49,6 +49,7 @@ qt_add_qml_module(appMotus QML_FILES NumberLetterButton.qml RESOURCES qtquickcontrols2.conf RESOURCES mots_francais_bis.txt + RESOURCES motus_stats.txt ) # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. diff --git a/Motus/Main.qml b/Motus/Main.qml index fce82e3dd147f779e46a2df1cd014034053618c3..33dc0ab0bea279c6250aefffe58dd118049ecd61 100644 --- a/Motus/Main.qml +++ b/Motus/Main.qml @@ -1,8 +1,7 @@ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 -// Optionnel : utilisez un style non natif pour autoriser la personnalisation -// import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 Window { id: mainWindow @@ -26,6 +25,12 @@ Window { property var keysArray: [] + // Propriété pour afficher/masquer l'overlay de statistiques + property bool statsOverlayVisible: false + // Propriété pour choisir le nombre de lettres pour afficher les stats locales dans l'overlay + property int selectedLetterForStats: nb_lettres + + // Fonction utilitaire pour changer la couleur d'une touche function changeKeyColor(letter, newColor) { for (var i = 0; i < keysArray.length; i++) { if (keysArray[i].keyLetter === letter) { @@ -34,17 +39,191 @@ Window { } } - // Rectangle principal qui couvre la fenêtre + // Fonction pour rafraîchir l'affichage de l'overlay de statistiques + function refreshStatsDisplay() { + // Ici, vous pouvez actualiser les Text avec les vraies statistiques via motusGame + // Par exemple, si vous exposez une propriété "globalGames", faites : + // globalGamesText.text = "Parties jouées : " + motusGame.globalGames; + // Pour cet exemple, les valeurs restent "0". + } + + // Fonction de mise à jour des statistiques à la fin d'une partie + function updateGameStats() { + var language = (motusGame.dictionnaryChoosed.indexOf("francais") >= 0) ? "French" : "English"; + var letterCount = nb_lettres; + var won = motusGame.win; + var attemptsUsed = won ? current_essai + 1 : 0; + var gameTime = won ? (motusGame.duree_timer - motusGame.remainingTime) : 0; + motusGame.updateStats(letterCount, language, won, attemptsUsed, gameTime); + motusGame.saveStats(); // Sauvegarde immédiatement après fin de partie + refreshStatsDisplay(); + } + + // Overlay des statistiques + Rectangle { + id: statsOverlay + anchors.fill: parent + color: "#323232AA" // Fond identique à la fenêtre principale avec transparence + visible: statsOverlayVisible + z: 100 + onVisibleChanged: { + if (!visible) { + // Redonner le focus à la zone de jeu lorsqu'on ferme l'overlay + mainRect.forceActiveFocus(); + } else { + refreshStatsDisplay(); + } + } + + // Dialogue centré + Rectangle { + id: statsDialog + width: parent.width * 0.6 + height: parent.height * 0.7 + color: "#323232" + opacity: 0.95 + radius: 10 + anchors.centerIn: parent + + ColumnLayout { + anchors.fill: parent + anchors.margins: 20 + spacing: 20 + + // En-tête avec titre et bouton de fermeture en haut à droite + RowLayout { + width: parent.width + Layout.alignment: Qt.AlignTop + Text { + text: "Statistiques" + font.pixelSize: 32 + color: "white" + font.bold: true + Layout.fillWidth: true + } + Button { + text: "X" + font.pixelSize: 24 + background: Rectangle { + color: "transparent" + border.color: "white" + radius: 5 + } + onClicked: { + statsOverlayVisible = false; + mainRect.forceActiveFocus(); + } + } + } + + // Statistiques Globales (exemple, à remplacer par vos appels réels) + GroupBox { + title: "Statistiques Globales" + Layout.fillWidth: true + contentItem: ColumnLayout { + spacing: 5 + Text { text: "Parties jouées : " + /*motusGame.getGlobalStats("globalGames")*/ "0"; color: "white"; font.pixelSize: 20 } + Text { text: "Victoires : " + /*motusGame.getGlobalStats("globalWins")*/ "0"; color: "white"; font.pixelSize: 20 } + Text { text: "Win Streak Actuel : " + /*motusGame.getGlobalStats("globalCurrentStreak")*/ "0"; color: "white"; font.pixelSize: 20 } + Text { text: "Meilleur Streak : " + /*motusGame.getGlobalStats("globalBestStreak")*/ "0"; color: "white"; font.pixelSize: 20 } + } + } + + // Sélecteur pour le nombre de lettres pour les statistiques locales + RowLayout { + spacing: 10 + Layout.fillWidth: true + Text { + text: "Stats locales pour" + font.pixelSize: 20 + color: "white" + } + ComboBox { + id: letterSelector + model: [4,5,6,7,8,9,10,11,12] + currentIndex: nb_lettres - 4 + onCurrentIndexChanged: { + selectedLetterForStats = model[currentIndex]; + } + font.pixelSize: 20 + } + Text { + text: "lettres" + font.pixelSize: 20 + color: "white" + } + } + + // Statistiques Locales (exemple, à remplacer par vos appels réels) + GroupBox { + title: "Statistiques Locales (" + selectedLetterForStats + " lettres)" + Layout.fillWidth: true + contentItem: ColumnLayout { + spacing: 5 + Text { text: "Temps moyen : " + /*motusGame.getLocalStats(selectedLetterForStats, language).averageTime*/ "0" + " sec"; color: "white"; font.pixelSize: 20 } + Text { text: "Meilleur temps : " + /*motusGame.getLocalStats(selectedLetterForStats, language).bestTime*/ "0" + " sec"; color: "white"; font.pixelSize: 20 } + Text { text: "Parties jouées : " + /*motusGame.getLocalStats(selectedLetterForStats, language).gamesPlayed*/ "0"; color: "white"; font.pixelSize: 20 } + Text { text: "Parties gagnées : " + /*motusGame.getLocalStats(selectedLetterForStats, language).gamesWon*/ "0"; color: "white"; font.pixelSize: 20 } + Text { text: "Win Streak Actuel : " + /*motusGame.getLocalStats(selectedLetterForStats, language).currentWinStreak*/ "0"; color: "white"; font.pixelSize: 20 } + Text { text: "Meilleur Streak : " + /*motusGame.getLocalStats(selectedLetterForStats, language).bestWinStreak*/ "0"; color: "white"; font.pixelSize: 20 } + } + } + + // Histogramme des victoires par numéro d'essai + GroupBox { + title: "Histogramme des victoires" + Layout.fillWidth: true + contentItem: RowLayout { + id: histogramRow + spacing: 10 + // Exemple d'histogramme : on utilise un tableau dummy "data" à remplacer par vos données réelles. + property var data: [0, 0, 0, 0, 0, 0] + property int maxValue: { + var maxVal = 0; + for (var i = 0; i < data.length; i++) { + if (data[i] > maxVal) + maxVal = data[i]; + } + return maxVal; + } + Repeater { + model: data.length + delegate: ColumnLayout { + spacing: 2 + // Item vide pour aligner les barres en bas + Item { Layout.fillHeight: true } + Rectangle { + width: 20 + // Hauteur proportionnelle, minimum 10 px + height: maxValue > 0 ? Math.max(10, 200 * (data[index] / maxValue)) : 10 + color: index === (data.length - 1) ? "#28a745" : "#4a4a4a" + border.width: 1 + border.color: "white" + } + Text { + text: "Essai " + (index + 1) + "\n(" + data[index] + ")" + font.pixelSize: 14 + color: "white" + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + } + } + } + } + } + } + + // Zone de jeu principale Rectangle { id: mainRect anchors.fill: parent color: "#323232" focus: true - // Gestion des touches du clavier physique Keys.onPressed: function(event) { if (mot !== "" && !motusGame.loosetry && !motusGame.win && !motusGame.loosetime) { - // Saisie d'une lettre (de A à Z) if (/^[a-zA-Z]$/.test(event.text)) { if (indice_case < nb_lettres) { var cellIndex = current_essai * nb_lettres + indice_case; @@ -52,20 +231,15 @@ Window { indice_case++; event.accepted = true; } - } - // Gestion de la touche Backspace - else if (event.key === Qt.Key_Backspace) { + } else if (event.key === Qt.Key_Backspace) { if (indice_case > 0) { indice_case--; var cellIndex = current_essai * nb_lettres + indice_case; caseArray[cellIndex]._textText = ""; event.accepted = true; } - } - // Passage à la ligne suivante avec la touche Entrée - else if (event.key === Qt.Key_Return) { + } else if (event.key === Qt.Key_Return) { if (indice_case === nb_lettres) { - // Construire le mot entré en majuscules var candidate = []; for (var i = 0; i < nb_lettres; i++){ candidate[i] = caseArray[current_essai * nb_lettres + i]._textText.toUpperCase(); @@ -74,50 +248,37 @@ Window { console.log("Mot entré :", mot_entre); if (motusGame.existWord(mot_entre)) { - // Le mot existe, on procède à l'évaluation avec gestion des doublons var target = mot_split.map(function(letter) { return letter.toUpperCase(); }); - - // Construire un tableau de fréquence des lettres du mot cible var targetCounts = {}; for (var i = 0; i < nb_lettres; i++) { var letter = target[i]; targetCounts[letter] = (targetCounts[letter] || 0) + 1; } - - // Tableau pour stocker la couleur à appliquer pour chaque case. - // Par défaut, on ne change pas le style (chaîne vide) var resultColors = new Array(nb_lettres); - - // Première passe : marquer en vert les lettres bien placées for (var i = 0; i < nb_lettres; i++) { if (candidate[i] === target[i]) { - resultColors[i] = "#42cc3d"; // vert + resultColors[i] = "#42cc3d"; targetCounts[candidate[i]]--; } else { resultColors[i] = ""; } } - - // Vérifier si le mot entré correspond exactement au mot à deviner if (mot_entre.toUpperCase() === mot.toUpperCase()) { motusGame.setWinbool(true); console.log("✅ Victoire !"); + updateGameStats(); } - - // Deuxième passe : marquer en jaune les lettres présentes mais mal placées for (var i = 0; i < nb_lettres; i++) { if (resultColors[i] === "") { var letter = candidate[i]; if (targetCounts[letter] && targetCounts[letter] > 0) { - resultColors[i] = "#f0d437"; // jaune + resultColors[i] = "#f0d437"; targetCounts[letter]--; } } } - - // Appliquer les couleurs aux cases et aux touches si une couleur a été définie for (var i = 0; i < nb_lettres; i++) { var case_verif = caseArray[current_essai * nb_lettres + i]; if (resultColors[i] !== "") { @@ -126,22 +287,18 @@ Window { changeKeyColor(candidate[i], resultColors[i]); } } - - // Passage à la ligne suivante et mise à jour du focus if (current_essai < nb_essais - 1) { current_essai++; indice_case = 0; case_focus = caseArray[current_essai * nb_lettres]; } else { - // Fin de partie : désactiver l'interface ou afficher un message if (mot_entre.toUpperCase() !== mot.toUpperCase()) { motusGame.setLoosetrybool(true); console.log("loosetry=true"); + updateGameStats(); } - } } else { - // Le mot n'existe pas dans le dictionnaire : colorer toute la ligne en rouge for (var i = 0; i < nb_lettres; i++) { var case_verif = caseArray[current_essai * nb_lettres + i]; case_verif.rectangleColor = "#cf1b2a"; @@ -155,10 +312,9 @@ Window { } } - // Timer pour réinitialiser le style de la ligne en cas de mot inexistant Timer { id: revertTimer - interval: 1000 // 1 seconde + interval: 1000 repeat: false onTriggered: { console.log("Réinitialisation du style de la ligne"); @@ -175,426 +331,428 @@ Window { anchors.fill: parent spacing: 150 - Rectangle { - id: rectangle_buttons + Column { + id: leftColumn width: 400 height: 850 - color: "#323232" - border.width: 0 + spacing: 20 anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 0.05 * Window.width - Column { - id: column_buttons - width: 400 - height: 850 - spacing: 20 - - Choosebutton { - id: choosebutton - anchors.horizontalCenter: parent.horizontalCenter - _textText: "Generate a word" - Case { - id: case3 - x: 131 - y: 8 - _textText: "mot test" - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - enabled: motusGame && (motusGame.win || motusGame.loosetry || motusGame.loosetime || !motusGame.debut) - onClicked: { - - // 1. Réinitialiser les indices AVANT de tout - current_essai = 0; - indice_case = 0; - - // 2. Vider toutes les cases - for (var i = 0; i < caseArray.length; i++) { - caseArray[i]._textText = ""; - caseArray[i].rectangleColor = "#323232"; - caseArray[i].rectangleBorderColor = "#ffffff"; - } - - // 3. Vider les touches clavier - for (var j = 0; j < keysArray.length; j++) { - keysArray[j].keyColor = "#808080"; - } - - // Début d'une partie, désactivation des boutons de régénération, etc. - motusGame.debut=true; - mot = motusGame.getRandomWord(nb_lettres); - motusGame.setLoosetrybool(false); - motusGame.setLoosetimebool(false); - motusGame.setWinbool(false); - motusGame.startTimer(); - case3._textText = mot; - mot_split = mot.split(""); - console.log(mot_split); - - + Choosebutton { + id: choosebutton + anchors.horizontalCenter: parent.horizontalCenter + _textText: "Generate a word" + Case { + id: case3 + x: 131 + y: 8 + _textText: "mot test" + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + enabled: motusGame && (motusGame.win || motusGame.loosetry || motusGame.loosetime || !motusGame.debut) + onClicked: { + current_essai = 0; + indice_case = 0; + for (var i = 0; i < caseArray.length; i++) { + caseArray[i]._textText = ""; + caseArray[i].rectangleColor = "#323232"; + caseArray[i].rectangleBorderColor = "#ffffff"; + } + for (var j = 0; j < keysArray.length; j++) { + keysArray[j].keyColor = "#808080"; } + motusGame.debut = true; + mot = motusGame.getRandomWord(nb_lettres); + motusGame.setLoosetrybool(false); + motusGame.setLoosetimebool(false); + motusGame.setWinbool(false); + motusGame.startTimer(); + case3._textText = mot; + mot_split = mot.split(""); + console.log(mot_split); } } + } - Rectangle { - id: big_rectangle_langue - width: 250 - height: 300 - color: "#323232" - radius: 10 - border.color: "#ffffff" - border.width: 3 - anchors.horizontalCenter: parent.horizontalCenter + Rectangle { + id: big_rectangle_langue + width: 250 + height: 300 + color: "#323232" + radius: 10 + border.color: "#ffffff" + border.width: 3 + anchors.horizontalCenter: parent.horizontalCenter - Column { - id: column - anchors.fill: parent - anchors.margins: 10 - spacing: 15 - - Text { - id:text_langue - y: 100 - text: "Language" - font.pixelSize: 25 - color: "white" - horizontalAlignment: Text.AlignHCenter - anchors.horizontalCenter: parent.horizontalCenter - } + Column { + id: column + anchors.fill: parent + anchors.margins: 10 + spacing: 15 - LangageButton { - id: langageButton - anchors.horizontalCenter: parent.horizontalCenter - _textText: "Français" - rectangleColor: "#7a7a7a" - MouseArea { - enabled: motusGame && (motusGame.win || motusGame.loosetry || motusGame.loosetime || !motusGame.debut) - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - cursorShape: Qt.PointingHandCursor - onClicked: { - - motusGame.dictionnaryChoosed = "Motus\\mots_francais_bis.txt"; - langageButton2.rectangleColor= "#7a7a7a"; - langageButton.rectangleColor= "green"; - choosebutton._textText="Générer un mot"; - text_langue.text="Langage" - text_choix_nb_lettres.text="Nombre de lettres" - numberLetterBtn.firstItemText="Aléatoire" + Text { + id: text_langue + y: 100 + text: "Language" + font.pixelSize: 25 + color: "white" + horizontalAlignment: Text.AlignHCenter + anchors.horizontalCenter: parent.horizontalCenter + } - } + LangageButton { + id: langageButton + anchors.horizontalCenter: parent.horizontalCenter + _textText: "Français" + rectangleColor: "#7a7a7a" + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + motusGame.dictionnaryChoosed = "Motus\\mots_francais_bis.txt"; + langageButton2.rectangleColor = "#7a7a7a"; + langageButton.rectangleColor = "green"; + choosebutton._textText = "Générer un mot"; + text_langue.text = "Langage"; + text_choix_nb_lettres.text = "Nombre de lettres"; + numberLetterBtn.firstItemText = "Aléatoire"; } } + } - LangageButton { - id: langageButton2 - anchors.horizontalCenter: parent.horizontalCenter - _textText: "English" - rectangleColor: "green" - MouseArea { - enabled: motusGame && (motusGame.win || motusGame.loosetry || motusGame.loosetime || !motusGame.debut) - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - motusGame.dictionnaryChoosed = "Motus\\words_alpha.txt"; - langageButton2.rectangleColor= "green"; - langageButton.rectangleColor= "#7a7a7a"; - choosebutton._textText="Generate a word;" - text_langue.text="Language" - text_choix_nb_lettres.text="Number of letters" - numberLetterBtn.firstItemText="Random" - - } + LangageButton { + id: langageButton2 + anchors.horizontalCenter: parent.horizontalCenter + _textText: "English" + rectangleColor: "green" + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + motusGame.dictionnaryChoosed = "Motus\\words_alpha.txt"; + langageButton2.rectangleColor = "green"; + langageButton.rectangleColor = "#7a7a7a"; + choosebutton._textText = "Generate a word;"; + text_langue.text = "Language"; + text_choix_nb_lettres.text = "Number of letters"; + numberLetterBtn.firstItemText = "Random"; } } } } + } - Rectangle { - id: big_rectangle_nb_lettres - width: 250 - height: 300 - color: "#323232" - radius: 10 - border.color: "#ffffff" - border.width: 3 - anchors.horizontalCenter: parent.horizontalCenter + Rectangle { + id: big_rectangle_nb_lettres + width: 250 + height: 300 + color: "#323232" + radius: 10 + border.color: "#ffffff" + border.width: 3 + anchors.horizontalCenter: parent.horizontalCenter - Column { - id: colonne_nb_lettres - anchors.fill: parent - spacing: 50 + Column { + id: colonne_nb_lettres + anchors.fill: parent + spacing: 50 - Text { - id: text_choix_nb_lettres - text: "Number of letters" - font.pixelSize: 22 - anchors.horizontalCenter: parent.horizontalCenter - color: "white" - } - - NumberLetterButton { - id: numberLetterBtn - firstItemText: "Random" - width: parent.width - height: 60 + Text { + id: text_choix_nb_lettres + text: "Number of letters" + font.pixelSize: 22 + anchors.horizontalCenter: parent.horizontalCenter + color: "white" + } - } + NumberLetterButton { + id: numberLetterBtn + firstItemText: "Random" + width: parent.width + height: 60 } } } + + Button { + id: statsButton + text: "Statistiques" + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 20 + onClicked: { + updateGameStats(); // Met à jour et sauvegarde les stats à la fin de la partie + statsOverlayVisible = true; + selectedLetterForStats = nb_lettres; + } + } } Rectangle { - id: rectangle_center - width: 700 - height: 900 + id: rectangle_right + width: 400 + height: 850 color: "#323232" border.width: 0 anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter + anchors.right: parent.right + anchors.rightMargin: 0.05 * Window.width Column { - id: center_column - x: 0 - y: 36 - width: 716 - height: 864 + id: column1 + width: 326 + height: 850 spacing: 30 - Rectangle { - id: rectangle_titre - width: 200 - height: 70 - color: "#323232" - border.width: 0 + MotusTimer { + id: motusTimer + width: 100 + height: 100 anchors.horizontalCenter: parent.horizontalCenter - - Text { - id: _text2 - text: qsTr("MOTUS") - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: 50 - color: "white" - font.family: "Tahoma" - font.bold: true - horizontalAlignment: Text.AlignHCenter - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top + } + Button { + id: statsButtonRight + text: "Statistiques" + icon.color: "#ffffff" + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 20 + onClicked: { + updateGameStats(); + statsOverlayVisible = true; + selectedLetterForStats = nb_lettres; } } + } + } + } - Rectangle { - id: rectangle_grid - width: 450 - height: 500 - color: "#323232" - border.width: 0 + Rectangle { + id: rectangle_center + width: 700 + height: 900 + color: "#323232" + border.width: 0 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + Column { + id: center_column + x: 0 + y: 36 + width: 716 + height: 864 + spacing: 30 + + Rectangle { + id: rectangle_titre + width: 200 + height: 70 + color: "#323232" + border.width: 0 + anchors.horizontalCenter: parent.horizontalCenter + + Text { + id: _text2 + text: qsTr("MOTUS") anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 50 + color: "white" + font.family: "Tahoma" + font.bold: true + horizontalAlignment: Text.AlignHCenter anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + } + } - Grid { - id: grid - x: -553 - y: -430 - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 39 - anchors.rightMargin: -39 - anchors.topMargin: -80 - anchors.bottomMargin: 80 - spacing: 5 - rows: 6 - columns: 5 - - Repeater { - id: caseRepeater - model: 30 - delegate: Case { - objectName: "caseDelegate" - } + Rectangle { + id: rectangle_grid + width: 450 + height: 500 + color: "#323232" + border.width: 0 + anchors.verticalCenter: rectangle_titre.verticalCenter + anchors.left: rectangle_titre.right + anchors.right: rectangle_titre.left + anchors.top: rectangle_titre.bottom + anchors.bottom: rectangle_titre.top + anchors.leftMargin: -325 + anchors.rightMargin: -325 + anchors.topMargin: 112 + anchors.bottomMargin: -682 + anchors.horizontalCenter: rectangle_titre.horizontalCenter + + Grid { + id: grid + x: -553 + y: -430 + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.leftMargin: 39 + anchors.rightMargin: -39 + anchors.topMargin: -80 + anchors.bottomMargin: 80 + spacing: 5 + rows: 6 + columns: 5 + + Repeater { + id: caseRepeater + model: 30 + delegate: Case { + objectName: "caseDelegate" } } } + } - Rectangle { - id: rectangle1 - width: 600 - height: 230 - anchors.horizontalCenter: parent.horizontalCenter - color: "#323232" - radius: 10 - border.color: "#ffffff" - border.width: 5 - anchors.bottom: parent.bottom - anchors.bottomMargin: 50 - - Column { - anchors.fill: parent - anchors.margins: 10 - spacing: 6 - topPadding: rectangle1.height * 0.05 - bottomPadding: rectangle1.height * 0.05 - - Repeater { - model: [ - ["A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P"], - ["Q", "S", "D", "F", "G", "H", "J", "K", "L"], - ["↑", "W", "X", "C", "V", "B", "N", "M", "<"] - ] - delegate: Row { - spacing: 5 - anchors.horizontalCenter: parent.horizontalCenter - - Repeater { - model: modelData - delegate: Rectangle { - id: keyRect - property string keyLetter: modelData // Stocke la lettre associée - property color keyColor: "#808080" // Couleur par défaut - - width: (modelData === "Entrée" || modelData === "<") ? 80 : 50 - height: 60 - color: keyColor - radius: 5 - border.color: keyColor - - Text { - anchors.centerIn: parent - text: modelData - font.pixelSize: 30 - color: "white" - font.family: "Tahoma" - font.bold: true - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - if (mot !== "" && !motusGame.loosetry && !motusGame.win && !motusGame.loosetime) { - - // Gestion de la touche Backspace - if (modelData === "<") { - if (indice_case > 0) { - indice_case--; - var cellIndex = current_essai * nb_lettres + indice_case; - caseArray[cellIndex]._textText = ""; - event.accepted = true; - } + Rectangle { + id: rectangle1 + width: 600 + height: 230 + anchors.horizontalCenter: parent.horizontalCenter + color: "#323232" + radius: 10 + border.color: "#ffffff" + border.width: 5 + anchors.bottom: parent.bottom + anchors.bottomMargin: 50 + + Column { + anchors.fill: parent + anchors.margins: 10 + spacing: 6 + topPadding: rectangle1.height * 0.05 + bottomPadding: rectangle1.height * 0.05 + + Repeater { + model: [ + ["A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["Q", "S", "D", "F", "G", "H", "J", "K", "L"], + ["↑", "W", "X", "C", "V", "B", "N", "M", "<"] + ] + delegate: Row { + spacing: 5 + anchors.horizontalCenter: parent.horizontalCenter + + Repeater { + model: modelData + delegate: Rectangle { + id: keyRect + property string keyLetter: modelData + property color keyColor: "#808080" + + width: (modelData === "Entrée" || modelData === "<") ? 80 : 50 + height: 60 + color: keyColor + radius: 5 + border.color: keyColor + + Text { + anchors.centerIn: parent + text: modelData + font.pixelSize: 30 + color: "white" + font.family: "Tahoma" + font.bold: true + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (mot !== "" && !motusGame.loosetry && !motusGame.win && !motusGame.loosetime) { + if (modelData === "<") { + if (indice_case > 0) { + indice_case--; + var cellIndex = current_essai * nb_lettres + indice_case; + caseArray[cellIndex]._textText = ""; + event.accepted = true; } - // Passage à la ligne suivante avec la touche Entrée - else if (modelData === "↑") { - if (indice_case === nb_lettres) { - // Construire le mot entré en majuscules - var candidate = []; - for (var i = 0; i < nb_lettres; i++){ - candidate[i] = caseArray[current_essai * nb_lettres + i]._textText.toUpperCase(); + } else if (modelData === "↑") { + if (indice_case === nb_lettres) { + var candidate = []; + for (var i = 0; i < nb_lettres; i++){ + candidate[i] = caseArray[current_essai * nb_lettres + i]._textText.toUpperCase(); + } + var mot_entre = candidate.join(""); + console.log("Mot entré :", mot_entre); + if (motusGame.existWord(mot_entre)) { + var target = mot_split.map(function(letter) { + return letter.toUpperCase(); + }); + var targetCounts = {}; + for (var i = 0; i < nb_lettres; i++) { + var letter = target[i]; + targetCounts[letter] = (targetCounts[letter] || 0) + 1; } - var mot_entre = candidate.join(""); - console.log("Mot entré :", mot_entre); - - if (motusGame.existWord(mot_entre)) { - // Le mot existe, on procède à l'évaluation avec gestion des doublons - var target = mot_split.map(function(letter) { - return letter.toUpperCase(); - }); - - // Construire un tableau de fréquence des lettres du mot cible - var targetCounts = {}; - for (var i = 0; i < nb_lettres; i++) { - var letter = target[i]; - targetCounts[letter] = (targetCounts[letter] || 0) + 1; - } - - // Tableau pour stocker la couleur à appliquer pour chaque case. - // Par défaut, on ne change pas le style (chaîne vide) - var resultColors = new Array(nb_lettres); - - // Première passe : marquer en vert les lettres bien placées - for (var i = 0; i < nb_lettres; i++) { - if (candidate[i] === target[i]) { - resultColors[i] = "#42cc3d"; // vert - targetCounts[candidate[i]]--; - } else { - resultColors[i] = ""; - } - } - - // Vérifier si le mot entré correspond exactement au mot à deviner - if (mot_entre.toUpperCase() === mot.toUpperCase()) { - motusGame.setWinbool(true); - console.log("✅ Victoire !"); - } - - // Deuxième passe : marquer en jaune les lettres présentes mais mal placées - for (var i = 0; i < nb_lettres; i++) { - if (resultColors[i] === "") { - var letter = candidate[i]; - if (targetCounts[letter] && targetCounts[letter] > 0) { - resultColors[i] = "#f0d437"; // jaune - targetCounts[letter]--; - } - } + var resultColors = new Array(nb_lettres); + for (var i = 0; i < nb_lettres; i++) { + if (candidate[i] === target[i]) { + resultColors[i] = "#42cc3d"; + targetCounts[candidate[i]]--; + } else { + resultColors[i] = ""; } - - // Appliquer les couleurs aux cases et aux touches si une couleur a été définie - for (var i = 0; i < nb_lettres; i++) { - var case_verif = caseArray[current_essai * nb_lettres + i]; - if (resultColors[i] !== "") { - case_verif.rectangleColor = resultColors[i]; - case_verif.rectangleBorderColor = resultColors[i]; - changeKeyColor(candidate[i], resultColors[i]); + } + if (mot_entre.toUpperCase() === mot.toUpperCase()) { + motusGame.setWinbool(true); + console.log("✅ Victoire !"); + updateGameStats(); + } + for (var i = 0; i < nb_lettres; i++) { + if (resultColors[i] === "") { + var letter = candidate[i]; + if (targetCounts[letter] && targetCounts[letter] > 0) { + resultColors[i] = "#f0d437"; + targetCounts[letter]--; } } - - // Passage à la ligne suivante et mise à jour du focus - if (current_essai < nb_essais - 1) { - current_essai++; - indice_case = 0; - case_focus = caseArray[current_essai * nb_lettres]; - } else { - // Fin de partie : désactiver l'interface ou afficher un message - if (mot_entre.toUpperCase() !== mot.toUpperCase()) { - motusGame.setLoosetrybool(true); - console.log("loosetry=true"); - } - + } + for (var i = 0; i < nb_lettres; i++) { + var case_verif = caseArray[current_essai * nb_lettres + i]; + if (resultColors[i] !== "") { + case_verif.rectangleColor = resultColors[i]; + case_verif.rectangleBorderColor = resultColors[i]; + changeKeyColor(candidate[i], resultColors[i]); } } - - - else { - // Le mot n'existe pas dans le dictionnaire : colorer toute la ligne en rouge - for (var i = 0; i < nb_lettres; i++) { - var case_verif = caseArray[current_essai * nb_lettres + i]; - case_verif.rectangleColor = "#cf1b2a"; - case_verif.rectangleBorderColor = "#cf1b2a"; + if (current_essai < nb_essais - 1) { + current_essai++; + indice_case = 0; + case_focus = caseArray[current_essai * nb_lettres]; + } else { + if (mot_entre.toUpperCase() !== mot.toUpperCase()) { + motusGame.setLoosetrybool(true); + console.log("loosetry=true"); + updateGameStats(); } - revertTimer.start(); } + } else { + for (var i = 0; i < nb_lettres; i++) { + var case_verif = caseArray[current_essai * nb_lettres + i]; + case_verif.rectangleColor = "#cf1b2a"; + case_verif.rectangleBorderColor = "#cf1b2a"; + } + revertTimer.start(); } } - // Saisie d'une lettre (de A à Z) - else{ - if (indice_case < nb_lettres) { - var cellIndex = current_essai * nb_lettres + indice_case; - caseArray[cellIndex]._textText = modelData.toUpperCase(); - indice_case++; - } + } else { + if (indice_case < nb_lettres) { + var cellIndex = current_essai * nb_lettres + indice_case; + caseArray[cellIndex]._textText = modelData.toUpperCase(); + indice_case++; } } } } - Component.onCompleted: { - mainWindow.keysArray.push(keyRect); - } + } + Component.onCompleted: { + mainWindow.keysArray.push(keyRect); } } } @@ -603,29 +761,6 @@ Window { } } } - - Rectangle { - id: rectangle_right - width: 400 - height: 850 - color: "#323232" - border.width: 0 - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 0.05 * Window.width - - Row { - y: 50 - anchors.horizontalCenter: parent.horizontalCenter - spacing: 20 - - MotusTimer { - id: motusTimer - width: 100 - height: 100 - } - } - } } } @@ -641,7 +776,7 @@ Window { Text { y: 32 - text: "Temps écoulé ! Mot : "+ mot + text: "Temps écoulé ! Mot : " + mot visible: motusGame && motusGame.loosetime color: "red" font.pixelSize: 30 @@ -650,7 +785,7 @@ Window { Text { y: 32 - text: "Nombre d'essais maximum atteint ! Mot : "+ mot + text: "Nombre d'essais maximum atteint ! Mot : " + mot visible: motusGame && motusGame.loosetry color: "red" font.pixelSize: 30 diff --git a/Motus/motus_stats.txt b/Motus/motus_stats.txt new file mode 100644 index 0000000000000000000000000000000000000000..c249136d2dc00aeacacbf60a83b91577733836d4 --- /dev/null +++ b/Motus/motus_stats.txt @@ -0,0 +1,201 @@ +0 +0 +0 +0 +0 +0 +0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 diff --git a/Motus/motus_stats_init.txt b/Motus/motus_stats_init.txt new file mode 100644 index 0000000000000000000000000000000000000000..c249136d2dc00aeacacbf60a83b91577733836d4 --- /dev/null +++ b/Motus/motus_stats_init.txt @@ -0,0 +1,201 @@ +0 +0 +0 +0 +0 +0 +0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 +0 +0 +0,0,0,0,0,0,0,0 +0 +0 +0 +0 diff --git a/Motus/motusgame.cpp b/Motus/motusgame.cpp index 1bf9d5e0fd394bc94c0e2e19261cd82532c34569..48926c7498e4b955263eae675ef7cf48edc0eb8a 100644 --- a/Motus/motusgame.cpp +++ b/Motus/motusgame.cpp @@ -2,43 +2,69 @@ #include <QFile> #include <QTextStream> #include <QRandomGenerator> - - #include <QDebug> +#include <QStandardPaths> +#include <QDir> +#include <QCoreApplication> - -// Constructeur ////////////////////////////////////////////////////////////////////////////////////////// - +// Constructeur MotusGame::MotusGame(QObject *parent) : QObject(parent) { loadWords(); qDebug() << "🔧 Connexion timer établie"; + // Initialiser les statistiques pour les sections "English", "French" et "Combined" + QStringList sections = {"English", "French", "Combined"}; + for (const QString &sec : sections) { + GameStats gs; + gs.totalGames = 0; + gs.totalWins = 0; + gs.currentWinStreak = 0; + gs.bestWinStreak = 0; + // Pour chaque longueur de 4 à 12 lettres + for (int len = 4; len <= 12; ++len) { + LocalStats ls; + ls.averageTime = 0.0; + ls.bestTime = 0.0; + int attempts = 0; + switch(len) { + case 4: attempts = 5; break; + case 5: attempts = 6; break; + case 6: attempts = 6; break; + case 7: attempts = 7; break; + case 8: attempts = 7; break; + default: attempts = 8; break; + } + for (int i = 0; i < attempts; ++i) + ls.attemptsHistogram.append(0); + ls.gamesPlayed = 0; + ls.gamesWon = 0; + ls.currentWinStreak = 0; + ls.bestWinStreak = 0; + gs.localStats.insert(len, ls); + } + stats.insert(sec, gs); + } + // Charger les statistiques sauvegardées si disponibles + loadStats(); - countdownTimer = new QTimer(this); // ✅ rattaché à this + countdownTimer = new QTimer(this); countdownTimer->setInterval(1000); connect(countdownTimer, &QTimer::timeout, this, [this]() { qDebug() << "🔁 Timeout déclenché"; - if (remainingTime > 0) { remainingTime--; emit timerUpdated(); qDebug() << "⏱️ Tick:" << remainingTime; - if (remainingTime == 0) { countdownTimer->stop(); qDebug() << "⏱️ Temps écoulé !"; - - setLoosetimebool(true); // ✅ Appel ici + setLoosetimebool(true); qDebug() << "💀 Défaite enregistrée via setLoosetimebool(true)"; } } }); - - qDebug() << "🔧 Connexion timer établie"; - - } void MotusGame::stopTimer() { @@ -48,19 +74,15 @@ void MotusGame::stopTimer() { } } -// Générer un mot ////////////////////////////////////////////////////////////////////////////////////////// - void MotusGame::loadWords() { - // Vider la structure existante wordsByLength.clear(); - QFile file(dictionnaryChoosed); // Fichier dans le .qrc + QFile file(dictionnaryChoosed); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&file); while (!in.atEnd()) { QString line = in.readLine().trimmed(); if (!line.isEmpty()) { int len = line.length(); - // On ne conserve que les mots de 4 à 12 lettres if (len >= 4 && len <= 12) { wordsByLength[len].append(line); } @@ -68,7 +90,6 @@ void MotusGame::loadWords() { } file.close(); int totalWords = 0; - // Calcul du nombre total de mots chargés dans l'intervalle [4,12] for (int i = 4; i <= 12; ++i) { totalWords += wordsByLength[i].size(); } @@ -81,20 +102,17 @@ void MotusGame::loadWords() { bool MotusGame::existWord(const QString &word) { int n = word.size(); bool exists = false; - // Supposons que wordsByLength est un QMap<int, QStringList> const QStringList &list = wordsByLength[n]; for (int i = 0; i < list.size(); i++) { if (list[i].toUpper() == word) { exists = true; - break; // On arrête la recherche dès qu'on trouve le mot + break; } } - return exists; } QString MotusGame::getRandomWord(int letterCount) { - // Vérifier que le nombre de lettres est dans l'intervalle autorisé if (letterCount < 5 || letterCount > 12) { qDebug() << "❌ Nombre de lettres invalide. Choisissez un nombre entre 4 et 12."; return ""; @@ -104,54 +122,38 @@ QString MotusGame::getRandomWord(int letterCount) { return ""; } int index = QRandomGenerator::global()->bounded(wordsByLength[letterCount].size()); - motChoisi = wordsByLength[letterCount][index]; // Stockage du mot choisi + motChoisi = wordsByLength[letterCount][index]; return motChoisi; } - -// Changement de langue ////////////////////////////////////////////////////////////////////////////////////////// - - QString MotusGame::getDictionnaryChoosed() const { return dictionnaryChoosed; } - - void MotusGame::setDictionnaryChoosed(const QString &value) { if (dictionnaryChoosed != value) { dictionnaryChoosed = value; - emit dictionnaryChoosedChanged(); // ✅ Signal pour que QML soit au courant + emit dictionnaryChoosedChanged(); loadWords(); qDebug() << "📁 Nouveau dictionnaire sélectionné :" << dictionnaryChoosed; } } -// Démarrer le timer ////////////////////////////////////////////////////////////////////////////////////////// void MotusGame::startTimer() { qDebug() << "🔁 startTimer() appelé"; - remainingTime = duree_timer; emit timerUpdated(); - countdownTimer->stop(); countdownTimer->start(); qDebug() << "▶️ Timer actif ?" << countdownTimer->isActive(); - qDebug() << "▶️ Timer lancé"; qDebug() << "⏳ remainingTime =" << remainingTime; - - } - int MotusGame::getRemainingTime() const { return remainingTime; } - -// Donner le nombre de lettres pour les mots à générer ////////////////////////////////////////////////////////////////////////////////////////// - int MotusGame::getLetterNumber() const { return letterNumber; } @@ -164,7 +166,6 @@ void MotusGame::setLetterNumber(int value) { } } - bool MotusGame::getRandomizeLetterNumber() const { return randomizeletterNumber; } @@ -177,9 +178,6 @@ void MotusGame::setRandomizeLetterNumber(bool value) { } } - -// Savoir si ça gagne ou ça perd ////////////////////////////////////////////////////////////////////////////////////////// - bool MotusGame::getLoosetrybool() const { return loosetry; } @@ -192,24 +190,139 @@ bool MotusGame::getLoosetimebool() const { return loosetime; } -void MotusGame::setLoosetimebool(bool value){ - loosetime=value; - this->stopTimer(); +void MotusGame::setLoosetimebool(bool value) { + loosetime = value; + stopTimer(); emit loosetimeChanged(); } -void MotusGame::setLoosetrybool(bool value){ - loosetry=value; - this->stopTimer(); +void MotusGame::setLoosetrybool(bool value) { + loosetry = value; + stopTimer(); emit loosetryChanged(); } -void MotusGame::setWinbool(bool value){ - win=value; - this->stopTimer(); +void MotusGame::setWinbool(bool value) { + win = value; + stopTimer(); emit winChanged(); } +// Méthode pour charger les statistiques depuis "motus_stats.txt" +void MotusGame::loadStats() { + // Obtenir un chemin d'accès fiable (répertoire de données de l'application) + QFile file("motus_stats.txt"); + // Si le fichier n'existe pas, on peut le créer avec les valeurs par défaut + if (!file.exists()) { + qDebug() << "Le fichier stats n'existe pas, création d'un fichier par défaut."; + saveStats(); // Cette méthode écrira un fichier avec les valeurs initiales + return; + } + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "Erreur d'ouverture du fichier stats" ; + return; + } + QTextStream in(&file); + QStringList sections = {"English", "French", "Combined"}; + for (const QString &sec : sections) { + GameStats &gs = stats[sec]; + gs.totalGames = in.readLine().toInt(); + gs.totalWins = in.readLine().toInt(); + gs.currentWinStreak = in.readLine().toInt(); + gs.bestWinStreak = in.readLine().toInt(); + for (int len = 4; len <= 12; ++len) { + LocalStats ls; + ls.averageTime = in.readLine().toDouble(); + ls.bestTime = in.readLine().toDouble(); + QString histLine = in.readLine(); + QStringList histValues = histLine.split(",", Qt::SkipEmptyParts); + ls.attemptsHistogram.clear(); + for (const QString &val : histValues) { + ls.attemptsHistogram.append(val.toInt()); + } + ls.gamesPlayed = in.readLine().toInt(); + ls.gamesWon = in.readLine().toInt(); + ls.currentWinStreak = in.readLine().toInt(); + ls.bestWinStreak = in.readLine().toInt(); + gs.localStats[len] = ls; + } + } + file.close(); + qDebug() << "Statistiques chargées"; +} +void MotusGame::saveStats() { + QFile file("motus_stats.txt"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "Erreur d'ouverture en écriture du fichier stats"; + return; + } + QTextStream out(&file); + QStringList sections = {"English", "French", "Combined"}; + for (const QString &sec : sections) { + GameStats &gs = stats[sec]; + out << gs.totalGames << "\n"; + out << gs.totalWins << "\n"; + out << gs.currentWinStreak << "\n"; + out << gs.bestWinStreak << "\n"; + for (int len = 4; len <= 12; ++len) { + LocalStats &ls = gs.localStats[len]; + out << ls.averageTime << "\n"; + out << ls.bestTime << "\n"; + QStringList histValues; + for (int val : ls.attemptsHistogram) { + histValues.append(QString::number(val)); + } + out << histValues.join(",") << "\n"; + out << ls.gamesPlayed << "\n"; + out << ls.gamesWon << "\n"; + out << ls.currentWinStreak << "\n"; + out << ls.bestWinStreak << "\n"; + } + } + file.close(); + qDebug() << "Statistiques sauvegardées"; +} - +// Méthode pour mettre à jour les statistiques d'une partie terminée +// letterCount : nombre de lettres du mot +// language : "English" ou "French" +// win : true en cas de victoire, false sinon +// attemptsUsed : nombre d'essais utilisés (si victoire) +// gameTime : temps mis (en secondes, si victoire) +void MotusGame::updateStats(int letterCount, const QString &language, bool win, int attemptsUsed, int gameTime) { + QString langKey = language; + QString combinedKey = "Combined"; + QList<QString> keysToUpdate; + keysToUpdate << langKey << combinedKey; + for (const QString &key : keysToUpdate) { + GameStats &gs = stats[key]; + gs.totalGames++; + gs.localStats[letterCount].gamesPlayed++; + if (win) { + gs.totalWins++; + gs.localStats[letterCount].gamesWon++; + gs.currentWinStreak++; + if (gs.currentWinStreak > gs.bestWinStreak) + gs.bestWinStreak = gs.currentWinStreak; + LocalStats &ls = gs.localStats[letterCount]; + ls.currentWinStreak++; + if (ls.currentWinStreak > ls.bestWinStreak) + ls.bestWinStreak = ls.currentWinStreak; + if (attemptsUsed >= 1 && attemptsUsed <= ls.attemptsHistogram.size()) + ls.attemptsHistogram[attemptsUsed - 1] += 1; + if (ls.gamesWon == 1) { + ls.averageTime = gameTime; + ls.bestTime = gameTime; + } else { + ls.averageTime = ((ls.averageTime * (ls.gamesWon - 1)) + gameTime) / ls.gamesWon; + if (gameTime < ls.bestTime) + ls.bestTime = gameTime; + } + } else { + gs.currentWinStreak = 0; + gs.localStats[letterCount].currentWinStreak = 0; + } + } + qDebug() << "Statistiques mises à jour pour" << language << "avec" << letterCount << "lettres, victoire:" << win; +} diff --git a/Motus/motusgame.h b/Motus/motusgame.h index 5963e2e0e4aade83f4165ecbd1f09a57a91fc667..7aaa25eb37404d3741e59905378a1064ce7c4773 100644 --- a/Motus/motusgame.h +++ b/Motus/motusgame.h @@ -6,43 +6,51 @@ #include <QStringList> #include <QMap> #include <QTimer> +#include <QList> + +// Structure pour stocker les statistiques locales par nombre de lettres +struct LocalStats { + double averageTime; + double bestTime; + QList<int> attemptsHistogram; // Histogramme des victoires par nombre d'essais + int gamesPlayed; + int gamesWon; + int currentWinStreak; + int bestWinStreak; +}; +// Structure pour stocker les statistiques globales et locales d'une section (ex : English, French, Combined) +struct GameStats { + int totalGames; + int totalWins; + int currentWinStreak; + int bestWinStreak; + QMap<int, LocalStats> localStats; // Clé = nombre de lettres (4 à 12) +}; class MotusGame : public QObject { Q_OBJECT - Q_PROPERTY(QString dictionnaryChoosed - READ getDictionnaryChoosed - WRITE setDictionnaryChoosed - NOTIFY dictionnaryChoosedChanged) - + Q_PROPERTY(QString dictionnaryChoosed READ getDictionnaryChoosed WRITE setDictionnaryChoosed NOTIFY dictionnaryChoosedChanged) Q_PROPERTY(int remainingTime READ getRemainingTime NOTIFY timerUpdated) - Q_PROPERTY(int letterNumber READ getLetterNumber WRITE setLetterNumber NOTIFY letterNumberChanged) - Q_PROPERTY(bool randomizeLetterNumber READ getRandomizeLetterNumber WRITE setRandomizeLetterNumber NOTIFY randomizeLetterNumberChanged) - Q_PROPERTY(bool loosetry READ getLoosetrybool WRITE setLoosetrybool NOTIFY loosetryChanged) - Q_PROPERTY(bool loosetime READ getLoosetimebool WRITE setLoosetimebool NOTIFY loosetimeChanged) - Q_PROPERTY(bool win READ getWinbool WRITE setWinbool NOTIFY winChanged) - - public: explicit MotusGame(QObject *parent = nullptr); Q_INVOKABLE QString getRandomWord(int value); - QString getDictionnaryChoosed() const; //Pour le changement de mot à deviner + QString getDictionnaryChoosed() const; void setDictionnaryChoosed(const QString &value); - - Q_INVOKABLE void startTimer(); //Pour le début du timer + Q_INVOKABLE void startTimer(); int getRemainingTime() const; Q_INVOKABLE void stopTimer(); - int getLetterNumber() const; //Pour le nombre de lettres du mot qu'on veut générer + int getLetterNumber() const; void setLetterNumber(int value); bool getRandomizeLetterNumber() const; void setRandomizeLetterNumber(bool value); @@ -54,40 +62,37 @@ public: bool getWinbool() const; Q_INVOKABLE void setWinbool(bool value); - Q_INVOKABLE bool existWord(const QString &word); - - - + // Méthodes pour la gestion des statistiques + Q_INVOKABLE void loadStats(); // Charger les stats depuis le fichier + Q_INVOKABLE void saveStats(); // Sauvegarder les stats dans le fichier + Q_INVOKABLE void updateStats(int letterCount, const QString &language, bool win, int attemptsUsed, int gameTime); private: QStringList words; - - QString motChoisi; // Mot choisi aléatoirement dans la liste, selon le nombre de lettres choisi par l'utilisateur - + QString motChoisi; QMap<int, QStringList> wordsByLength; - int letterNumber = 5; // Nombre de lettres dans le mot, choisi par l'utilisateur + int letterNumber = 5; bool randomizeletterNumber = true; - - int tryTotal=6; // Nombre d'essais total - int tryNumber=0; - - QString dictionnaryChoosed="Motus\\words_alpha.txt"; // "Motus\\words_alpha.txt" ou "Motus\\mots_francais.txt" + int tryTotal = 6; + int tryNumber = 0; + QString dictionnaryChoosed = "Motus\\words_alpha.txt"; QTimer *countdownTimer; - - int remainingTime = 120; // chrono en secondes - + int remainingTime = 120; bool loosetry = false; bool loosetime = false; bool win = false; bool debut = false; - int duree_timer=120; + int duree_timer = 120; void loadWords(); + // Stockage des statistiques pour "English", "French" et "Combined" + QMap<QString, GameStats> stats; // Clés : "English", "French", "Combined" + signals: void dictionnaryChoosedChanged(); void timerUpdated(); @@ -96,8 +101,6 @@ signals: void loosetryChanged(); void loosetimeChanged(); void winChanged(); - - }; #endif // MOTUSGAME_H