Compare commits

..

44 Commits

Author SHA1 Message Date
13fcdf05fb grand dico 2025-05-08 01:47:58 +02:00
ca39709fbd nombre de mots 2025-05-07 23:54:00 +02:00
ddebd453df interface trie avec des strings 2025-05-07 21:21:48 +02:00
d5a120cd9a omettre les mots plus grands que la grille 2025-05-07 19:38:04 +02:00
ab9e1f08ef Tries everywhere! 2025-05-07 18:34:08 +02:00
b1f3e8b85f omission des lettres isolées 2025-05-07 00:16:55 +02:00
c20d2324e9 FAST & FURIOUS 2025-05-06 17:44:16 +02:00
65bb8be2e3 Revert "Merge branch 'tmp3'"
This reverts commit ed1ad0566f, reversing
changes made to 6ee909914d.
2025-05-06 17:29:51 +02:00
ed1ad0566f Merge branch 'tmp3' 2025-05-06 17:27:47 +02:00
47be3d2e51 FAST & FURIOUS 2025-05-06 16:56:59 +02:00
6ee909914d WIP 2025-05-06 15:55:58 +02:00
4c014bc789 variable inutilisée 2025-05-06 08:47:10 +02:00
b063d39689 Merge branch 'tmp2' 2025-05-06 02:43:01 +02:00
d150e76a9e fix id vide 2025-05-06 02:38:18 +02:00
a6b15fbb87 fix 2025-05-06 02:15:13 +02:00
afd79eb3a2 utiliser session_id comme id 2025-05-06 01:40:53 +02:00
ea4555144c sans cookies 2025-05-05 22:41:55 +02:00
1a35d8e920 sauvegarde 2025-05-05 20:02:56 +02:00
7ae6506539 WIP 2025-05-05 16:57:29 +02:00
f8bd1d35cd TRIE 2025-05-05 02:49:57 +02:00
23cac370a1 conserver la numérotation 2025-05-04 13:57:08 +02:00
dc488aea4d renvoyer un numéro de grille seulement si elle est valide 2025-05-03 14:48:54 +02:00
91db11cc2d permutations 2025-05-03 14:14:56 +02:00
4a19252a94 petites modifs 2025-05-03 12:59:29 +02:00
ca95b76558 ne vérifier les doublons que sur le dernier mot de la ligne 2025-05-03 12:43:11 +02:00
97b9766db3 optimisation 2025-05-03 02:40:38 +02:00
168fc5f7a2 moins de comparaisons 2025-05-03 02:36:46 +02:00
0dd9881f6d correction OFFLINE_URL 2025-05-03 02:25:55 +02:00
32db8f5a6c 2025-05-03 02:21:25 +02:00
9f3a90a04e 2025-05-03 02:09:16 +02:00
73e8d6a857 ça marche 2025-05-03 01:18:33 +02:00
7a245d601a suppression melanger_valeurs 2025-05-02 16:23:39 +02:00
e1eb7685e4 suppression des points quand une seule phrase 2025-05-02 16:23:29 +02:00
10ea7a7f0b fix 2025-05-02 15:15:47 +02:00
63cd243e91 Merge branch 'tmp' 2025-05-02 11:55:23 +02:00
c097be27d8 style 2025-05-02 11:50:08 +02:00
399370624c 7 2025-05-02 04:32:32 +02:00
911da7aef9 ajouts 2025-05-02 04:30:46 +02:00
1987905eb9 annulation de l'autorisation des doublons 2025-05-02 04:08:38 +02:00
2214cf4a05 épurage 2025-05-02 03:55:38 +02:00
5997397908 retour de la limite de mots 2025-05-02 03:34:07 +02:00
b85bf7eb99 corrections 2025-05-02 03:27:01 +02:00
4a1106fcba autoriser autant de doublons que de définitions,
suppression de la limite de mots
2025-05-02 03:06:18 +02:00
d867e40499 mots_espace renvoie une liste de mots 2025-05-02 00:47:00 +02:00
9 changed files with 974138 additions and 3390 deletions

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
__pycache__/ __pycache__/
test*.* test*.*
*.py
dico2.csv
wiktionaryXfr2010.7z

View File

