diff --git a/public/index.html b/public/index.html index b86e2cc..1f42e68 100644 --- a/public/index.html +++ b/public/index.html @@ -14,31 +14,47 @@
-
+

SUTOM

-
+
 
-
-
-

Félicitations

-

Bravo, tu as gagné. Reviens demain pour une nouvelle grille.

+
+
+
+

Règles

+ +
+
-
-

Perdu

-

- Le mot a trouver était :
- Peut être feras-tu mieux demain ? -

-
-

Résumé de ta partie Partager

-

       
Chargement de la grille en cours…
@@ -78,67 +94,106 @@
-
-

- Vous avez six essais pour deviner le mot du jour.
- Vous ne pouvez proposer que des mots commençant par la même lettre que le mot recherché, et qui se trouvent dans notre dictionnaire.
- Les lettres entourées d'un carré rouge sont bien placées,
- les lettres entourées d'un cercle jaune sont mal placées (mais présentes dans le mot).
- Les lettres qui restent sur fond bleu ne sont pas dans le mot.
- Il y a un mot par jour, entre 6 et 9 lettres, et il est identique pour tout le monde. Évitez donc les spoils et privilégiez le bouton de partage.
- En cas de soucis, vous pouvez contacter @Jonamaths sur twitter. − - Page du projet
- Basé sur l'excellent Wordle et le regretté Motus.
- Merci à Emmanuel pour l'aide sur les mots à trouver, et à GaranceAmarante pour l'aide sur le dictionnaire. -

