From ca22cb129ddedcff389cd2a16059ba969b93d299 Mon Sep 17 00:00:00 2001 From: adrien Date: Sat, 17 Apr 2021 15:55:41 +0200 Subject: [PATCH] V2.8 --- classes.php | 77 +++++++-- img/light-bulb.svg | 1 + style.css | 235 +++++++++++++++----------- style.css~ => style.css.bak | 26 ++- style.css.bak2 | 328 ++++++++++++++++++++++++++++++++++++ sudoku.js | 147 +++++++--------- sudoku.php | 26 +-- thumbnail.php | 8 +- 8 files changed, 634 insertions(+), 214 deletions(-) create mode 100644 img/light-bulb.svg rename style.css~ => style.css.bak (93%) create mode 100644 style.css.bak2 diff --git a/classes.php b/classes.php index 0e7e4cb..c9c830c 100644 --- a/classes.php +++ b/classes.php @@ -1,6 +1,8 @@ value != UNKNOWN; } @@ -84,7 +86,7 @@ } } - function generate() { + function generate_() { // Init with a shuffle row $values = array("1", "2", "3", "4", "5", "6", "7", "8", "9"); shuffle($values); @@ -101,9 +103,9 @@ $nbClues = count($this->boxes); foreach($this->boxes as $testBox) { $testBoxes = array($testBox); - if ($nbClues >=30) + if ($nbClues >= 30) $testBoxes[] = $this->rows[8-$testBox->rowId][8-$testBox->columnId]; - if ($nbClues >=61) { + if ($nbClues >= 61) { $testBoxes[] = $this->rows[8-$testBox->rowId][$testBox->columnId]; $testBoxes[] = $this->rows[$testBox->rowId][8-$testBox->columnId]; } @@ -124,6 +126,60 @@ } } } + $validGrids[] = $this->toString(); + } + + function generate() { + // Init with a shuffle row + $values = array("1", "2", "3", "4", "5", "6", "7", "8", "9"); + shuffle($values); + forEach($this->rows[0] as $columnId => $box) { + $box->value = $values[$columnId]; + forEach($box->neighbourhood as $neighbour) + array_unset_value($box->value, $neighbour->candidates); + } + // Fill grid + $this->solutionsGenerator(true)->current(); + + // Group boxes with their groupedSymetricals + $groupedSymetricals = array(array($this->rows[4][4])); + for ($rowId = 0; $rowId <= 3; $rowId++) { + for ($columnId = 0; $columnId <= 3; $columnId++) { + $groupedSymetricals[] = array( + $this->rows[$rowId][$columnId], + $this->rows[8-$rowId][8-$columnId], + $this->rows[8-$rowId][$columnId], + $this->rows[$rowId][8-$columnId] + ); + } + $groupedSymetricals[] = array( + $this->rows[$rowId][4], + $this->rows[8-$rowId][4] + ); + } + for ($columnId = 0; $columnId <= 3; $columnId++) { + $groupedSymetricals[] = array( + $this->rows[4][$columnId], + $this->rows[4][8-$columnId] + ); + } + + // Remove clues randomly and their groupedSymetricals while there is still a unique solution + shuffle($groupedSymetricals); + foreach($groupedSymetricals as $symetricals) { + shuffle($symetricals); + foreach ($symetricals as $testBox) { + $erasedValue = $testBox->value; + $testBox->value = UNKNOWN; + forEach($testBox->neighbourhood as $neighbour) + $neighbour->searchCandidates(); + if (!$this->isValid()) { + $testBox->value = $erasedValue; + forEach($testBox->neighbourhood as $neighbour) array_unset_value($testBox->value, $neighbour->candidates); + } + } + } + $validGrids[] = $this->toString(); } function containsDuplicates() { @@ -139,17 +195,19 @@ } return false; } - + function countSolutions($max=2) { $solutions = $this->solutionsGenerator(false); $solutionsWithoutDuplicates = array(); $nbSolutions = 0; foreach($solutions as $solution) { - $solutionsWithoutDuplicates[$solution] = true; - $nbSolutions = count($solutionsWithoutDuplicates); - if ($nbSolutions >= $max) { - $solutions->send(true); - break; + if (!in_array($solution, $solutionsWithoutDuplicates)) { + $solutionsWithoutDuplicates[] = $solution; + $nbSolutions ++; + if ($nbSolutions >= $max) { + $solutions->send(true); + break; + } } } return $nbSolutions; @@ -165,7 +223,6 @@ if ($randomized) shuffle($emptyBoxes); usort($emptyBoxes, "easyFirst"); $testBox = $emptyBoxes[0]; - $nbTries = 0; if ($randomized) shuffle($testBox->candidates); $stop = null; foreach($testBox->candidates as $testBox->value) { diff --git a/img/light-bulb.svg b/img/light-bulb.svg new file mode 100644 index 0000000..f19f9ae --- /dev/null +++ b/img/light-bulb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/style.css b/style.css index 45c4980..cabd55b 100644 --- a/style.css +++ b/style.css @@ -1,12 +1,78 @@ +:root { + --body-bc: #e8ffff; + --body-fc: black; + --shadow: lightgray; + + --border-color: #555b6e; + + --box-bc: #F7FFF7; + --box-fc: black; + --forbidden-box-bc: #FFEE99; + --same-value-box-bc: #ffbc25; + --placeholder-fc: var(--grid-color); + + --clue-bc: #60D2CB; + --forbidden-clue-bc: var(--clue-bc); + --same-value-clue-bc: #24737F; + --clue-fc: var(--box-bc); + --forbidden-clue-fc: var(--forbidden-box-bc); + --same-value-clue-fc: var(--forbidden-box-bc); + + --tool-fc: var(--clue-fc); + --tool-bc: var(--clue-bc); + --selected-tool-bc: var(--same-value-clue-bc); + --selected-tool-fc: var(--same-value-clue-fc); + --tool-hover-bc: #80DBD5; + --selected-tool-hover-bc: #319EAF; +} + +@media (prefers-color-scheme: dark) { + :root { + --body-bc: #1c1c1e; + --body-fc: #fefefe; + --shadow: darkgray; + + --border-color: #292929; + + --box-bc: #0C151D; + --box-fc: #bfcbce; + --forbidden-box-bc: #004E52; + --same-value-box-bc: #09BC8A; + --placeholder-fc: var(--grid-color); + + --clue-bc: #508991; + --forbidden-clue-bc: var(--clue-bc); + --same-value-clue-bc: #75DDDD; + --clue-fc: var(--box-bc); + --forbidden-clue-fc: var(--forbidden-box-bc); + --same-value-clue-fc: var(--forbidden-box-bc); + + --tool-fc: var(--clue-fc); + --tool-bc: var(--clue-bc); + --selected-tool-bc: var(--same-value-clue-bc); + --selected-tool-fc: var(--same-value-clue-fc); + --tool-hover-bc: #61A0A8; + --selected-tool-hover-bc: #9DE7E7; + } +} + + body { font-family: sans-serif; width: min-content; margin: auto; + background: var(--body-bc); + color: var(--body-fc) } h1 { text-align: center; - margin: 1rem; + font-weight: 50; + margin: 2rem; + text-transform: uppercase; + letter-spacing: 1rem; + text-indent: 1rem; + text-shadow: .1rem .1rem var(--shadow); } section, div, footer { @@ -14,7 +80,7 @@ section, div, footer { align-items: center; justify-content: center; text-align: center; - margin: 0.8rem 0; + margin: 1rem 0; } div { @@ -28,79 +94,45 @@ div { .grid { border-spacing: 0; - border: 1px solid black; - border-radius: 6px; margin: auto; cursor: url(img/ink-pen.svg) 2 22, text; + border-collapse: collapse; } .grid td, tr { padding: 0; } -.grid tr:first-child td:first-child { - border-top-left-radius: 5px; -} - -.grid tr:first-child td:first-child input { - border-top-left-radius: 4px; -} - -.grid tr:first-child td:last-child { - border-top-right-radius: 5px; -} - -.grid tr:first-child td:last-child input { - border-top-right-radius: 4px; -} - -.grid tr:last-child td:first-child { - border-bottom-left-radius: 5px; -} - -.grid tr:last-child td:first-child > input { - border-bottom-left-radius: 4px; -} - -.grid tr:last-child td:last-child { - border-bottom-right-radius: 5px; -} - -.grid tr:last-child td:last-child input { - border-bottom-right-radius: 4px; -} - .grid tr:nth-child(3n+1) td { - border-top: 1px solid black; + border-top: 2px solid var(--border-color); } .grid tr:nth-child(3n+2) td { - border-top: 1px solid grey; - border-bottom: 1px solid grey; + border-top: 1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); } .grid tr:nth-child(3n) td { - border-bottom: 1px solid black; + border-bottom: 2px solid var(--border-color); } .grid td:nth-child(3n+1) { - border-left: 1px solid black; + border-left: 2px solid var(--border-color); } .grid td:nth-child(3n+2) { - border-left: 1px solid grey; - border-right: 1px solid grey; + border-left: 1px solid var(--border-color); + border-right: 1px solid var(--border-color); } .grid td:nth-child(3n+3) { - border-right: 1px solid black; + border-right: 2px solid var(--border-color); } .grid input { width: 2.5rem; height: 2.5rem; - font-size: 1.5rem; border: 0; border-radius: 0; padding: 0; @@ -120,8 +152,9 @@ input[type="number"]::-webkit-calendar-picker-indicator { } .grid input:enabled { - background: white; - color: darkblue; + font-size: 1.4rem; + color: var(--box-fc); + background: var(--box-bc); cursor: inherit; } @@ -132,49 +165,52 @@ input[type="number"]::-webkit-calendar-picker-indicator { } .grid input:disabled { - color: white !important; - background: #6666ff !important; - cursor: not-allowed !important; + font-size: 1.3rem; + font-weight: bold; + color: var(--clue-fc); + background: var(--clue-bc); + cursor: default; } .grid input.forbidden:enabled { - background: #ffffaa !important; + background: var(--forbidden-box-bc); cursor: not-allowed; } .grid input.same-value:enabled { - background: #ffff33; + background: var(--same-value-box-bc); cursor: not-allowed !important; } .grid input.forbidden:disabled { - color: #ffffaa; - background: #6666ff; + color: var(--forbidden-clue-fc); + background: var(--forbidden-clue-bc) !important; + cursor: not-allowed; } -.grid input.same-value:disabled, -.tools button.same-value:enabled, -.tools input:enabled:checked + label { - color: #ffffaa !important; - background: #00b359 !important; +.grid input.same-value:disabled { + color: var(--same-value-clue-fc) !important; + background: var(--same-value-clue-bc) !important; } .grid input.one-candidate { cursor: help !important; } +.tools div { + justify-content: space-between; +} .tools button, .tools input + label { - color: white; - text-shadow: -1px -1px #5b6c9e; - background: #8ca6f2; - border: 2px outset #8ca6f2; - border-radius: 4px; + color: var(--tool-fc); + background: var(--clue-bc); + border: 1px outset var(--clue-bc); font-size: 1.3rem; + font-weight: bold; min-width:20px; - padding: 4px 5px 5px 5px; - margin: 0px 1px 1px 1px; + padding: 6px; + margin: .1rem; cursor: pointer; } @@ -191,51 +227,44 @@ input[type="number"]::-webkit-calendar-picker-indicator { pointer-events: none; } -.tools button:enabled:hover, -.tools button:enabled:focus, -.tools input:enabled:hover + label, -.tools input:enabled:focus + label { - border-width: 1px; - border-style: outset; - padding: 5px 5px 5px 6px; - margin: 1px 1px 1px 2px; -} - -.tools input:enabled:checked:hover + label, -.tools input:enabled:checked:focus + label { - border-width: 3px; - border-style: inset; - padding: 4px 2px 2px 5px; - margin: 1px 1px 1px 2px; -} - +.tools button:enabled:checked, .tools input:enabled:checked + label { - text-shadow: -1px -1px #005f2f; - border: 2px inset #00b359; - background: #00b359; - padding: 4px 4px 4px 5px; - margin: 1px 1px 0px 2px; + border-style: inset; } -.tools button:enabled:active, -.tools input:enabled:active + label { - border-width: 4px !important; - border-style: inset !important; - padding: 4px 0px 0px 5px !important; - margin: 0px 1px 0px 2px !important; +.tools button.same-value:enabled, +.tools input:enabled:checked + label { + background: var(--selected-tool-bc); + color: var(--selected-tool-fc); + border-color: var(--selected-tool-bc); +} + +.tools button:enabled:hover, +.tools input:enabled:hover + label { + background: var(--tool-hover-bc); + border-color: var(--tool-hover-bc); +} + +.tools button:enabled:checked:hover, +.tools input:enabled:checked:hover + label { + background: var(--selected-tool-hover-bc); + border-color: var(--selected-tool-hover-bc); } .tools button:disabled, .tools input:disabled + label { - text-shadow: -1px -1px #555; color: #ccc; background: darkgrey; - border: 1px outset darkgrey; - padding: 5px 6px 6px 6px; - margin: 0px 1px 1px 1px; + border-style: outset; + border-color: darkgrey; cursor: not-allowed; } +.tools button:disabled * { + filter: grayscale(100%); + opacity: 0.7; +} + .tools button.warning:hover { background: #ff5050; border-color: #ff5050; @@ -282,11 +311,15 @@ input[type="number"]::-webkit-calendar-picker-indicator { a { text-decoration: none; + color: var(--forbidden-clue-bc); +} + +a:visited { + color: var(--same-value-clue-bc); } .credits { font-size: 0.8rem; margin: 0; -} - +} \ No newline at end of file diff --git a/style.css~ b/style.css.bak similarity index 93% rename from style.css~ rename to style.css.bak index 0cd223c..e712bb3 100644 --- a/style.css~ +++ b/style.css.bak @@ -134,11 +134,11 @@ input[type="number"]::-webkit-calendar-picker-indicator { .grid input:disabled { color: white !important; background: #6666ff !important; - cursor: not-allowed !important; + cursor: default; } .grid input.forbidden:enabled { - background: #ffffaa !important; + background: #f9f99f; cursor: not-allowed; } @@ -148,14 +148,15 @@ input[type="number"]::-webkit-calendar-picker-indicator { } .grid input.forbidden:disabled { - color: #ffffaa; + color: #f9f99f; background: #6666ff; + cursor: not-allowed; } .grid input.same-value:disabled, .tools button.same-value:enabled, .tools input:enabled:checked + label { - color: #ffffaa !important; + color: #f9f99f !important; background: #00b359 !important; } @@ -236,7 +237,12 @@ input[type="number"]::-webkit-calendar-picker-indicator { cursor: not-allowed; } -.tools button.warning { +.tools button:disabled * { + filter: grayscale(100%); + opacity: 0.7; +} + +.tools button.warning:hover { background: #ff5050; border-color: #ff5050; } @@ -290,3 +296,13 @@ a { margin: 0; } +@media (prefers-color-scheme: dark) { + body { + color: #eee; + background: #121212; + } + + body a { + color: #809fff; + } + } \ No newline at end of file diff --git a/style.css.bak2 b/style.css.bak2 new file mode 100644 index 0000000..06f4d5e --- /dev/null +++ b/style.css.bak2 @@ -0,0 +1,328 @@ +:root { + --body-bc: #e8ffff; + --body-fc: black; + --shadow: lightgray; + + --border-color: #555b6e; + + --box-bc: #F7FFF7; + --box-fc: black; + --forbidden-box-bc: #FFEE99; + --same-value-box-bc: #ffbc25; + --placeholder-fc: var(--grid-color); + + --clue-bc: #60D2CB; + --forbidden-clue-bc: var(--clue-bc); + --same-value-clue-bc: #24737F; + --clue-fc: var(--box-bc); + --forbidden-clue-fc: var(--forbidden-box-bc); + --same-value-clue-fc: var(--forbidden-box-bc); + + --tool-fc: var(--clue-fc); + --tool-bc: var(--clue-bc); + --selected-tool-bc: var(--same-value-clue-bc); + --selected-tool-fc: var(--same-value-clue-fc); + --tool-hover-bc: #80DBD5; + --selected-tool-hover-bc: #319EAF; +} + +@media (prefers-color-scheme: dark) { + :root { + --body-bc: #1c1c1e; + --body-fc: #fefefe; + --shadow: darkgray; + + --border-color: #292929; + + --box-bc: #0C151D; + --box-fc: #bfcbce; + --forbidden-box-bc: #004E52; + --same-value-box-bc: #09BC8A; + --placeholder-fc: var(--grid-color); + + --clue-bc: #508991; + --forbidden-clue-bc: var(--clue-bc); + --same-value-clue-bc: #75DDDD; + --clue-fc: var(--box-bc); + --forbidden-clue-fc: var(--forbidden-box-bc); + --same-value-clue-fc: var(--forbidden-box-bc); + + --tool-fc: var(--clue-fc); + --tool-bc: var(--clue-bc); + --selected-tool-bc: var(--same-value-clue-bc); + --selected-tool-fc: var(--same-value-clue-fc); + --tool-hover-bc: #61A0A8; + --selected-tool-hover-bc: #9DE7E7; + } +} + + +body { + font-family: sans-serif; + width: min-content; + margin: auto; + background: var(--body-bc); + color: var(--body-fc) +} + +h1 { + text-align: center; + font-weight: 50; + margin: 2rem; + text-transform: uppercase; + letter-spacing: 1rem; + text-indent: 1rem; + text-shadow: .1rem .1rem var(--shadow); +} + +section, +div, +footer { + align-items: center; + align-items: center; + justify-content: center; + text-align: center; + margin: 1rem 0; +} + +div { + display: flex; + flex-wrap: wrap; + row-gap: 0.5rem; + column-gap: 0.3rem; + margin: 0.5rem auto; +} + + +.grid { + border-spacing: 0; + margin: auto; + cursor: url(img/ink-pen.svg) 2 22, text; + border-collapse: collapse; +} + +.grid td, +tr { + padding: 0; +} + +.grid tr:nth-child(3n+1) td { + border-top: 2px solid var(--border-color); +} + +.grid tr:nth-child(3n+2) td { + border-top: 1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); +} + +.grid tr:nth-child(3n) td { + border-bottom: 2px solid var(--border-color); +} + +.grid td:nth-child(3n+1) { + border-left: 2px solid var(--border-color); +} + +.grid td:nth-child(3n+2) { + border-left: 1px solid var(--border-color); + border-right: 1px solid var(--border-color); +} + +.grid td:nth-child(3n+3) { + border-right: 2px solid var(--border-color); +} + + +.grid input { + width: 2.5rem; + height: 2.5rem; + border: 0; + border-radius: 0; + padding: 0; + text-align: center; + transition: background 0.5s; + -moz-appearance: textfield; +} + +input[type="number"]::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type="number"]::-webkit-calendar-picker-indicator { + display: none; +} + +.grid input:enabled { + font-size: 1.4rem; + color: var(--box-fc); + background: var(--box-bc); + cursor: inherit; +} + +.grid input.pencil, +.grid input::placeholder { + color: #666 !important; + font-size: 0.9rem !important; +} + +.grid input:disabled { + font-size: 1.3rem; + font-weight: bold; + color: var(--clue-fc); + background: var(--clue-bc); + cursor: default; +} + +.grid input.forbidden:enabled { + background: var(--forbidden-box-bc); + cursor: not-allowed; +} + +.grid input.same-value:enabled { + background: var(--same-value-box-bc); + cursor: not-allowed !important; +} + +.grid input.forbidden:disabled { + color: var(--forbidden-clue-fc); + background: var(--forbidden-clue-bc) !important; + cursor: not-allowed; +} + +.grid input.same-value:disabled { + color: var(--same-value-clue-fc) !important; + background: var(--same-value-clue-bc) !important; +} + +.grid input.one-candidate { + cursor: help !important; +} + +.tools div { + justify-content: space-between; +} + +.tools button, +.tools input+label { + color: var(--tool-fc); + background: var(--clue-bc); + border: 1px outset var(--clue-bc); + font-size: 1.3rem; + font-weight: bold; + min-width: 20px; + padding: 6px; + margin: .1rem; + cursor: pointer; +} + +.tools img { + display: block; + width: 24px; + height: 24px; +} + +.tools input { + position: fixed; + opacity: 0; + width: 0; + pointer-events: none; +} + +.tools button:enabled:checked, +.tools input:enabled:checked+label { + border-style: inset; +} + +.tools button.same-value:enabled, +.tools input:enabled:checked+label { + background: var(--selected-tool-bc); + color: var(--selected-tool-fc); + border-color: var(--selected-tool-bc); +} + +.tools button:enabled:hover, +.tools input:enabled:hover+label { + background: var(--tool-hover-bc); + border-color: var(--tool-hover-bc); +} + +.tools button:enabled:checked:hover, +.tools input:enabled:checked:hover+label { + background: var(--selected-tool-hover-bc); + border-color: var(--selected-tool-hover-bc); +} + +.tools button:disabled, +.tools input:disabled+label { + color: #ccc; + background: darkgrey; + border-style: outset; + border-color: darkgrey; + cursor: not-allowed; +} + +.tools button:disabled * { + filter: grayscale(100%); + opacity: 0.7; +} + +.tools button.warning:hover { + background: #ff5050; + border-color: #ff5050; +} + + +.context-menu { + display: none; + z-index: 1000; + position: absolute; + overflow: hidden; + border: 1px solid #CCC; + white-space: nowrap; + font-family: sans-serif; + background: #EEE; + color: #333; + border-top-right-radius: 3px; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + padding: 0; + margin: 0; +} + +.context-menu li { + padding: 6px 10px; + cursor: default; + list-style-type: none; + transition: all .3s ease; + font-size: 1rem; +} + +.context-menu li:hover { + background-color: #DEF; +} + +.context-menu li.error { + color: #888 +} + +.context-menu li.error:hover { + background-color: #EEE; +} + + +a { + text-decoration: none; + color: var(--forbidden-clue-bc); +} + +a:visited { + color: var(--same-value-clue-bc); +} + + +.credits { + font-size: 0.8rem; + margin: 0; +} \ No newline at end of file diff --git a/sudoku.js b/sudoku.js index b556e2e..14288f8 100755 --- a/sudoku.js +++ b/sudoku.js @@ -10,7 +10,7 @@ let suggestionTimer = null let valueToInsert = "" let history = [] let accessKeyModifiers = "AccessKey+" -let changesToSave = false +let easyBoxes = [] function shuffle(iterable) { array = Array.from(iterable) @@ -103,7 +103,7 @@ function searchCandidatesOf(box) { box.title = "Aucune possibilité !" break case 1: - box.title = "1 possibilité [Clic-droit]" + box.title = "Une seule possibilité [Clic-droit]" break default: box.title = box.candidates.size + " possibilités [Clic-droit]" @@ -113,6 +113,7 @@ function searchCandidatesOf(box) { function onfocus() { if (pencilRadio.checked) { + //this.type = "text" this.value = this.placeholder this.classList.add("pencil") } else { @@ -130,9 +131,10 @@ function onclick() { this.select() } } else if (pencilRadio.checked) { - if (valueToInsert) - this.value += valueToInsert + if (valueToInsert) { + this.value = Array.from(new Set(this.value + valueToInsert)).join("") this.oninput() + } } else if (eraserRadio.checked) { this.value = "" this.placeholder = "" @@ -143,9 +145,8 @@ function onclick() { function oninput() { history.push({ box: this, value: this.previousValue, placeholder: this.previousPlaceholder }) undoButton.disabled = false - changesToSave = true + saveButton.disabled = false if (pencilRadio.checked) { - this.value = Array.from(new Set(this.value)).sort().join("") this.previousValue = "" this.previousPlaceholder = this.value } else { @@ -153,9 +154,6 @@ function oninput() { this.previousPlaceholder = this.placeholder refreshBox(this) } - - if (suggestionTimer) clearTimeout(suggestionTimer) - suggestionTimer = setTimeout(showSuggestion, SUGESTION_DELAY) } function refreshBox(box) { @@ -165,33 +163,32 @@ function refreshBox(box) { function checkBox(box) { box.neighbourhood.concat([box]).forEach(neighbour => { - searchCandidatesOf(neighbour) neighbour.setCustomValidity("") + searchCandidatesOf(neighbour) + if (neighbour.candidates.size == 0) { + neighbour.setCustomValidity("Aucun chiffre possible !") + } }) - for (neighbour1 of box.neighbourhood) { - if (neighbour1.value) { - for (area of [ - { name: "région", neighbours: regions[neighbour1.regionId] }, - { name: "ligne", neighbours: rows[neighbour1.rowId] }, - { name: "colonne", neighbours: columns[neighbour1.columnId] }, - ]) - for (neighbour2 of area.neighbours) - if (neighbour2 != neighbour1 && neighbour2.value == neighbour1.value) { - for (neighbour of [neighbour1, neighbour2]) { - neighbour.setCustomValidity(`Il y a un autre ${neighbour.value} dans cette ${area.name}.`) - } - } - } else { - if (neighbour1.candidates.size == 0) { - neighbour1.setCustomValidity("Aucun chiffre possible !") + if (box.value) { + for (area of [ + { name: "région", neighbours: regions[box.regionId] }, + { name: "ligne", neighbours: rows[box.rowId] }, + { name: "colonne", neighbours: columns[box.columnId] }, + ]) + for (neighbour of area.neighbours) + if (box != neighbour && box.value == neighbour.value) { + for (neighbour of [box, neighbour]) { + neighbour.setCustomValidity(`Il y a un autre ${box.value} dans cette ${area.name}.`) + } } - } } if (box.form.checkValidity()) { // Correct grid - if (boxes.filter(box => box.value == "").length == 0) + if (boxes.filter(box => box.value == "").length == 0) { setTimeout(() => alert(`Bravo ! Vous avez résolu la grille.`), 500) + saveButton.disabled = true + } } else { // Errors on grid box.form.reportValidity() } @@ -200,7 +197,6 @@ function checkBox(box) { function refreshUI() { enableRadio() highlight() - showEasyBoxes() } function enableRadio() { @@ -211,62 +207,42 @@ function enableRadio() { } else { radio.disabled = true radio.label.title = `Tous les ${radio.value} sont posés.` - if (valueToInsert == radio.value) { - let nextRadio = document.querySelector(".insertRadioGroup :checked ~ input:enabled") || document.querySelector(".insertRadioGroup :enabled") - if (nextRadio) { - nextRadio.click() - nextRadio.onfocus() - } else { - valueToInsert = "" - } - } + if (valueToInsert == radio.value) + valueToInsert = "" } } } function highlight() { + hintButton.disabled = true + easyBoxes = [] boxes.forEach(box => { if (valueToInsert && box.value == valueToInsert) { box.classList.add("same-value") box.tabIndex = -1 } else { box.classList.remove("same-value") - if (box.disabled) { + if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) { box.classList.add("forbidden") + box.tabIndex = -1 } else { - if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) { - box.classList.add("forbidden") - box.tabIndex = -1 - } else { - box.classList.remove("forbidden") - box.tabIndex = 0 - } + box.classList.remove("forbidden") + box.tabIndex = 0 } } + if (!box.value && box.candidates.size == 1) { + hintButton.disabled = false + easyBoxes.push(box) + } }) highlighterCheckbox.label.title = "Surligner les lignes, colonnes et régions contenant déjà " + (valueToInsert? "un " + valueToInsert: "le chiffre sélectionné") } -function showEasyBoxes() { - boxes.filter(box => !box.disabled).forEach(box => { - if (!box.value && box.candidates.size == 1) { - box.classList.add("one-candidate") - box.onclick = function() { - valueToInsert = this.candidates.values().next().value - document.getElementById("insertRadio" + valueToInsert).checked = true - onclick.apply(box) - } - } else { - box.classList.remove("one-candidate") - box.onclick = onclick - } - }) -} - function onblur() { if (this.classList.contains("pencil")) { this.placeholder = this.value this.value = "" + //this.type = "number" this.classList.remove("pencil") } } @@ -292,7 +268,10 @@ function undo() { previousState.box.value = previousState.value previousState.box.placeholder = previousState.placeholder refreshBox(previousState.box) - if (history.length < 1) undoButton.disabled = true + if (history.length < 1) { + undoButton.disabled = true + saveButton.disabled = true + } } } @@ -316,26 +295,30 @@ function save() { let saveGame = boxes.map(box => box.value || UNKNOWN).join("") localStorage[location.pathname] = saveGame fixGridLink.href = saveGame - changesToSave = false + saveButton.disabled = true + alert("Partie sauvegardée") } window.onbeforeunload = function(event) { - if (changesToSave) { + if (!saveButton.disabled) { event.preventDefault() - event.returnValue = "" + event.returnValue = "La partie n'est pas sauvegardée. Quitter quand même ?" } } -function showSuggestion() { - const easyBoxes = boxes.filter(box => box.value == "" && box.candidates.size == 1) +function showHint() { if (easyBoxes.length) { - let randomEasyBox = shuffle(easyBoxes)[0] - randomEasyBox.placeholder = "💡" - randomEasyBox.focus() - } else { - clearTimeout(suggestionTimer) - suggestionTimer = null + shuffle(easyBoxes) + let box = easyBoxes.pop() + box.placeholder = "💡" + box.focus() + /*value = Array.from(box.candidates)[0] + radio = document.getElementById("insertRadio" + value) + radio.checked = true + insert(radio)*/ + return box } + hintButton.disabled = true } function oncontextmenu(event) { @@ -348,10 +331,9 @@ function oncontextmenu(event) { li.innerText = candidate li.onclick = function (event) { contextMenu.style.display = "none" - box.onfocus() - box.value = event.target.innerText - box.oninput() - box.onblur() + valueToInsert = event.target.innerText + document.getElementById("insertRadio" + valueToInsert).checked = true + box.onclick() } contextMenu.appendChild(li) }) @@ -363,14 +345,13 @@ function oncontextmenu(event) { } contextMenu.style.left = `${event.pageX}px` contextMenu.style.top = `${event.pageY}px` - console.log(event.target) contextMenu.style.display = "block" - return false -} -document.onclick = function (event) { - if (contextMenu.style.display == "block") + document.onclick = function (event) { contextMenu.style.display = "none" + document.onclick = null + } + return false } document.onkeydown = function(event) { diff --git a/sudoku.php b/sudoku.php index 42fc230..8762e0a 100755 --- a/sudoku.php +++ b/sudoku.php @@ -4,7 +4,7 @@ $currentGrid = strip_tags($_GET['grid']); $_SESSION["currentGrid"] = $currentGrid; - if (!isset($_SESSION[$currentGrid])) { + if (!in_array($currentGrid, $validGrids)) { $grid = new Grid(); $grid->import($currentGrid); if ($grid->containsDuplicates()) { @@ -15,6 +15,7 @@ $warning = "Cette grille n'a pas de solution."; break; case 1: + $validGrids[] = $currentGrid; break; default: $warning = "Cette grille a plusieurs solutions."; @@ -58,6 +59,18 @@

Sudoku

+
+
+ + + + + + + + +
+
@@ -94,15 +107,6 @@ } ?> -
- - - - - - - -
- Icônes par Freepik chez www.flaticon.com + Icônes par Freepik chez www.flaticon.com
diff --git a/thumbnail.php b/thumbnail.php index 0fc1c44..aefd6ed 100644 --- a/thumbnail.php +++ b/thumbnail.php @@ -14,10 +14,10 @@ $thumbnail = imagecreate($size, $size); $transparent = imagecolorallocate($thumbnail, 1, 1, 1); imagecolortransparent($thumbnail, $transparent); - $black = imagecolorallocate($thumbnail, 0, 0, 0); - $grey = imagecolorallocate($thumbnail, 128, 128, 128); - $blue = imagecolorallocate($thumbnail, 102, 102, 255); - $white = imagecolorallocate($thumbnail, 255, 255, 255); + $black = imagecolorallocate($thumbnail, 85, 91, 110); + $grey = imagecolorallocate($thumbnail, 85, 91, 110); + $blue = imagecolorallocate($thumbnail, 96, 210, 203); + $white = imagecolorallocate($thumbnail, 247, 255, 247); if ($size <= 36) { $boxSize = floor(($size-4) / 9);