@ -1,189 +1,228 @@
<?php <?php
include_once "dico.php"; include_once "dico.php";
function melanger_cles($tableau) $randmax = mt_getrandmax() + 1;
{ function gaussienne($moyenne = 0, $ecartType = 1.0): float {
uksort($tableau, function ($a, $b) { global $randmax;
return mt_rand(-1, 1);
}); $u = 0;
return $tableau; $v = 0;
$u = (mt_rand() + 1) / $randmax;
$v = (mt_rand() + 1) / $randmax;
$z = sqrt(-2.0 * log($u)) * cos(2.0 * M_PI * $v);
return $z * $ecartType + $moyenne;
} }
class Grille implements Iterator, ArrayAccess { class Grille implements ArrayAccess
{
public $grille; public $grille;
public $hauteur; public $hauteur;
public $largeur; public $largeur;
private $grilles; public $dico;
private $lettres_suivantes;
private $positions; private $positions;
private $nb_positions; private $nb_positions;
public $lignes; public $lignes = [];
public $colonnes; public $colonnes = [];
public $valide = false;
private $id;
public function __construct($hauteur, $largeur, $id = "") public function __construct($hauteur, $largeur)
{ {
$this->hauteur = $hauteur; $this->hauteur = $hauteur;
$this->largeur = $largeur; $this->largeur = $largeur;
$this->grille = array_fill(0, $hauteur, array_fill(0, $largeur, '')); $this->grille = array_fill(0, $hauteur, array_fill(0, $largeur, ''));
$this->lignes = [];
$this->colonnes = [];
$this->lettres_suivantes = [];
foreach ($hauteur == $largeur ? [$hauteur] : [$hauteur, $largeur] as $longueur) {
$this->lettres_suivantes[$longueur] = [];
foreach (mots_espaces($longueur, $hauteur == $largeur ? MAX_MOTS : MAX_MOTS/2) as $mot) {
$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->positions = []; $this->positions = [];
for ($y = 0; $y < $hauteur; $y++) { for ($y = 0; $y < $hauteur; $y++) {
for ($x = 0; $x < $largeur; $x++) { for ($x = 0; $x < $largeur; $x++)
$this->positions[] = [$x, $y]; $this->positions[] = [$x, $y];
}
} }
$this->nb_positions = count($this->positions); $this->nb_positions = count($this->positions);
mt_srand($id == "" ? null : crc32($id)); $this->dico = mots_espaces(max($hauteur, $largeur));
$this->grilles = $this->generateur();
} }
public function get_ligne($y, $largeur) public function get_ligne($y, $largeur)
{ {
$ligne = ""; $ligne = "";
for ($x = 0; $x < $largeur; $x++) { for ($x = 0; $x < $largeur; $x++)
$ligne .= $this->grille[$y][$x]; $ligne .= $this->grille[$y][$x];
}
return $ligne; return $ligne;
} }
public function get_colonne($x, $hauteur) public function get_colonne($x, $hauteur)
{ {
$colonne = ""; $colonne = "";
for ($y = 0; $y < $hauteur; $y++) { for ($y = 0; $y < $hauteur; $y++)
$colonne .= $this->grille[$y][$x]; $colonne .= $this->grille[$y][$x];
}
return $colonne; return $colonne;
} }
public function generateur($i = 0) public function gen_grilles($i = 0, $lettres_ligne = NULL)
{ {
if ($i == $this->nb_positions) {
yield $this;
return;
}
[$x, $y] = $this->positions[$i]; [$x, $y] = $this->positions[$i];
$lettres_suivantes_ligne = $this->lettres_suivantes[$this->largeur]; // Recherche de la prochaine lettre possible sur la case courante
for ($x2 = 0; $x2 < $x; $x2++) { // en ligne
$lettres_suivantes_ligne = $lettres_suivantes_ligne[$this->grille[$y][$x2]]; if ($x == 0) {
$lettres_ligne = $this->dico[$this->largeur];
} }
$lettres_suivantes_colonne = $this->lettres_suivantes[$this->hauteur];
// en colonne
$lettres_colonne = $this->dico[$this->hauteur];
for ($y2 = 0; $y2 < $y; $y2++) { for ($y2 = 0; $y2 < $y; $y2++) {
$lettres_suivantes_colonne = $lettres_suivantes_colonne[$this->grille[$y2][$x]]; $lettres_colonne = $lettres_colonne->branches[$this->grille[$y2][$x]];
} }
$lettres_communes = melanger_cles(array_intersect_key( $lettres_communes = array_intersect_key(
$lettres_suivantes_ligne, $lettres_ligne->branches,
$lettres_suivantes_colonne $lettres_colonne->branches
)); );
foreach ($lettres_communes as $lettre => $_) {
$lettres_communes[$lettre] = count($lettres_ligne->branches[$lettre]) * count($lettres_colonne->branches[$lettre]) * gaussienne(1, 5);
}
uksort($lettres_communes, function($a, $b) use ($lettres_communes) {
return $lettres_communes[$b] <=> $lettres_communes[$a];
});
$lettres_communes = array_slice($lettres_communes, 0, 3);
foreach ($lettres_communes as $lettre => $_) { foreach ($lettres_communes as $lettre => $_) {
$this->grille[$y][$x] = $lettre; $this->grille[$y][$x] = $lettre;
if ($x == $this->largeur - 1) { // Omission des lettres isolées
$mots_ligne = explode(" ", $this->get_ligne($y, $this->largeur)); if ($lettre == " "
foreach ($mots_ligne as $mot_ligne) { && ($y - 2 < 0 || $this->grille[$y - 2][$x] == " ")
if (in_array($mot_ligne, array_merge(...$this->lignes, ...$this->colonnes))) { && ($y - 1 < 0 || $x - 1 < 0 || $this->grille[$y - 1][$x - 1] == " ")
continue 2; && ($y - 1 < 0 || $x + 1 >= $this->largeur || $this->grille[$y - 1][$x + 1] == " ")
} ) {
if (!isset($this->lignes[$y])) { continue;
$this->lignes[$y] = [];
}
$this->lignes[$y][] = $mot_ligne;
}
} else {
unset($this->lignes[$y]);
}
if ($y == $this->hauteur - 1) {
$mots_colonne = explode(" ", $this->get_colonne($x, $this->hauteur));
foreach ($mots_colonne as $mot_colonne) {
if (in_array($mot_colonne, array_merge(...$this->lignes, ...$this->colonnes))) {
continue 2;
}
if (!isset($this->lignes[$x])) {
$this->colonnes[$x] = [];
}
$this->colonnes[$x][] = $mot_colonne;
}
} else {
unset($this->colonnes[$x]);
} }
if ($i < $this->nb_positions) { // Omission des doublons
yield from $this->generateur($i + 1); $mots = [];
if ($x == $this->largeur - 1) $mots = explode(" ", $this->get_ligne($y, $this->largeur));
else if ($lettre == " ") $mots = explode(" ", $this->get_ligne($y, $x));
else $mots = [];
$this->lignes[$y] = array_filter($mots, function ($mot) {
return strlen($mot) >= 2;
});
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) < 2) continue;
if (strlen($mot > 2) && in_array($mot, array_merge(...$this->lignes, ...$this->colonnes))) continue 2;
else $this->colonnes[$x][$rang] = $mot;
}
} else {
$this->colonnes[$x] = [];
}
if ($i < $this->nb_positions - 1) {
yield from $this->gen_grilles($i + 1, $lettres_ligne->branches[$lettre]);
} else { } else {
yield $this; yield $this;
} }
} }
} }
public function genere($id)
{
mt_srand(crc32($id));
$grilles = $this->gen_grilles();
$grilles->current();
if ($grilles->valid()) {
$this->save($id);
return true;
} else {
return false;
}
}
public function hash() public function hash()
{ {
$string = ""; $string = "";
foreach ($this->grille as $ligne) { foreach ($this->grille as $ligne)
$string .= implode("", $ligne); $string .= implode("", $ligne);
}
return hash('sha256', $string); return hash('sha256', $string);
} }
public function current(): mixed public function save($id)
{ {
return $this->grilles->current(); session_id($id);
session_start(["use_cookies" => false]);
$_SESSION["$this->largeur,$this->hauteur"] = implode(
"",
array_map(
function ($ligne) {
return implode("", $ligne);
},
$this->grille
)
);
} }
public function key(): mixed { public function load($id)
return $this->grilles->key();
}
public function next(): void {
$this->grilles->next();
}
public function rewind(): void {
$this->grilles->rewind();
}
public function valid(): bool
{ {
return $this->grilles->valid(); session_id($id);
session_start(["use_cookies" => false]);
if (!isset($_SESSION["$this->largeur,$this->hauteur"])) {
return false;
}
foreach (str_split($_SESSION["$this->largeur,$this->hauteur"], $this->largeur) as $y => $ligne) {
foreach (str_split($ligne) as $x => $lettre) {
$this->grille[$y][$x] = $lettre;
}
}
for ($y = 0; $y < $this->hauteur; $y++) {
$mots = explode(" ", $this->get_ligne($y, $this->largeur));
$this->lignes[$y] = array_filter($mots, function ($mot) {
return strlen($mot) >= 2;
});
}
for ($x = 0; $x < $this->largeur; $x++) {
$mots = explode(" ", $this->get_colonne($x, $this->hauteur));
$this->colonnes[$x] = array_filter($mots, function ($mot) {
return strlen($mot) >= 2;
});
}
return true;
} }
public function offsetExists(mixed $offset): bool {
public function offsetExists(mixed $offset): bool
{
return isset($this->grille[$offset]); return isset($this->grille[$offset]);
} }
public function offsetGet(mixed $offset): mixed { public function offsetGet(mixed $offset): mixed
{
return $this->grille[$offset]; return $this->grille[$offset];
} }
public function offsetSet(mixed $offset, mixed $value): void { public function offsetSet(mixed $offset, mixed $value): void
{
$this->grille[$offset] = $value; $this->grille[$offset] = $value;
} }
public function offsetUnset(mixed $offset): void { public function offsetUnset(mixed $offset): void
{
unset($this->grille[$offset]); unset($this->grille[$offset]);
} }
} }