-
- + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
diff --git a/public/jeu.css b/public/jeu.css index 60ecea7..3399c14 100644 --- a/public/jeu.css +++ b/public/jeu.css @@ -7,6 +7,8 @@ --couleur-fond-grille: #0077c7; --couleur-non-trouve: rgb(112, 112, 112); --couleur-icone: rgb(200, 200, 200); + --couleur-fond-rgb: 43, 43, 43; + --couleur-fond: rgb(var(--couleur-fond-rgb)); } @font-face { @@ -17,7 +19,7 @@ body { font-family: "Roboto Medium", Ubuntu, Arial, Helvetica, sans-serif; font-size: 32px; - background-color: #2b2b2b; + background-color: var(--couleur-fond); height: 100vh; text-align: center; color: white; @@ -52,7 +54,7 @@ body { header { display: grid; - grid-template-columns: 1fr 6fr 1fr; + grid-template-columns: 2fr 6fr 2fr; align-items: center; justify-content: space-between; width: 100%; @@ -65,9 +67,17 @@ h1 { margin: 0; } +.header-icones { + display: flex; + justify-content: space-around; +} + +#configuration-regles-icone, +#configuration-config-icone, +#configuration-stats-icone, #configuration-audio-icone { height: 32px; - width: 40px; + width: 32px; } #grille { @@ -93,7 +103,7 @@ h1 { } #grille td:not(.resultat) { - background-color: #0077c7; + background-color: var(--couleur-fond-grille); } #grille td.resultat::after { @@ -119,9 +129,7 @@ h1 { background-color: var(--couleur-fond-grille); } -#fin-de-partie-panel, -#victoire-panel, -#defaite-panel { +#panel-area { display: none; font-size: 24px; @@ -192,15 +200,13 @@ h1 { cursor: initial; } -#regles-panel { +.regles-panel #panel-fenetre { font-size: 14px; text-align: left; } -#regles-panel a, -#regles-panel a:visited, -#fin-de-partie-panel a, -#fin-de-partie-panel a:visited { +#panel-area a, +#panel-area a:visited { color: white; } @@ -208,3 +214,59 @@ h1 { opacity: 0; transition: opacity linear 1s; } + +#panel-area { + position: fixed; + top: 0; + left: 0; + background-color: rgba(var(--couleur-fond-rgb), 0.45); + width: 100vw; + height: 100vh; + z-index: 10; +} + +#panel-fenetre { + background-color: var(--couleur-fond); + width: 50%; + margin-left: auto; + margin-right: auto; + min-height: 400px; + border: 1px solid white; + border-radius: 0.25em; + margin-top: 3em; + display: flex; + flex-direction: column; + padding: 0.5em; +} + +#panel-fenetre-header { + display: flex; + align-content: center; + justify-content: space-between; + width: calc(100% - 1em); + height: calc(36px + 0.75em); + margin-left: 0.5em; + margin-right: 0.5em; + margin-top: 0.25em; + margin-bottom: 0.5em; +} + +#panel-fenetre-titre { + font-size: 36px; + margin: 0; +} + +#panel-fenetre-bouton-fermeture { + text-decoration: none; +} + +#panel-fenetre-bouton-fermeture-icone { + width: 32px; + height: 32px; +} + +@media (max-width: 1024px) { + #panel-fenetre { + width: 90%; + } +} diff --git a/ts/configuration.ts b/ts/configuration.ts index bfdc684..197b065 100644 --- a/ts/configuration.ts +++ b/ts/configuration.ts @@ -1,5 +1,6 @@ export default class Configuration { - public static Default: Configuration = { hasAudio: false }; + public static Default: Configuration = { hasAudio: false, afficherRegles: true }; hasAudio: boolean = false; + afficherRegles: boolean = true; } diff --git a/ts/configurationPanel.ts b/ts/configurationPanel.ts new file mode 100644 index 0000000..bea5f31 --- /dev/null +++ b/ts/configurationPanel.ts @@ -0,0 +1,29 @@ +import Configuration from "./configuration"; +import PanelManager from "./panelManager"; +import Sauvegardeur from "./sauvegardeur"; + +export default class ConfigurationPanel { + private readonly _panelManager: PanelManager; + private readonly _configBouton: HTMLElement; + + public constructor(panelManager: PanelManager) { + this._panelManager = panelManager; + this._configBouton = document.getElementById("configuration-config-bouton") as HTMLElement; + + this._configBouton.addEventListener( + "click", + (() => { + this.afficher(); + }).bind(this) + ); + } + + public afficher(): void { + let titre = "Configuration"; + let contenu = ""; + + this._panelManager.setContenu(titre, contenu); + this._panelManager.setClasses(["config-panel"]); + this._panelManager.afficherPanel(); + } +} diff --git a/ts/finDePartiePanel.ts b/ts/finDePartiePanel.ts index 30d94e2..c150340 100644 --- a/ts/finDePartiePanel.ts +++ b/ts/finDePartiePanel.ts @@ -1,46 +1,32 @@ import LettreResultat from "./lettreResultat"; import { LettreStatut } from "./lettreStatut"; import NotificationMessage from "./notificationMessage"; +import PanelManager from "./panelManager"; export default class FinDePartiePanel { - private readonly _finDePartiePanel: HTMLElement; - private readonly _victoirePanel: HTMLElement; - private readonly _defaitePanel: HTMLElement; - private readonly _defaitePanelMot: HTMLElement; - private readonly _resume: HTMLPreElement; - private readonly _resumeBouton: HTMLElement; private readonly _datePartie: Date; + private readonly _panelManager: PanelManager; + private readonly _statsButton: HTMLElement; private _resumeTexte: string = ""; + private _motATrouver: string = ""; + private _estVictoire: boolean = false; + private _partieEstFinie: boolean = false; - public constructor(datePartie: Date) { - this._finDePartiePanel = document.getElementById("fin-de-partie-panel") as HTMLElement; - this._victoirePanel = document.getElementById("victoire-panel") as HTMLElement; - this._defaitePanel = document.getElementById("defaite-panel") as HTMLElement; - this._defaitePanelMot = document.getElementById("defaite-panel-mot") as HTMLElement; - this._resume = document.getElementById("fin-de-partie-panel-resume") as HTMLPreElement; - this._resumeBouton = document.getElementById("fin-de-partie-panel-resume-bouton") as HTMLElement; - + public constructor(datePartie: Date, panelManager: PanelManager) { this._datePartie = datePartie; + this._panelManager = panelManager; + this._statsButton = document.getElementById("configuration-stats-bouton") as HTMLElement; - this._resumeBouton.addEventListener("click", (event) => { - event.stopPropagation(); - if (!navigator.clipboard) { - NotificationMessage.ajouterNotification("Votre navigateur n'est pas compatible"); - } - - navigator.clipboard - .writeText(this._resumeTexte + "\n\nhttps://sutom.nocle.fr") - .then(() => { - NotificationMessage.ajouterNotification("Résumé copié dans le presse papier"); - }) - .catch((raison) => { - NotificationMessage.ajouterNotification("Votre navigateur n'est pas compatible"); - }); - }); + this._statsButton.addEventListener( + "click", + (() => { + this.afficher(); + }).bind(this) + ); } - public genererResume(estBonneReponse: boolean, resultats: Array>): void { + public genererResume(estBonneReponse: boolean, motATrouver: string, resultats: Array>): void { let resultatsEmojis = resultats.map((mot) => mot .map((resultat) => resultat.statut) @@ -57,20 +43,60 @@ export default class FinDePartiePanel { ); let dateGrille = this._datePartie.getTime(); let origine = new Date(2022, 0, 8).getTime(); + this._motATrouver = motATrouver; + this._estVictoire = estBonneReponse; + this._partieEstFinie = true; let numeroGrille = Math.floor((dateGrille - origine) / (24 * 3600 * 1000)) + 1; this._resumeTexte = "SUTOM #" + numeroGrille + " " + (estBonneReponse ? resultats.length : "-") + "/6\n\n" + resultatsEmojis.join("\n"); - this._resume.innerText = this._resumeTexte; } - public afficher(estVictoire: boolean, motATrouver: string): void { - this._finDePartiePanel.style.display = "block"; + private attacherPartage(): void { + let resumeBouton = document.getElementById("fin-de-partie-panel-resume-bouton") as HTMLElement; + resumeBouton.addEventListener("click", (event) => { + event.stopPropagation(); + if (!navigator.clipboard) { + NotificationMessage.ajouterNotification("Votre navigateur n'est pas compatible"); + } - if (estVictoire) this._victoirePanel.style.display = "block"; - else { - this._defaitePanelMot.innerText = motATrouver; - this._defaitePanel.style.display = "block"; + navigator.clipboard + .writeText(this._resumeTexte + "\n\nhttps://sutom.nocle.fr") + .then(() => { + NotificationMessage.ajouterNotification("Résumé copié dans le presse papier"); + }) + .catch((raison) => { + NotificationMessage.ajouterNotification("Votre navigateur n'est pas compatible"); + }); + }); + } + + public afficher(): void { + let titre: string; + let contenu: string; + if (!this._partieEstFinie) { + titre = "Statistiques"; + contenu = "Vous n'avez pas encore fini votre partie du jour"; + } else { + if (this._estVictoire) { + titre = "Félicitations"; + contenu = "

