From 5a2d2af6c706cf94710864fcdf3b967c2129539b Mon Sep 17 00:00:00 2001 From: amassies <alexandre.massies@etu.ec-lyon.fr> Date: Fri, 4 Apr 2025 23:08:04 +0200 Subject: [PATCH] nouvelles dimensions et stats --- Motus/CMakeLists.txt | 1 + Motus/Choosebutton.qml | 6 +- Motus/LangageButton.qml | 13 +- Motus/Main.qml | 732 +++++++++++++++++++--------------------- Motus/motusgame.cpp | 70 ++-- Motus/motusgame.h | 17 +- 6 files changed, 401 insertions(+), 438 deletions(-) diff --git a/Motus/CMakeLists.txt b/Motus/CMakeLists.txt index ad0ea9c..d2ba494 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 RESOURCES motus_stats.txt ) diff --git a/Motus/Choosebutton.qml b/Motus/Choosebutton.qml index f7f4327..9cbab26 100644 --- a/Motus/Choosebutton.qml +++ b/Motus/Choosebutton.qml @@ -2,8 +2,8 @@ import QtQuick import QtQuick.Controls 2.15 Item { - width: 300 - height: 120 + width: 200 + height: 80 property alias _textText: _text.text Rectangle { @@ -19,7 +19,7 @@ Item { color: "#ffffff" text: qsTr("Bouton") anchors.fill: parent - font.pixelSize: 35 + font.pixelSize: 25 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.family: "Tahoma" diff --git a/Motus/LangageButton.qml b/Motus/LangageButton.qml index 13123c2..a62757c 100644 --- a/Motus/LangageButton.qml +++ b/Motus/LangageButton.qml @@ -1,8 +1,8 @@ import QtQuick Item { - width: 200 - height: 100 + width: 180 + height: 70 property alias rectangleColor: rectangle.color property alias _textText: _text.text @@ -11,8 +11,8 @@ Item { id: rectangle x: 0 y: 0 - width: 200 - height: 100 + width: 180 + height: 70 color: rectangleColor radius: 10 border.color: "#ffffff" @@ -22,12 +22,9 @@ Item { Text { id: _text - x: 0 - y: 0 - width: 200 - height: 100 color: "#ffffff" text: qsTr("Langage") + anchors.fill: parent font.pixelSize: 30 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter diff --git a/Motus/Main.qml b/Motus/Main.qml index 78da2f9..88ae310 100644 --- a/Motus/Main.qml +++ b/Motus/Main.qml @@ -11,8 +11,7 @@ Window { visibility: Window.Maximized title: qsTr("Motus") - // Les propriétés nb_lettres et nb_essais sont reliées aux propriétés C++ de motusGame. - // Ainsi, dès que motusGame.letterNumber ou motusGame.tryTotal évoluent, l'interface se met à jour. + // Propriétés liées au jeu property int nb_lettres: 5 property int nb_essais: 6 property var caseArray: [] // Rempli lors de la création de la grille @@ -25,10 +24,12 @@ Window { property var keysArray: [] - // Propriété pour afficher/masquer l'overlay de statistiques + // Propriétés pour l'affichage des 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 + // Nombre de lettres pour afficher les stats locales dans l'overlay (entre 4 et 12) + property int selectedLetterForStats: 5 + // Choix de la langue pour les statistiques ("Combined", "French" ou "English") + property string language: "Combined" // Fonction utilitaire pour changer la couleur d'une touche function changeKeyColor(letter, newColor) { @@ -41,20 +42,32 @@ Window { // Fonction pour rafraîchir l'affichage de l'overlay de statistiques function refreshStatsDisplay() { - // Mettez ici à jour vos Text via les propriétés/méthodes de motusGame - // Par exemple : - // globalGamesText.text = "Parties jouées : " + motusGame.getGlobalStats("globalGames"); + var globalStats = motusGame.getGlobalStats("Combined"); + partiesJouees.text = "Parties jouées : " + globalStats.totalGames; + victoires.text = "Victoires : " + globalStats.totalWins; + winStreakActuel.text = "Win Streak Actuel : " + globalStats.currentWinStreak; + meilleurStreak.text = "Meilleur Streak : " + globalStats.bestWinStreak; + + var localStats = motusGame.getLocalStats(selectedLetterForStats, language); + tempsMoyen.text = "Temps moyen : " + localStats.averageTime + " sec"; + meilleurTemps.text = "Meilleur temps : " + localStats.bestTime + " sec"; + partiesJoueesLocal.text = "Parties jouées : " + localStats.gamesPlayed; + partiesGagnees.text = "Parties gagnées : " + localStats.gamesWon; + winStreakActuelLocal.text = "Win Streak Actuel : " + localStats.currentWinStreak; + meilleurStreakLocal.text = "Meilleur Streak : " + localStats.bestWinStreak; + // Mise à jour de l'histogramme via la nouvelle propriété "histData" + histogramRow.histData = localStats.attemptsHistogram; } // Fonction de mise à jour des statistiques à la fin d'une partie function updateGameStats() { - var language = (motusGame.dictionnaryChoosed.indexOf("francais") >= 0) ? "French" : "English"; + var lang = (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 + motusGame.updateStats(letterCount, lang, won, attemptsUsed, gameTime); + motusGame.saveStats(); // Sauvegarde immédiate refreshStatsDisplay(); } @@ -62,12 +75,11 @@ Window { Rectangle { id: statsOverlay anchors.fill: parent - color: "#323232AA" // Fond avec transparence + color: "#323232AA" visible: statsOverlayVisible z: 100 onVisibleChanged: { if (!visible) { - // Redonner le focus à la zone de jeu lorsqu'on ferme l'overlay mainRect.forceActiveFocus(); } else { refreshStatsDisplay(); @@ -89,7 +101,7 @@ Window { anchors.margins: 20 spacing: 20 - // En-tête avec titre et bouton de fermeture en haut à droite + // En-tête RowLayout { width: parent.width Layout.alignment: Qt.AlignTop @@ -115,20 +127,20 @@ Window { } } - // Statistiques Globales (à remplacer par vos appels réels) + // Statistiques Globales GroupBox { title: "Statistiques Globales" Layout.fillWidth: true - contentItem: ColumnLayout { + 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 } + Text { id: partiesJouees; color: "white"; font.pixelSize: 20 } + Text { id: victoires; color: "white"; font.pixelSize: 20 } + Text { id: winStreakActuel; color: "white"; font.pixelSize: 20 } + Text { id: meilleurStreak; color: "white"; font.pixelSize: 20 } } } - // Sélecteur pour le nombre de lettres pour les statistiques locales + // Sélecteur et Statistiques Locales RowLayout { spacing: 10 Layout.fillWidth: true @@ -139,33 +151,14 @@ Window { } ComboBox { id: letterSelector - model: [4,5,6,7,8,9,10,11,12] - currentIndex: 1 + // Modèle de 4 à 12 lettres + model: [4, 5, 6, 7, 8, 9, 10, 11, 12] + currentIndex: 1 // Par défaut, 5 lettres + font.pixelSize: 20 onCurrentIndexChanged: { - if (currentIndex === 9){ - nb_lettres = motusGame.getRandomNum(); - } - else{ - nb_lettres = model[currentIndex]; - } - console.log(nb_lettres); - if (nb_lettres == 4){ - nb_essais = 5; - } - else if (nb_lettres == 5 || nb_lettres == 6){ - nb_essais = 6; - } - else if (nb_lettres == 7 || nb_lettres == 8){ - nb_essais = 7; - } - else{ - nb_essais = 8; - } - caseRepeater.model = nb_lettres*nb_essais; - console.log(caseRepeater.model); - console.log(caseArray); + selectedLetterForStats = model[currentIndex]; + refreshStatsDisplay(); } - font.pixelSize: 20 } Text { text: "lettres" @@ -174,52 +167,53 @@ Window { } } - // Statistiques Locales (à remplacer par vos appels réels) + // Affichage des statistiques locales GroupBox { title: "Statistiques Locales (" + selectedLetterForStats + " lettres)" Layout.fillWidth: true - contentItem: ColumnLayout { + 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 } + Text { id: tempsMoyen; color: "white"; font.pixelSize: 20 } + Text { id: meilleurTemps; color: "white"; font.pixelSize: 20 } + Text { id: partiesJoueesLocal; color: "white"; font.pixelSize: 20 } + Text { id: partiesGagnees; color: "white"; font.pixelSize: 20 } + Text { id: winStreakActuelLocal; color: "white"; font.pixelSize: 20 } + Text { id: meilleurStreakLocal; color: "white"; font.pixelSize: 20 } } } - // Histogramme des victoires par numéro d'essai + // Histogramme des victoires GroupBox { title: "Histogramme des victoires" Layout.fillWidth: true contentItem: RowLayout { id: histogramRow spacing: 10 - // Exemple d'histogramme : à remplacer par vos données réelles. - property var data: [0, 0, 0, 0, 0, 0] + // Propriété renommée pour stocker les données de l'histogramme + property var histData: [] + // Calcul dynamique de la valeur maximum dans histData property int maxValue: { - var maxVal = 0; - for (var i = 0; i < data.length; i++) { - if (data[i] > maxVal) - maxVal = data[i]; + var m = 0; + for (var i = 0; i < histData.length; i++) { + if (histData[i] > m) + m = histData[i]; } - return maxVal; + return m; } Repeater { - model: data.length + model: histogramRow.histData.length delegate: ColumnLayout { spacing: 2 - Item { Layout.fillHeight: true } Rectangle { width: 20 - height: maxValue > 0 ? Math.max(10, 200 * (data[index] / maxValue)) : 10 - color: index === (data.length - 1) ? "#28a745" : "#4a4a4a" + height: histogramRow.maxValue > 0 ? Math.max(10, Math.round((histogramRow.histData[index] / histogramRow.maxValue) * 200)) : 10 + // Vous pouvez personnaliser la couleur en fonction de l'index si besoin + color: "#4a4a4a" border.width: 1 border.color: "white" } Text { - text: "Essai " + (index + 1) + "\n(" + data[index] + ")" + text: "Essai " + (index + 1) + "\n(" + histogramRow.histData[index] + ")" font.pixelSize: 14 color: "white" horizontalAlignment: Text.AlignHCenter @@ -241,7 +235,7 @@ Window { focus: true Keys.onPressed: function(event) { - // On vérifie que le jeu est en cours (ni gagné, ni perdu) + // Si le jeu est en cours if (mot !== "" && !motusGame.loosetry && !motusGame.win && !motusGame.loosetime && !motusGame.looseabandon) { if (/^[a-zA-Z]$/.test(event.text)) { if (indice_case < nb_lettres) { @@ -258,19 +252,14 @@ Window { event.accepted = true; } } else if (event.key === Qt.Key_Return) { - console.log("coucou"); 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 target = mot_split.map(function(letter) { return letter.toUpperCase(); }); var targetCounts = {}; for (var i = 0; i < nb_lettres; i++) { var letter = target[i]; @@ -287,7 +276,6 @@ Window { } if (mot_entre.toUpperCase() === mot.toUpperCase()) { motusGame.setWinbool(true); - console.log("✅ Victoire !"); updateGameStats(); } for (var i = 0; i < nb_lettres; i++) { @@ -305,8 +293,7 @@ Window { case_verif.rectangleColor = resultColors[i]; case_verif.rectangleBorderColor = resultColors[i]; changeKeyColor(candidate[i], resultColors[i]); - } - else{ + } else { changeKeyColor(candidate[i], "#323232"); } } @@ -317,7 +304,6 @@ Window { } else { if (mot_entre.toUpperCase() !== mot.toUpperCase()) { motusGame.setLoosetrybool(true); - console.log("loosetry=true"); updateGameStats(); } } @@ -340,7 +326,6 @@ Window { interval: 1000 repeat: false onTriggered: { - console.log("Réinitialisation du style de la ligne"); for (var i = 0; i < nb_lettres; i++) { var case_verif = caseArray[current_essai * nb_lettres + i]; case_verif.rectangleColor = "#323232"; @@ -356,21 +341,16 @@ Window { Column { id: leftColumn - width: 400 - height: 850 - spacing: 50 + width: 310 + height: 700 + spacing: 40 anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 0.05 * Window.width - - Case { - id: case3 - anchors.horizontalCenter: parent.horizontalCenter - _textText: "mot test" - } + anchors.right: rectangle_center.left + anchors.rightMargin: 50 Choosebutton { id: choosebutton + height: 90 anchors.horizontalCenter: parent.horizontalCenter _textText: "Generate a word" MouseArea { @@ -390,7 +370,7 @@ Window { keysArray[j].keyColor = "#808080"; } motusGame.debut = true; - // Utilisation de la propriété nb_lettres (mappée sur motusGame.letterNumber) + // Génération d'un mot avec nb_lettres (entre 4 et 12) mot = motusGame.getRandomWord(nb_lettres); motusGame.setLoosetrybool(false); motusGame.setLoosetimebool(false); @@ -399,16 +379,14 @@ Window { motusGame.startTimer(); case3._textText = mot; mot_split = mot.split(""); - console.log(mot_split); } } } - Rectangle { id: big_rectangle_langue - width: 300 - height: 300 + width: 200 + height: 240 color: "#323232" radius: 10 border.color: "#ffffff" @@ -433,21 +411,21 @@ Window { LangageButton { id: langageButton + width: 180 + height: 70 anchors.horizontalCenter: parent.horizontalCenter _textText: "Français" rectangleColor: "#7a7a7a" MouseArea { - enabled: motusGame && (motusGame.win || motusGame.loosetry || motusGame.looseabandon || motusGame.loosetime || !motusGame.debut) anchors.fill: parent + enabled: motusGame && (motusGame.win || motusGame.loosetry || motusGame.looseabandon || motusGame.loosetime || !motusGame.debut) 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 = "5"; + text_langue.text = "Language"; } } } @@ -465,20 +443,17 @@ Window { motusGame.dictionnaryChoosed = "Motus\\words_alpha.txt"; langageButton2.rectangleColor = "green"; langageButton.rectangleColor = "#7a7a7a"; - choosebutton._textText = "Generate a word;"; + choosebutton._textText = "Generate a word"; text_langue.text = "Language"; - text_choix_nb_lettres.text = "Number of letters"; - numberLetterBtn.firstItemText = "5"; } } } } } - Rectangle { id: big_rectangle_nb_lettres - width: 300 + width: 200 height: 130 color: "#323232" radius: 10 @@ -502,17 +477,15 @@ Window { ComboBox { id: comboBox - width: 250 + width: 150 height: 50 anchors.horizontalCenter: parent.horizontalCenter currentIndex: 1 - - model: ["4", "5", "6", "7", "8", "9", "10","11","12", "Aléatoire"] + // Modèle de 4 à 12 (pas d'aléatoire) + model: [4, 5, 6, 7, 8, 9, 10, 11, 12] flat: true enabled: motusGame && (motusGame.win || motusGame.loosetry || motusGame.looseabandon || motusGame.loosetime || !motusGame.debut) - - // Le contenu affiché (texte sélectionné) contentItem: Text { width: 200 text: comboBox.displayText @@ -527,7 +500,6 @@ Window { anchors.leftMargin: 20 } - // Fond personnalisé background: Rectangle { radius: 10 color: "#7a7a7a" @@ -535,7 +507,6 @@ Window { border.width: 5 } - // Petite flèche personnalisée indicator: Rectangle { width: 20 height: 20 @@ -543,8 +514,6 @@ Window { anchors.right: parent.right anchors.rightMargin: 10 color: "transparent" - - // Triangle (flèche vers le bas) Canvas { anchors.fill: parent onPaint: { @@ -561,7 +530,6 @@ Window { } } - // Liste déroulante delegate: ItemDelegate { width: comboBox.width contentItem: Text { @@ -572,27 +540,19 @@ Window { } onCurrentIndexChanged: { - if (currentIndex === 9){ - nb_lettres = motusGame.getRandomNum(); - } - else{ - nb_lettres = model[currentIndex]; - } - console.log(nb_lettres); - if (nb_lettres == 4){ + nb_lettres = parseInt(model[currentIndex]); + console.log("nb_lettres =", nb_lettres); + // Mettre à jour nb_essais en fonction de nb_lettres + if (nb_lettres == 4) nb_essais = 5; - } - else if (nb_lettres == 5 || nb_lettres == 6){ + else if (nb_lettres == 5 || nb_lettres == 6) nb_essais = 6; - } - else if (nb_lettres == 7 || nb_lettres == 8){ + else if (nb_lettres == 7 || nb_lettres == 8) nb_essais = 7; - } - else{ + else nb_essais = 8; - } - caseRepeater.model = nb_lettres*nb_essais; - console.log(caseRepeater.model); + caseRepeater.model = nb_lettres * nb_essais; + console.log("Grille de", caseRepeater.model, "cases"); caseArray = []; for (var i = 0; i < caseRepeater.count; i++) { caseArray.push(caseRepeater.itemAt(i)); @@ -600,8 +560,8 @@ Window { if (caseArray.length > 0) { case_focus = caseArray[0]; } - indice_case=0; - current_essai=0; + indice_case = 0; + current_essai = 0; mainRect.focus = true; } } @@ -610,8 +570,8 @@ Window { Rectangle { id: stats_rect - width: 250 - height: 100 + width: 200 + height: 80 color: "#323232" radius: 10 border.color: "#ffffff" @@ -625,24 +585,244 @@ Window { anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom - font.pixelSize: 35 + font.pixelSize: 25 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter font.family: "Tahoma" - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter + anchors.fill: parent cursorShape: Qt.PointingHandCursor - enabled: motusGame && (!motusGame.win && !motusGame.loosetry && !motusGame.looseabandon && !motusGame.loosetime && motusGame.debut) + enabled: motusGame && (!motusGame.win && !motusGame.loosetry && !motusGame.looseabandon && !motusGame.loosetime) onClicked: { updateGameStats(); statsOverlayVisible = true; selectedLetterForStats = nb_lettres; - motusGame.looseabandon=true; + motusGame.looseabandon = true; + } + } + } + } + } + + Rectangle { + id: rectangle_center + width: 500 + height: 700 + color: "#323232" + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + Column { + id: center_column + width: 600 + height: 700 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + spacing: 10 + + Rectangle { + id: rectangle_titre + width: 210 + height: 100 + color: "#323232" + radius: 20 + border.color: "#ffffff" + border.width: 5 + 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 + verticalAlignment: Text.AlignVCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + + // Grille de jeu + Rectangle { + id: rectangle_grid + width: 450 + height: 420 + color: "#323232" + anchors.horizontalCenter: parent.horizontalCenter + + Grid { + id: grid + height: nb_essais * 50 + (nb_essais - 1) * 5 + width: nb_lettres * 50 + (nb_lettres - 1) * 5 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + spacing: 5 + columns: nb_lettres + Repeater { + id: caseRepeater + model: nb_lettres * nb_essais + delegate: Case { + objectName: "caseDelegate" + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: { + rectangle_grid.focus = true; + } + } + } + + Rectangle { + id: rectangle1 + width: 500 + height: 200 + anchors.horizontalCenter: parent.horizontalCenter + color: "#323232" + radius: 10 + border.color: "#ffffff" + border.width: 5 + + Column { + id: column2 + width: 500 + anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.top + anchors.margins: 10 + anchors.horizontalCenter: parent.horizontalCenter + spacing: 6 + topPadding: rectangle1.height * 0.05 + bottomPadding: rectangle1.height * 0.05 + + Repeater { + anchors.verticalCenter: parent.verticalCenter + 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 === "<") ? 70 : 40 + height: 50 + color: keyColor + radius: 5 + border.color: keyColor + + Text { + anchors.centerIn: parent + text: modelData + font.pixelSize: 25 + color: "white" + font.family: "Tahoma" + font.bold: true + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (mot !== "" && !motusGame.loosetry && !motusGame.looseabandon && !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; + } + } 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(""); + 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 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] = ""; + } + } + if (mot_entre.toUpperCase() === mot.toUpperCase()) { + motusGame.setWinbool(true); + 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]--; + } + } + } + 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 (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); + updateGameStats(); + } + } + } 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(); + } + } + } 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); + } + } + } + } } } } @@ -651,18 +831,17 @@ Window { Rectangle { id: rectangle_right - width: 400 + width: 250 height: 850 color: "#323232" - border.width: 0 anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 0.05 * Window.width + anchors.left: rectangle_center.right + anchors.leftMargin: 80 Column { id: column1 - width: 326 - height: 850 + width: 250 + height: 680 anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter spacing: 30 @@ -695,18 +874,12 @@ Window { verticalAlignment: Text.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter font.family: "Tahoma" - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter + anchors.fill: parent cursorShape: Qt.PointingHandCursor - enabled: !(motusGame && (motusGame.win || motusGame.loosetry || motusGame.looseabandon || motusGame.loosetime || !motusGame.debut)) onClicked: { - motusGame.setLooseabandonbool(true); - console.log("looseabandon=true"); updateGameStats(); + statsOverlayVisible = true; } } } @@ -726,7 +899,7 @@ Window { Text { visible: motusGame && motusGame.loosetry color: "red" - text: "Nombre d'essais maximum atteint ! \n Mot : " + mot.toUpperCase() + text: "Nombre d'essais \n maximum atteint ! \n Mot : " + mot.toUpperCase() font.pixelSize: 30 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -757,244 +930,19 @@ Window { } } } - } - - // Zone de grille : ici la grille est désormais dynamique selon nb_lettres et nb_essais - Rectangle { - id: rectangle_center - width: 700 - height: 1000 - color: "#323232" - border.width: 0 - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - - Column { - id: center_column - width: 716 - height: 900 - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - spacing: 20 - - Rectangle { - id: rectangle_titre - width: 300 - height: 120 - color: "#323232" - radius: 20 - border.color: "#ffffff" - border.width: 5 - anchors.horizontalCenter: parent.horizontalCenter - - Text { - id: _text2 - text: qsTr("MOTUS") - anchors.verticalCenter: parent.verticalCenter - font.pixelSize: 70 - color: "white" - font.family: "Tahoma" - font.bold: true - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - anchors.horizontalCenter: parent.horizontalCenter - } - } - - // Grille de jeu dynamique - Rectangle { - id: rectangle_grid - width: 450 - height: 500 - color: "#323232" - border.width: 0 - anchors.horizontalCenter: parent.horizontalCenter - - // Grille dynamique : nombre de colonnes = nb_lettres et nombre de cases = nb_lettres * nb_essais - Grid { - id: grid - height: nb_essais*50 + (nb_essais-1)*5 - anchors.verticalCenter: parent.verticalCenter - width: nb_lettres*50 + (nb_lettres-1)*5 - anchors.horizontalCenter: parent.horizontalCenter - spacing: 5 - columns: nb_lettres - Repeater { - id: caseRepeater - model: nb_lettres * nb_essais - delegate: Case { - objectName: "caseDelegate" - } - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - onClicked: { - rectangle_grid.focus = true; - } - } - } - Rectangle { - id: rectangle1 - width: 600 - height: 230 - anchors.horizontalCenter: parent.horizontalCenter - color: "#323232" - radius: 10 - border.color: "#ffffff" - border.width: 5 + } - Column { - anchors.fill: parent - anchors.margins: 10 - spacing: 6 - topPadding: rectangle1.height * 0.05 - bottomPadding: rectangle1.height * 0.05 + // Zone de grille dynamique - 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.looseabandon && !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; - } - } 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 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] = ""; - } - } - 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]--; - } - } - } - 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 (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(); - } - } - } 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(); - } - } - } 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: { + caseArray = []; + for (var i = 0; i < caseRepeater.count; i++) { + caseArray.push(caseRepeater.itemAt(i)); + } + if (caseArray.length > 0) { + case_focus = caseArray[0]; } - } - } - - Component.onCompleted: { - caseArray = []; - for (var i = 0; i < caseRepeater.count; i++) { - caseArray.push(caseRepeater.itemAt(i)); - } - if (caseArray.length > 0) { - case_focus = caseArray[0]; } } } diff --git a/Motus/motusgame.cpp b/Motus/motusgame.cpp index 89218ac..9c57223 100644 --- a/Motus/motusgame.cpp +++ b/Motus/motusgame.cpp @@ -12,7 +12,7 @@ MotusGame::MotusGame(QObject *parent) : QObject(parent) { loadWords(); qDebug() << "🔧 Connexion timer établie"; - // Initialiser les statistiques pour les sections "English", "French" et "Combined" + // Initialiser les statistiques pour "English", "French" et "Combined" QStringList sections = {"English", "French", "Combined"}; for (const QString &sec : sections) { GameStats gs; @@ -20,7 +20,6 @@ MotusGame::MotusGame(QObject *parent) : QObject(parent) { 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; @@ -44,8 +43,9 @@ MotusGame::MotusGame(QObject *parent) : QObject(parent) { } stats.insert(sec, gs); } - // Charger les statistiques sauvegardées si disponibles + // Charger les statistiques sauvegardées loadStats(); + emit statsChanged(); countdownTimer = new QTimer(this); countdownTimer->setInterval(1000); @@ -113,7 +113,8 @@ bool MotusGame::existWord(const QString &word) { } QString MotusGame::getRandomWord(int letterCount) { - if (letterCount < 5 || letterCount > 12) { + // Accepte désormais les mots de 4 à 12 lettres + if (letterCount < 4 || letterCount > 12) { qDebug() << "❌ Nombre de lettres invalide. Choisissez un nombre entre 4 et 12."; return ""; } @@ -161,7 +162,7 @@ int MotusGame::getLetterNumber() const { void MotusGame::setLetterNumber(int value) { if (letterNumber != value) { letterNumber = value; - // Mettre à jour le nombre d'essais en fonction du nombre de lettres + // Mise à jour du nombre d'essais en fonction du nombre de lettres if (value == 4) tryTotal = 5; else if (value == 5 || value == 6) @@ -171,17 +172,11 @@ void MotusGame::setLetterNumber(int value) { else tryTotal = 8; emit letterNumberChanged(); - emit tryTotalChanged(); // Signal pour notifier l'UI que le nombre d'essais a changé + emit tryTotalChanged(); qDebug() << "Nouvelle valeur de letterNumber :" << letterNumber << ", tryTotal mis à jour à" << tryTotal; } } -int MotusGame::getRandomNum() { - QList<int> essaisPossibles = {5, 6, 6, 7, 7, 8, 8, 8, 8}; - int index = QRandomGenerator::global()->bounded(essaisPossibles.size()); - return essaisPossibles[index]; -} - bool MotusGame::getLoosetrybool() const { return loosetry; } @@ -222,18 +217,15 @@ void MotusGame::setWinbool(bool value) { 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 + saveStats(); return; } if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qDebug() << "Erreur d'ouverture du fichier stats" ; + qDebug() << "Erreur d'ouverture du fichier stats"; return; } QTextStream in(&file); @@ -263,6 +255,7 @@ void MotusGame::loadStats() { } file.close(); qDebug() << "Statistiques chargées"; + emit statsChanged(); } void MotusGame::saveStats() { @@ -298,12 +291,6 @@ void MotusGame::saveStats() { 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"; @@ -339,10 +326,39 @@ void MotusGame::updateStats(int letterCount, const QString &language, bool win, } } qDebug() << "Statistiques mises à jour pour" << language << "avec" << letterCount << "lettres, victoire:" << win; - - // Sauvegarde immédiate dans le fichier texte saveStats(); - - // Émettre un signal pour notifier à l'UI que les stats ont changé emit statsChanged(); } + +QVariantMap MotusGame::getGlobalStats(const QString §ion) { + QVariantMap map; + if (stats.contains(section)) { + GameStats gs = stats[section]; + map["totalGames"] = gs.totalGames; + map["totalWins"] = gs.totalWins; + map["currentWinStreak"] = gs.currentWinStreak; + map["bestWinStreak"] = gs.bestWinStreak; + } + return map; +} + +QVariantMap MotusGame::getLocalStats(int letterCount, const QString §ion) { + QVariantMap map; + if (stats.contains(section)) { + const GameStats &gs = stats[section]; + if (gs.localStats.contains(letterCount)) { + const LocalStats &ls = gs.localStats[letterCount]; + map["averageTime"] = ls.averageTime; + map["bestTime"] = ls.bestTime; + map["gamesPlayed"] = ls.gamesPlayed; + map["gamesWon"] = ls.gamesWon; + map["currentWinStreak"] = ls.currentWinStreak; + map["bestWinStreak"] = ls.bestWinStreak; + QVariantList histo; + for (int val : ls.attemptsHistogram) + histo.append(val); + map["attemptsHistogram"] = histo; + } + } + return map; +} diff --git a/Motus/motusgame.h b/Motus/motusgame.h index 1f13628..bd57568 100644 --- a/Motus/motusgame.h +++ b/Motus/motusgame.h @@ -7,6 +7,7 @@ #include <QMap> #include <QTimer> #include <QList> +#include <QVariantMap> // Structure pour stocker les statistiques locales par nombre de lettres struct LocalStats { @@ -25,7 +26,7 @@ struct GameStats { int totalWins; int currentWinStreak; int bestWinStreak; - QMap<int, LocalStats> localStats; // Clé = nombre de lettres (4 à 12) + QMap<int, LocalStats> localStats; // Clé = nombre de lettres (de 4 à 12) }; class MotusGame : public QObject { @@ -52,7 +53,7 @@ public: int getLetterNumber() const; void setLetterNumber(int value); - Q_INVOKABLE int getRandomNum(); + Q_INVOKABLE int getTryTotal() const { return tryTotal; } bool getLoosetrybool() const; Q_INVOKABLE void setLoosetrybool(bool value); @@ -66,11 +67,12 @@ public: 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 loadStats(); + Q_INVOKABLE void saveStats(); Q_INVOKABLE void updateStats(int letterCount, const QString &language, bool win, int attemptsUsed, int gameTime); - int getTryTotal() const { return tryTotal; } + Q_INVOKABLE QVariantMap getGlobalStats(const QString §ion); + Q_INVOKABLE QVariantMap getLocalStats(int letterCount, const QString §ion); private: QStringList words; @@ -94,7 +96,6 @@ private: void loadWords(); - // Stockage des statistiques pour "English", "French" et "Combined" QMap<QString, GameStats> stats; // Clés : "English", "French", "Combined" signals: @@ -105,8 +106,8 @@ signals: void loosetimeChanged(); void looseabandonChanged(); void winChanged(); - void statsChanged(); // Signal pour informer que les statistiques ont changé - void tryTotalChanged(); // Signal pour informer que le nombre d'essais a changé + void statsChanged(); + void tryTotalChanged(); }; #endif // MOTUSGAME_H -- GitLab