95
Trie.php Normal file
View File

@ -0,0 +1,95 @@
<?php
class Trie implements ArrayAccess, IteratorAggregate, Countable {
public array $branches = [];
private $nb_branches = 0;
public function arraySet($cles, $valeur) {
$this->nb_branches++;
$cle = $cles[0];
$cles = array_slice($cles, 1);
if ($cles == []) {
$this->branches[$cle] = $valeur;
} else {
if (!isset($this->branches[$cle])) $this->branches[$cle] = new Trie();
$this->branches[$cle]->arraySet($cles, $valeur);
}
}
public function arrayExists($cles) {
$cle = $cles[0];
$cles = array_slice($cles, 1);
if ($cles == []) {
return isset($this->branches[$cle]);
} else {
return isset($this->branches[$cle]) && $this->branches[$cle]->arrayExists($cles);
}
}
public function &arrayGet($cles) {
$cle = $cles[0];
$cles = array_slice($cles, 1);
if ($cles == []) {
return $this->branches[$cle];
} else {
return $this->branches[$cle]->arrayGet($cles);
}
}
public function arrayUnset($cles) {
$cle = $cles[0];
$cles = array_slice($cles, 1);
if ($cles == []) {
unset($this->branches[$cle]);
$this->nb_branches--;
} else {
$this->branches[$cle]->arrayUnset($cles);
$this->nb_branches--;
if (count($this->branches[$cle]) == 0) {
unset($this->branches[$cle]);
}
}
}
public function arrayIterator() {
foreach ($this->branches as $cle => $branche) {
if ($branche instanceof Trie) {
foreach($branche->arrayIterator() as $sous_cles => $feuille) {
yield array_merge([$cle], $sous_cles) => $feuille;
}
} else {
yield [$cle] => $branche;
}
}
}
// ArrayAccess
public function offsetSet($string, $valeur): void {
$this->arraySet(str_split($string), $valeur);
}
public function offsetExists($string): bool {
return $this->arrayExists(str_split($string));
}
public function &offsetGet($string): mixed {
return $this->arrayGet(str_split($string));
}
public function offsetUnset($string): void {
$this->arrayUnset(str_split($string));
}
// IteratorAggregate
public function getIterator(): Traversable {
foreach($this->arrayIterator() as $array => $valeur) {
yield implode("", $array) => $valeur;
}
}
// Countable
public function count(): int {
return $this->nb_branches;
}
}