Bravo, tu as gagné. Reviens demain pour une nouvelle grille.

"; + } else { + titre = "Perdu"; + contenu = "

\ + Le mot a trouver était : " + this._motATrouver + "
\ + Peut être feras-tu mieux demain ? \ +

"; + } + contenu += + '

Résumé de ta partie Partager

\ +
' +
+        this._resumeTexte +
+        "
"; } + this._panelManager.setContenu(titre, contenu); + this._panelManager.setClasses(["fin-de-partie-panel"]); + if (this._partieEstFinie) this.attacherPartage(); + this._panelManager.afficherPanel(); } } diff --git a/ts/gestionnaire.ts b/ts/gestionnaire.ts index 32a0fb2..6d1648e 100644 --- a/ts/gestionnaire.ts +++ b/ts/gestionnaire.ts @@ -9,14 +9,20 @@ import SauvegardeStats from "./sauvegardeStats"; import Sauvegardeur from "./sauvegardeur"; import Configuration from "./configuration"; import PartieEnCours from "./partieEnCours"; +import PanelManager from "./panelManager"; +import ReglesPanel from "./reglesPanel"; +import ConfigurationPanel from "./configurationPanel"; export default class Gestionnaire { private readonly _dictionnaire: Dictionnaire; private readonly _grille: Grille; private readonly _input: Input; - private readonly _victoirePanel: FinDePartiePanel; + private readonly _reglesPanel: ReglesPanel; + private readonly _finDePartiePanel: FinDePartiePanel; + private readonly _configurationPanel: ConfigurationPanel; private readonly _propositions: Array; private readonly _resultats: Array>; + private readonly _panelManager: PanelManager; private _motATrouver: string; private _compositionMotATrouver: { [lettre: string]: number }; @@ -43,9 +49,14 @@ export default class Gestionnaire { this._propositions = new Array(); this._resultats = new Array>(); this._compositionMotATrouver = this.decompose(this._motATrouver); - this._victoirePanel = new FinDePartiePanel(this._datePartieEnCours); + this._panelManager = new PanelManager(); + this._reglesPanel = new ReglesPanel(this._panelManager); + this._finDePartiePanel = new FinDePartiePanel(this._datePartieEnCours, this._panelManager); + this._configurationPanel = new ConfigurationPanel(this._panelManager); this.chargerPropositions(partieEnCours.propositions); + + this.afficherReglesSiNecessaire(); } private chargerPartieEnCours(): PartieEnCours { @@ -110,19 +121,20 @@ export default class Gestionnaire { let isBonneReponse = resultats.every((item) => item.statut === LettreStatut.BienPlace); this._propositions.push(mot); this._resultats.push(resultats); + + if (isBonneReponse || this._propositions.length === this._maxNbPropositions) { + this._finDePartiePanel.genererResume(isBonneReponse, this._motATrouver, this._resultats); + this.enregistrerPartieDansStats(); + } + this._grille.validerMot(mot, resultats, isBonneReponse, skipAnimation, () => { this._input.updateClavier(resultats); if (isBonneReponse || this._propositions.length === this._maxNbPropositions) { this._input.bloquer(); - this._victoirePanel.afficher(isBonneReponse, this._motATrouver); + this._finDePartiePanel.afficher(); } }); - if (isBonneReponse || this._propositions.length === this._maxNbPropositions) { - this._victoirePanel.genererResume(isBonneReponse, this._resultats); - this.enregistrerPartieDansStats(); - } - this.sauvegarderPartieEnCours(); } @@ -170,4 +182,10 @@ export default class Gestionnaire { return resultats; } + + private afficherReglesSiNecessaire(): void { + if (this._config.afficherRegles !== undefined && !this._config.afficherRegles) return; + + this._reglesPanel.afficher(); + } } diff --git a/ts/panelManager.ts b/ts/panelManager.ts new file mode 100644 index 0000000..074c21e --- /dev/null +++ b/ts/panelManager.ts @@ -0,0 +1,66 @@ +export default class PanelManager { + private readonly _panelArea: HTMLElement; + private readonly _panelFenetre: HTMLElement; + private readonly _panelTitre: HTMLElement; + private readonly _panelContenu: HTMLElement; + private readonly _panelFermetureBouton: HTMLElement; + + public constructor() { + this._panelArea = document.getElementById("panel-area") as HTMLElement; + this._panelFenetre = document.getElementById("panel-fenetre") as HTMLElement; + this._panelTitre = document.getElementById("panel-fenetre-titre") as HTMLElement; + this._panelContenu = document.getElementById("panel-fenetre-contenu") as HTMLElement; + this._panelFermetureBouton = document.getElementById("panel-fenetre-bouton-fermeture") as HTMLElement; + + this._panelArea.addEventListener( + "click", + ((event: Event) => { + event.stopPropagation(); + this.cacherPanel(); + }).bind(this) + ); + + this._panelFenetre.addEventListener( + "click", + ((event: Event) => { + event.stopPropagation(); // On évite ainsi de fermer le panel en appuyant sur la fenêtre + }).bind(this) + ); + + this._panelFermetureBouton.addEventListener( + "click", + ((event: Event) => { + event.stopPropagation(); + this.cacherPanel(); + }).bind(this) + ); + } + + public afficherPanel(): void { + this._panelArea.style.display = "block"; + } + + public cacherPanel(): void { + this._panelArea.style.display = "none"; + } + + public setContenu(titre: string, contenu: string): void { + this._panelTitre.innerText = titre; + this._panelContenu.innerHTML = contenu; + } + + public setClasses(classes: Array): void { + this._panelArea.className = ""; + classes.forEach((nomClasse) => this._panelArea.classList.add(nomClasse)); + } + + public setCallbackFermeture(callback: () => void): void { + this._panelFermetureBouton.addEventListener( + "click", + ((event: Event) => { + callback(); + }).bind(this), + { once: true } + ); + } +} diff --git a/ts/reglesPanel.ts b/ts/reglesPanel.ts new file mode 100644 index 0000000..d4d6552 --- /dev/null +++ b/ts/reglesPanel.ts @@ -0,0 +1,47 @@ +import Configuration from "./configuration"; +import PanelManager from "./panelManager"; +import Sauvegardeur from "./sauvegardeur"; + +export default class ReglesPanel { + private readonly _panelManager: PanelManager; + private readonly _rulesBouton: HTMLElement; + + public constructor(panelManager: PanelManager) { + this._panelManager = panelManager; + this._rulesBouton = document.getElementById("configuration-regles-bouton") as HTMLElement; + + this._rulesBouton.addEventListener( + "click", + (() => { + this.afficher(); + }).bind(this) + ); + } + + public afficher(): void { + let titre = "Règles"; + let contenu = + '

