From f8bd1d35cda204e17fe487037c9f4742b39645d9 Mon Sep 17 00:00:00 2001 From: adrien <adrien@malingrey.fr> Date: Mon, 5 May 2025 02:49:57 +0200 Subject: [PATCH] TRIE --- Grille.php | 68 +++++++++++++++------------------- Trie.php | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++ dico.csv | 2 +- dico.php | 48 ++++++++---------------- index.php | 67 ++++++++++++++++++--------------- 5 files changed, 190 insertions(+), 101 deletions(-) create mode 100644 Trie.php diff --git a/Grille.php b/Grille.php index 5a6fb4d..d7c203b 100644 --- a/Grille.php +++ b/Grille.php @@ -1,8 +1,10 @@ <?php - include_once "dico.php"; +const MAX_ESSAIS = 0; + + class Grille implements Iterator, ArrayAccess { public $grille; public $hauteur; @@ -22,20 +24,7 @@ class Grille implements Iterator, ArrayAccess { $this->lignes = []; $this->colonnes = []; - $this->lettres_suivantes = []; - foreach ($hauteur == $largeur ? [$hauteur] : [$hauteur, $largeur] as $longueur) { - $this->lettres_suivantes[$longueur] = []; - foreach (mots_permutes($longueur) as $mots) { - $mot = implode(" ", $mots); - $ref = &$this->lettres_suivantes[$longueur]; - for ($i = 0; $i < $longueur; $i++) { - $lettre = $mot[$i]; - if (!isset($ref[$lettre])) $ref[$lettre] = []; - $ref = &$ref[$lettre]; - } - $ref = []; - } - } + $this->lettres_suivantes = tries(max($hauteur, $largeur)); $this->positions = []; for ($y = 0; $y < $hauteur; $y++) { @@ -45,7 +34,7 @@ class Grille implements Iterator, ArrayAccess { $this->nb_positions = count($this->positions); mt_srand($id == "" ? null : crc32($id)); - $this->grilles = $this->generateur(); + $this->grilles = $this->generateur(0); } public function get_ligne($y, $largeur) @@ -64,10 +53,8 @@ class Grille implements Iterator, ArrayAccess { return $colonne; } - public function generateur($i = 0) + public function generateur($i, $lettres_suivantes_ligne = NULL) { - global $dico; - if ($i == $this->nb_positions) { yield $this; return; @@ -75,42 +62,45 @@ class Grille implements Iterator, ArrayAccess { [$x, $y] = $this->positions[$i]; - $lettres_suivantes_ligne = $this->lettres_suivantes[$this->largeur]; - for ($x2 = 0; $x2 < $x; $x2++) - $lettres_suivantes_ligne = $lettres_suivantes_ligne[$this->grille[$y][$x2]]; + if ($x) { + $lettres_suivantes_ligne = $lettres_suivantes_ligne->noeud[$this->grille[$y][$x-1]]; + } else { + $lettres_suivantes_ligne = $this->lettres_suivantes[$this->largeur]; + } + $lettres_suivantes_colonne = $this->lettres_suivantes[$this->hauteur]; for ($y2 = 0; $y2 < $y; $y2++) - $lettres_suivantes_colonne = $lettres_suivantes_colonne[$this->grille[$y2][$x]]; - $lettres_communes = array_intersect_key( - $lettres_suivantes_ligne, - $lettres_suivantes_colonne + $lettres_suivantes_colonne = $lettres_suivantes_colonne->noeud[$this->grille[$y2][$x]]; + $lettres_communes = array_intersect( + array_keys($lettres_suivantes_ligne->noeud), + array_keys($lettres_suivantes_colonne->noeud) ); - uksort($lettres_communes, function ($a, $b) { + usort($lettres_communes, function ($a, $b) { return mt_rand(-1, 1); }); + if (MAX_ESSAIS) $lettres_communes = array_slice($lettres_communes, 0, MAX_ESSAIS); - foreach ($lettres_communes as $lettre => $_) { + foreach ($lettres_communes as $lettre) { $this->grille[$y][$x] = $lettre; $mots = []; if ($x == $this->largeur - 1) $mots = explode(" ", $this->get_ligne($y, $this->largeur)); else if ($lettre == " ") $mots = explode(" ", $this->get_ligne($y, $x)); - $mots = array_filter($mots, function($mot) { - return strlen($mot) > 1; + else $mots = []; + $this->lignes[$y] = array_filter($mots, function($mot) { + return strlen($mot) >= 2; }); - if (count($mots) >= 1) { - $dernier_mot = array_pop($mots); - $this->lignes[$y] = $mots; - if (in_array($dernier_mot, array_merge(...$this->lignes, ...$this->colonnes))) continue; - else $this->lignes[$y][] = $dernier_mot; + if (count($this->lignes[$y])) { + $mot = array_pop($this->lignes[$y]); + if (strlen($mot > 2) && in_array($mot, array_merge(...$this->lignes, ...$this->colonnes))) continue; + else $this->lignes[$y][] = $mot; } - if ($y == $this->hauteur - 1) { $mots = explode(" ", $this->get_colonne($x, $this->hauteur)); foreach ($mots as $rang => $mot) { - if (strlen($mot) <= 1) continue; - if (in_array($mot, array_merge(...$this->lignes, ...$this->colonnes))) continue 2; + if (strlen($mot) < 2) continue; + if (strlen($mot > 2) && in_array($mot, array_merge(...$this->lignes, ...$this->colonnes))) continue 2; else $this->colonnes[$x][$rang] = $mot; } } else { @@ -118,7 +108,7 @@ class Grille implements Iterator, ArrayAccess { } if ($i < $this->nb_positions) { - yield from $this->generateur($i + 1); + yield from $this->generateur($i + 1, $lettres_suivantes_ligne); } else { yield $this; } diff --git a/Trie.php b/Trie.php new file mode 100644 index 0000000..d5cf94d --- /dev/null +++ b/Trie.php @@ -0,0 +1,106 @@ +<?php + + +class Trie implements ArrayAccess, Countable //, Iterator +{ + public array $noeud = []; + private array $cles_en_cours = []; + private mixed $valeur_en_cours; + private $marcheur; + private $nb_branches = 0; + + // ArrayAccess + public function offsetExists($cles): bool { + if (!count($cles)) { + return false; + } + $cle = array_shift($cles); + if (count($cles)) { + return $this->noeud[$cle]->offsetExists($cles); + } else { + return isset($this->noeud[$cles]); + } + } + + public function offsetGet($cles): mixed { + if (!count($cles)) { + throw new \OutOfBoundsException("Liste de clés vide."); + } + $cle = array_shift($cles); + if (!isset($this->noeud[$cle])) $this->noeud[$cle] = new Trie(); + if (count($cles)) { + return $this->noeud[$cle]->offsetGet($cles); + } else { + return $this->noeud[$cle]; + } + } + + public function offsetSet($cles, $valeur): void { + if (!count($cles)) { + throw new \OutOfBoundsException("Liste de clés vide."); + return; + } + $cle = array_shift($cles); + if (!isset($this->noeud[$cle])) $this->noeud[$cle] = new Trie(); + if (count($cles)) { + $this->noeud[$cle]->offsetSet($cles, $valeur); + } else { + $this->noeud[$cle] = $valeur; + } + $this->nb_branches++; + } + + public function offsetUnset($cles): void { + if ($this->offsetExists(cles)) { + $cle = array_shift($cles); + if (count($cles)) { + $this->noeud[$cle]->offsetUnset($cles); + } else { + unset($this->noeud[$cle]); + } + $this->nb_branches--; + } + } + + // Countable + public function count(): int { + return $this->nb_branches; + } + +/* + // Iterator + public function marcheurs(): generator { + foreach ($this->noeud as $cle => $branche) { + if ($branche instanceof Trie) { + foreach($branche as $sous_cles => $feuille) { + $this->cles_en_cours = [$cle, ...$sous_cles]; + yield $feuille; + } + } else { + $this->cles_en_cours = [$cle]; + yield $branche; + } + } + } + + public function current(): mixed { + return $this->marcheur->current(); + } + + public function key(): array { + return $this->cles_en_cours; + } + + public function next(): void { + $this->marcheur->next(); + } + + public function rewind(): void { + $this->marcheur = $this->marcheurs(); + } + + public function valid(): bool { + return $this->marcheur->valid(); + } +*/ +} \ No newline at end of file diff --git a/dico.csv b/dico.csv index 7af37c4..b8c68fa 100644 --- a/dico.csv +++ b/dico.csv @@ -378,7 +378,7 @@ BECASSINE Une bonne partie du finistère Michel Laclos BEL Un vieux beau Georges Perec BEL Avant l'été, mais on ne peut en être sûr qu'après Pangloss BEL Ne peut jamais arriver avant le printemps, contrairement aux trois autres Pangloss -BELLE Sûrement pas la première venue, mais la dernière partie, çà c'est sûr Pangloss +BELLE Sûrement pas la première venue, mais la dernière partie, ça c'est sûr ! Pangloss BENIN Anodin sauf en afrique Jean Teularge BENIOUIOUI Apparemment, il consent à se marier à l'église Jacques Drillon BERET Complément populaire de la baguette Jean-Marie Lamy diff --git a/dico.php b/dico.php index 6722b9c..d46dd73 100644 --- a/dico.php +++ b/dico.php @@ -1,8 +1,10 @@ <?php +include_once "Trie.php"; -const MIN_LETTRES_MOT_1 = 2; -const MIN_LETTRES_MOT_2 = 1; +const MIN_PREMIER_MOT = 1; +const MIN_MOTS_SUIVANTS = 1; + $dico = [[""]]; if (($lecteur = fopen("dico.csv", "r")) !== FALSE) { @@ -31,39 +33,21 @@ if (($lecteur = fopen("dico.csv", "r")) !== FALSE) { fclose($lecteur); } -function mots_espaces($longueur) -{ +function tries($longueur_max) { global $dico; - foreach ($dico[$longueur] as $mot => $definition) { - yield [$mot]; - } - for ($i = MIN_LETTRES_MOT_1; ($j = $longueur - $i - 1) >= MIN_LETTRES_MOT_2; $i++) { - foreach ($dico[$i] as $mot => $definition) { - foreach (mots_espaces($j) as $mots) { - if (!in_array($mot, $mots)) { - yield [$mot, ...$mots]; - } + $_tries = [[]]; + for ($longueur = 1; $longueur <= $longueur_max; $longueur++) { + $_tries[$longueur] = new Trie(); + foreach ($dico[$longueur] as $mot => $definition) { + $_tries[$longueur][str_split($mot)] = []; + } + for ($position_espace = MIN_PREMIER_MOT; $position_espace + MIN_MOTS_SUIVANTS < $longueur; $position_espace++) { + $mots_suivants = $_tries[$longueur - $position_espace - 1]; + foreach ($dico[$position_espace] as $premier_mot => $definition) { + $_tries[$longueur][str_split($premier_mot . " ")] = $mots_suivants; } } } -} - -function permutations(array $elements) -{ - if (count($elements) <= 1) { - yield $elements; - } else { - foreach (permutations(array_slice($elements, 1)) as $permutation) { - foreach (range(0, count($elements) - 1) as $i) { - yield [...array_slice($permutation, 0, $i), $elements[0], ...array_slice($permutation, $i)]; - } - } - } -} - -function mots_permutes($longueur) { - foreach (mots_espaces($longueur) as $mots) { - yield from permutations($mots); - } + return $_tries; } \ No newline at end of file diff --git a/index.php b/index.php index 4e0238d..cd7b4d8 100644 --- a/index.php +++ b/index.php @@ -1,13 +1,21 @@ <?php + +if (!isset($_GET["grille"])) { + $_GET["grille"] = uniqid(); + header("Location: " . dirname($_SERVER['DOCUMENT_URI']) . "?" . http_build_query($_GET)); + exit; +} + + include_once "dico.php"; include_once "Grille.php"; -const HAUTEUR_DEFAUT = 7; +const HAUTEUR_DEFAUT = 6; const HAUTEUR_MIN = 2; const HAUTEUR_MAX = 10; -const LARGEUR_DEFAUT = 7; +const LARGEUR_DEFAUT = 6; const LARGEUR_MIN = 2; const LARGEUR_MAX = 10; @@ -19,6 +27,7 @@ $hauteur = filter_input(INPUT_GET, 'lignes', FILTER_VALIDATE_INT, [ "max_range" => HAUTEUR_MAX ] ]); + $largeur = filter_input(INPUT_GET, 'colonnes', FILTER_VALIDATE_INT, [ "options" => [ "default" => LARGEUR_DEFAUT, @@ -27,11 +36,7 @@ $largeur = filter_input(INPUT_GET, 'colonnes', FILTER_VALIDATE_INT, [ ] ]); -if (isset($_GET["grille"])) { - $id = htmlspecialchars($_GET["grille"]); -} else { - $id = uniqid(); -} +$id = htmlspecialchars($_GET["grille"]); $grille = new Grille($hauteur, $largeur, $id); $grille->current(); @@ -44,9 +49,9 @@ if ($grille->valid()) { } $definitions_horizontales = []; - foreach ($grille->lignes as $y => $mots) { + for ($y = 0; $y < $hauteur; $y++) { $definitions_horizontales[$y] = []; - foreach ($mots as $mot) { + foreach ($grille->lignes[$y] as $mot) { $definitions = $dico[strlen($mot)][$mot]; if (count($definitions)) { $definitions_horizontales[$y][] = $definitions[mt_rand(0, count($definitions) - 1)]; @@ -54,9 +59,9 @@ if ($grille->valid()) { } } $definitions_verticales = []; - foreach ($grille->colonnes as $x => $mots) { + for ($x = 0 ; $x < $largeur; $x++) { $definitions_verticales[$x] = []; - foreach ($mots as $mot) { + foreach ($grille->colonnes[$x] as $mot) { $definitions = $dico[strlen($mot)][$mot]; if (count($definitions)) { $definitions_verticales[$x][] = $definitions[mt_rand(0, count($definitions) - 1)]; @@ -70,7 +75,7 @@ if ($grille->valid()) { <head> <meta charset="utf-8"> - <title>MOTS CROISÉS</title> + <title>⬜⬜⬜⬜⬛⬜⬜⬜⬜⬜⬜⬜</title> <link rel="stylesheet" href="style.css"> <link rel="icon" href="favicon.svg"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> @@ -128,7 +133,7 @@ if ($grille->valid()) { <?php else: ?> <td class="case blanche"> <input id="<?= chr($x + 65) . ($y + 1) ?>" type="text" maxlength="1" size="1" pattern="[A-Z]" placeholder="<?= $grille[$y][$x] ?>" - title="<?= "→ " . strip_tags(implode("\n→ ", $definitions_horizontales[$y])) . "\n↓ " . strip_tags(implode("\n↓ ", $definitions_verticales[$x])) ?>" /> + title="<?= strip_tags("→ " . implode("\n→ ", $definitions_horizontales[$y]) . "\n↓ " . implode("\n↓ ", $definitions_verticales[$x])) ?>" /> </td> <?php endif; ?> <?php endfor; ?> @@ -141,14 +146,16 @@ if ($grille->valid()) { <ol type="1"> <?php foreach ($definitions_horizontales as $y => $definitions): ?> <li> - <?php if (count($definitions) == 1): ?> - <?= $definitions[0] ?> - <?php else: ?> - <ol> - <?php foreach ($definitions as $definition) : ?> - <li><?= $definition ?></li> - <?php endforeach ?> - </ol> + <?php if (count($definitions)): ?> + <?php if (count($definitions) == 1): ?> + <?= $definitions[0] ?> + <?php else: ?> + <ol> + <?php foreach ($definitions as $definition) : ?> + <li><?= $definition ?></li> + <?php endforeach ?> + </ol> + <?php endif ?> <?php endif ?> </li> <?php endforeach; ?> @@ -159,14 +166,16 @@ if ($grille->valid()) { <ol type="A"> <?php foreach ($definitions_verticales as $x => $definitions): ?> <li> - <?php if (count($definitions) == 1): ?> - <?= $definitions[0] ?> - <?php else: ?> - <ol> - <?php foreach ($definitions as $definition) : ?> - <li><?= $definition ?></li> - <?php endforeach ?> - </ol> + <?php if (count($definitions)): ?> + <?php if (count($definitions) == 1): ?> + <?= $definitions[0] ?> + <?php else: ?> + <ol> + <?php foreach ($definitions as $definition) : ?> + <li><?= $definition ?></li> + <?php endforeach ?> + </ol> + <?php endif ?> <?php endif ?> </li> <?php endforeach; ?>