976698
dico.csv

File diff suppressed because one or more lines are too long

114
dico.php
View File

@ -1,72 +1,66 @@
<?php <?php
include_once "Trie.php";
const MIN_LETTRES_MOT_1 = 2; const MIN_PREMIER_MOT = 1;
const MIN_LETTRES_MOT_2 = 0; const MIN_MOTS_SUIVANTS = 1;
const MAX_MOTS = 1000000;
$dico = [[]];
if (($lecteur = fopen("dico.csv", "r")) !== FALSE) { $nb_mots = 0;
$header = fgetcsv($lecteur, 0, "\t");
while (($ligne = fgetcsv($lecteur, 0, "\t")) !== FALSE) { function dico($longueur_max) {
if ($ligne[0] == NULL || substr($ligne[0], 0, 1) == "#") { global $nb_mots;
continue;
} $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: Upper(); :: NFC;', Transliterator::FORWARD);
switch(count($ligne)) {
case 1: $dico = [[""]];
[$mot] = $ligne; for ($longueur = 0; $longueur <= $longueur_max; $longueur++) {
$definition = ""; $dico[] = new Trie();
break;
case 2:
[$mot, $definition] = $ligne;
break;
case 3:
[$mot, $definition, $auteur] = $ligne;
$definition .= " <small><em>$auteur</em></small>";
break;
}
$mot = strtoupper($mot);
$longueur = strlen($mot);
if (!isset($dico[$longueur])) {
$dico[$longueur] = [];
}
if (!isset($dico[$longueur][$mot])) {
$dico[$longueur][$mot] = [];
}
if (strlen($definition)) {
$dico[$longueur][$mot][] = $definition;
}
} }
fclose($lecteur); if (($lecteur = fopen("dico.csv", "r")) !== FALSE) {
} $entete = fgetcsv($lecteur, 0, "\t");
foreach ($dico as $longueur => $mots) { while (($ligne = fgetcsv($lecteur, 0, "\t")) !== FALSE) {
foreach ($mots as $mot => $definitions) { if (
if (count($definitions)) { $ligne[0] == NULL
$dico[$longueur][$mot] = $definitions[array_rand($definitions)]; || substr($ligne[0], 0, 1) == "#"
} else { || strlen($ligne[0]) > $longueur_max
$dico[$longueur][$mot] = ""; ) continue;
}
}
}
function mots_espaces($longueur, $nb_mots_restants=MAX_MOTS) $mot = $ligne[0];
{ $definitions = array_slice($ligne, 1);
global $dico; $mot = str_replace("-", " ", $mot);
foreach ($dico[$longueur] as $mot => $definition) { $mot = $transliterator->transliterate($mot);
yield $mot; if (strpos($mot, " ") !== false) {
if (--$nb_mots_restants <= 0) return; $mots = explode(" ", $mot);
} $nb_mots = count($mots);
for ($i = MIN_LETTRES_MOT_1; $longueur - $i - 1 >= MIN_LETTRES_MOT_2; $i++) { $mot = implode("", $mots);
foreach ($dico[$i] as $mot1 => $definition) { foreach($definitions as $i => $definition) {
foreach (mots_espaces($longueur - $i - 1, $nb_mots_restants) as $mot2) { $definitions[$i] .= " ($nb_mots mots)";
if ($mot1 != $mot2) {
yield "$mot1 $mot2";
if (--$nb_mots_restants <= 0) return;
yield "$mot2 $mot1";
if (--$nb_mots_restants <= 0) return;
} }
} }
$longueur = strlen($mot);
$dico[$longueur][$mot] = $definitions;
}
fclose($lecteur);
}
return $dico;
}
function mots_espaces($longueur_max) {
global $nb_mots;
$dico = dico($longueur_max);
for ($longueur = 1; $longueur <= $longueur_max; $longueur++) {
for ($position_espace = MIN_PREMIER_MOT; $position_espace + MIN_MOTS_SUIVANTS < $longueur; $position_espace++) {
$mots_suivants = $dico[$longueur - $position_espace - 1];
foreach ($dico[$position_espace]->arrayIterator() as $premier_mot => $definition) {
$premier_mot[] = " ";
$dico[$longueur]->arraySet($premier_mot, $mots_suivants);
}
} }
} }
return $dico;
} }