\ + Vous avez six essais pour deviner le mot du jour.
\ + Vous ne pouvez proposer que des mots commençant par la même lettre que le mot recherché, et qui se trouvent dans notre dictionnaire.
\ + Les lettres entourées d\'un carré rouge sont bien placées,
\ + les lettres entourées d\'un cercle jaune sont mal placées (mais présentes dans le mot).
\ + Les lettres qui restent sur fond bleu ne sont pas dans le mot.
\ + Il y a un mot par jour, entre 6 et 9 lettres, et il est identique pour tout le monde. Évitez donc les spoils et privilégiez le bouton de partage.
\ + En cas de soucis, vous pouvez contacter @Jonamaths sur twitter. − \ + Page du projet
\ + Basé sur l\'excellent Wordle et le regretté Motus.
\ + Merci à Emmanuel pour l\'aide sur les mots à trouver, et à GaranceAmarante pour l\'aide sur le dictionnaire. \ +

'; + + this._panelManager.setContenu(titre, contenu); + this._panelManager.setClasses(["regles-panel"]); + this._panelManager.setCallbackFermeture(() => { + Sauvegardeur.sauvegarderConfig({ + ...(Sauvegardeur.chargerConfig() ?? Configuration.Default), + afficherRegles: false, + }); + }); + this._panelManager.afficherPanel(); + } +}