148
index.php
View File

@ -1,21 +1,11 @@
<?php <?php
if (!isset($_GET["grille"])) {
$_GET["grille"] = uniqid();
header("Location: " . dirname($_SERVER['DOCUMENT_URI']) . "?" . http_build_query($_GET));
exit;
} else {
$id = htmlspecialchars($_GET["grille"]);
}
include_once "dico.php";
include_once "Grille.php"; include_once "Grille.php";
const HAUTEUR_DEFAUT = 7;
const HAUTEUR_DEFAUT = 8;
const HAUTEUR_MIN = 2; const HAUTEUR_MIN = 2;
const HAUTEUR_MAX = 10; const HAUTEUR_MAX = 10;
const LARGEUR_DEFAUT = 7; const LARGEUR_DEFAUT = 8;
const LARGEUR_MIN = 2; const LARGEUR_MIN = 2;
const LARGEUR_MAX = 10; const LARGEUR_MAX = 10;
@ -27,6 +17,7 @@ $hauteur = filter_input(INPUT_GET, 'lignes', FILTER_VALIDATE_INT, [
"max_range" => HAUTEUR_MAX "max_range" => HAUTEUR_MAX
] ]
]); ]);
$largeur = filter_input(INPUT_GET, 'colonnes', FILTER_VALIDATE_INT, [ $largeur = filter_input(INPUT_GET, 'colonnes', FILTER_VALIDATE_INT, [
"options" => [ "options" => [
"default" => LARGEUR_DEFAUT, "default" => LARGEUR_DEFAUT,
@ -35,27 +26,42 @@ $largeur = filter_input(INPUT_GET, 'colonnes', FILTER_VALIDATE_INT, [
] ]
]); ]);
$grille = new Grille($hauteur, $largeur, $id); $grille = new Grille($hauteur, $largeur);
$grille->current();
$definitions = [ if (!isset($_GET["grille"]) || $_GET["grille"] == "") {
"lignes" => [], do {
"colonnes" => [] $id = uniqid();
]; } while (!$grille->genere($id));
foreach ($grille->lignes as $y => $mots) {
$definitions["lignes"][$y] = []; $_GET["grille"] = $id;
foreach ($mots as $mot) { header("Location: " . dirname($_SERVER['DOCUMENT_URI']) . "?" . http_build_query($_GET));
$definition = $dico[strlen($mot)][$mot]; exit;
if ($dico[strlen($mot)][$mot] != "") { }
$definitions["lignes"][$y][] = $definition;
$id = htmlspecialchars($_GET["grille"]);
$grille_valide = $grille->load($id) || $grille->genere($id);
mt_srand(crc32($id));
if ($grille_valide) {
$definitions_horizontales = [];
for ($y = 0; $y < $hauteur; $y++) {
$definitions_horizontales[$y] = [];
foreach ($grille->lignes[$y] as $mot) {
$definitions = $grille->dico[strlen($mot)][$mot];
if (count($definitions)) {
$definitions_horizontales[$y][] = $definitions[mt_rand(0, count($definitions) - 1)];
}
} }
} }
} $definitions_verticales = [];
foreach ($grille->colonnes as $x => $mots) { for ($x = 0 ; $x < $largeur; $x++) {
$definitions["colonnes"][$x] = []; $definitions_verticales[$x] = [];
foreach ($mots as $mot) { foreach ($grille->colonnes[$x] as $mot) {
$definition = $dico[strlen($mot)][$mot]; $definitions = $grille->dico[strlen($mot)][$mot];
if ($dico[strlen($mot)][$mot] != "") { if (count($definitions)) {
$definitions["colonnes"][$x][] = $definition; $definitions_verticales[$x][] = $definitions[mt_rand(0, count($definitions) - 1)];
}
} }
} }
} }
@ -65,7 +71,7 @@ foreach ($grille->colonnes as $x => $mots) {
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Mots croisés</title> <title>MOTS■CROISES</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<link rel="icon" href="favicon.svg"> <link rel="icon" href="favicon.svg">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -100,9 +106,9 @@ foreach ($grille->colonnes as $x => $mots) {
</tbody> </tbody>
</table> </table>
</h1> </h1>
<h1 class="small width">Mots croisés</h1> <h1 class="small width">Motscroisés</h1>
<div class="grille-et-definitions"> <div class="grille-et-definitions">
<?php if ($grille->valid()): ?> <?php if ($grille_valide): ?>
<div class="grille"> <div class="grille">
<table> <table>
<tr> <tr>
@ -116,14 +122,16 @@ foreach ($grille->colonnes as $x => $mots) {
<tr> <tr>
<th><?= $y + 1 ?></th> <th><?= $y + 1 ?></th>
<?php for ($x = 0; $x < $largeur; $x++): ?> <?php for ($x = 0; $x < $largeur; $x++): ?>
<td class="case <?= $grille[$y][$x] == " " ? "noire" : "blanche" ?>"> <?php if ($grille[$y][$x] == " "): ?>
<?php if ($grille[$y][$x] == " "): ?> <td class="case noire">
<input id="<?= chr($x + 65) . ($y + 1) ?>" type="text" maxlength="1" size="1" value=" " disabled /> <input id="<?= chr($x + 65) . ($y + 1) ?>" type="text" maxlength="1" size="1" value=" " disabled />
<?php else: ?> </td>
<?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] ?>" <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["lignes"][$y])) . "\n↓ " . strip_tags(implode("\n↓ ", $definitions["colonnes"][$x])) ?>" /> title="<?= strip_tags("→ " . implode("\n→ ", $definitions_horizontales[$y]) . "\n↓ " . implode("\n↓ ", $definitions_verticales[$x])) ?>" />
<?php endif; ?> </td>
</td> <?php endif; ?>
<?php endfor; ?> <?php endfor; ?>
</tr> </tr>
<?php endfor; ?> <?php endfor; ?>
@ -131,17 +139,19 @@ foreach ($grille->colonnes as $x => $mots) {
</div> </div>
<div class="definitions horizontales"> <div class="definitions horizontales">
<h2>Horizontalement</h2> <h2>Horizontalement</h2>
<ol> <ol type="1">
<?php foreach ($definitions["lignes"] as $y => $definitions_ligne): ?> <?php foreach ($definitions_horizontales as $y => $definitions): ?>
<li> <li>
<?php if (count($definitions_ligne) == 1): ?> <?php if (count($definitions)): ?>
<?= $definitions_ligne[0] ?> <?php if (count($definitions) == 1): ?>
<?php else: ?> <?= $definitions[0] ?>
<ol> <?php else: ?>
<?php foreach ($definitions_ligne as $definition) : ?> <ol>
<li><?= $definition ?></li> <?php foreach ($definitions as $definition) : ?>
<?php endforeach ?> <li><?= $definition ?></li>
</ol> <?php endforeach ?>
</ol>
<?php endif ?>
<?php endif ?> <?php endif ?>
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
@ -150,39 +160,41 @@ foreach ($grille->colonnes as $x => $mots) {
<div class="definitions verticales"> <div class="definitions verticales">
<h2>Verticalement</h2> <h2>Verticalement</h2>
<ol type="A"> <ol type="A">
<?php foreach ($definitions["colonnes"] as $x => $definitions_colonne): ?> <?php foreach ($definitions_verticales as $x => $definitions): ?>
<li> <li>
<?php if (count($definitions_colonne) == 1): ?> <?php if (count($definitions)): ?>
<?= $definitions_colonne[0] ?> <?php if (count($definitions) == 1): ?>
<?php else: ?> <?= $definitions[0] ?>
<ol> <?php else: ?>
<?php foreach ($definitions_colonne as $definition) : ?> <ol>
<li><?= $definition ?></li> <?php foreach ($definitions as $definition) : ?>
<?php endforeach ?> <li><?= $definition ?></li>
</ol> <?php endforeach ?>
</ol>
<?php endif ?>
<?php endif ?> <?php endif ?>
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
</ol> </ol>
</div> </div>
<?php else: ?> <?php else: http_response_code(500); ?>
<h3 class="erreur">Erreur de génération de la grille</h3> <h3 class="erreur">Erreur de génération de la grille</h3>
<?php endif ?> <?php endif ?>
</div> </div>
<div class="nouvelle-grille"> <div class="nouvelle-grille">
<img src="favicon.svg" width="16" height="16"> <img src="favicon.svg" width="16" height="16">
<button type="submit"> <button type="submit">Nouvelle grille</button>
Nouvelle grille de de
<input type="number" id="lignes" name="lignes" value="<?= $hauteur ?>" min="<?=HAUTEUR_MIN?>" max="<?=HAUTEUR_MAX?>"/> <input type="number" id="lignes"<?= isset($_GET["lignes"])? 'name="lignes"': "" ?> value="<?= $hauteur ?>" min="<?=HAUTEUR_MIN?>" max="<?=HAUTEUR_MAX?>"/>
lignes et lignes et
<input type="number" id="colonnes" name="colonnes" value="<?= $largeur ?>" min="<?=LARGEUR_MIN?>" max="<?=LARGEUR_MAX?>"/> <input type="number" id="colonnes"<?= isset($_GET["colonnes"])? 'name="colonnes"': "" ?> value="<?= $largeur ?>" min="<?=LARGEUR_MIN?>" max="<?=LARGEUR_MAX?>"/>
colonnes colonnes
</button>
</div> </div>
</form> </form>
<script src="script.js"></script> <script src="script.js"></script>
<script>navigator?.serviceWorker.register('service-worker.js')</script>
</body> </body>
</html> </html>

View File

@ -6,7 +6,7 @@ async function sha256(text) {
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
} }
let inputs = grilleForm.querySelectorAll('.grille input') let inputs = grilleForm.querySelectorAll(".grille input");
let largeur = Number(colonnes.value); let largeur = Number(colonnes.value);
let nb_cases = inputs.length; let nb_cases = inputs.length;
let index = 0; let index = 0;
@ -16,11 +16,19 @@ for (let input of inputs) {
input.y = Math.floor(input.index / largeur); input.y = Math.floor(input.index / largeur);
input.onfocus = function (event) { input.onfocus = function (event) {
for (li of document.querySelectorAll(`.definitions.horizontales > ol > li:nth-child(${input.y+1}), .definitions.verticales > ol > li:nth-child(${input.x+1})`)) { for (li of document.querySelectorAll(
li.classList.add("selectionee") `.definitions.horizontales > ol > li:nth-child(${
input.y + 1
}), .definitions.verticales > ol > li:nth-child(${input.x + 1})`
)) {
li.classList.add("selectionee");
} }
for (li of document.querySelectorAll(`.definitions.horizontales > ol > li:not(:nth-child(${input.y+1})), .definitions.verticales > ol > li:not(:nth-child(${input.x+1}))`)) { for (li of document.querySelectorAll(
li.classList.add("non-selectionee") `.definitions.horizontales > ol > li:not(:nth-child(${
input.y + 1
})), .definitions.verticales > ol > li:not(:nth-child(${input.x + 1}))`
)) {
li.classList.add("non-selectionee");
} }
input.select(); input.select();
@ -69,17 +77,26 @@ for (let input of inputs) {
}; };
input.onblur = function (event) { input.onblur = function (event) {
for (li of document.querySelectorAll(`.definitions.horizontales > ol > li:nth-child(${input.y+1}), .definitions.verticales > ol > li:nth-child(${input.x+1})`)) { for (li of document.querySelectorAll(
li.classList.remove("selectionee") `.definitions.horizontales > ol > li:nth-child(${
input.y + 1
}), .definitions.verticales > ol > li:nth-child(${input.x + 1})`
)) {
li.classList.remove("selectionee");
} }
for (li of document.querySelectorAll(`.definitions.horizontales > ol > li:not(:nth-child(${input.y+1})), .definitions.verticales > ol > li:not(:nth-child(${input.x+1}))`)) { for (li of document.querySelectorAll(
li.classList.remove("non-selectionee") `.definitions.horizontales > ol > li:not(:nth-child(${
input.y + 1
})), .definitions.verticales > ol > li:not(:nth-child(${input.x + 1}))`
)) {
li.classList.remove("non-selectionee");
} }
}; };
}; }
for (let input of grilleForm.querySelectorAll('.nouvelle-grille input')) { for (let input of grilleForm.querySelectorAll(".nouvelle-grille input")) {
input.onfocus = function (event) { input.onfocus = function (event) {
input.name = input.id;
input.select(); input.select();
}; };
} }

86
service-worker.js Normal file
View File

@ -0,0 +1,86 @@
/*
Copyright 2015, 2019, 2020 Google LLC. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Incrementing OFFLINE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
const OFFLINE_VERSION = 1;
const CACHE_NAME = "offline";
// Customize this with a different URL if needed.
const OFFLINE_URL = "index.php";
self.addEventListener("install", (event) => {
event.waitUntil(
(async () => {
const cache = await caches.open(CACHE_NAME);
// Setting {cache: 'reload'} in the new request will ensure that the
// response isn't fulfilled from the HTTP cache; i.e., it will be from
// the network.
await cache.add(new Request(OFFLINE_URL, { cache: "reload" }));
})()
);
// Force the waiting service worker to become the active service worker.
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
(async () => {
// Enable navigation preload if it's supported.
// See https://developers.google.com/web/updates/2017/02/navigation-preload
if ("navigationPreload" in self.registration) {
await self.registration.navigationPreload.enable();
}
})()
);
// Tell the active service worker to take control of the page immediately.
self.clients.claim();
});
self.addEventListener("fetch", (event) => {
// We only want to call event.respondWith() if this is a navigation request
// for an HTML page.
if (event.request.mode === "navigate") {
event.respondWith(
(async () => {
try {
// First, try to use the navigation preload response if it's supported.
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
// Always try the network first.
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// catch is only triggered if an exception is thrown, which is likely
// due to a network error.
// If fetch() returns a valid HTTP response with a response code in
// the 4xx or 5xx range, the catch() will NOT be called.
console.log("Fetch failed; returning offline page instead.", error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse;
}
})()
);
}
// If our if() condition is false, then this fetch handler won't intercept the
// request. If there are any other fetch handlers registered, they will get a
// chance to call event.respondWith(). If no fetch handlers call
// event.respondWith(), the request will be handled by the browser as if there
// were no service worker involvement.
});

View File

@ -95,10 +95,6 @@ h2 {
background-color: black; background-color: black;
} }
.grille .case.noire {
background-color: black;
}
.grille input { .grille input {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -178,12 +174,14 @@ h2 {
text-align: center; text-align: center;
} }
.nouvelle-grille img {
margin: 0 0.5em;
}
.nouvelle-grille { .nouvelle-grille {
margin: 1em auto 0 auto; margin: 1em auto 0 auto;
display: flex; display: flex;
justify-content: center; align-items: baseline;
align-items: flex-start;
gap: 0.5em;
} }
.nouvelle-grille button, .nouvelle-grille button,
@ -193,22 +191,17 @@ h2 {
color: inherit; color: inherit;
font-family: inherit; font-family: inherit;
font-size: 1em; font-size: 1em;
cursor: pointer;
} }
.nouvelle-grille button { .nouvelle-grille button {
display: flex; text-decoration: underline;
} }
.nouvelle-grille button:hover { .nouvelle-grille button:hover {
cursor: pointer;
color: #2a6496; color: #2a6496;
} }
.nouvelle-grille button:hover,
.nouvelle-grille button:hover input {
text-decoration: underline;
}
.nouvelle-grille button:active { .nouvelle-grille button:active {
color: darkorchid; color: darkorchid;
} }
@ -295,7 +288,10 @@ h2 {
} }
.definitions li.non-selectionee { .definitions li.non-selectionee {
display: none; visibility: hidden;
height: 0;
margin: 0;
padding: 0;
} }
} }