V2.8
This commit is contained in:
parent
80d368446a
commit
ca22cb129d
77
classes.php
77
classes.php
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
const UNKNOWN = ".";
|
const UNKNOWN = ".";
|
||||||
|
|
||||||
|
$validGrids = array();
|
||||||
|
|
||||||
function isKnown($box) {
|
function isKnown($box) {
|
||||||
return $box->value != UNKNOWN;
|
return $box->value != UNKNOWN;
|
||||||
}
|
}
|
||||||
@ -84,7 +86,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate() {
|
function generate_() {
|
||||||
// Init with a shuffle row
|
// Init with a shuffle row
|
||||||
$values = array("1", "2", "3", "4", "5", "6", "7", "8", "9");
|
$values = array("1", "2", "3", "4", "5", "6", "7", "8", "9");
|
||||||
shuffle($values);
|
shuffle($values);
|
||||||
@ -101,9 +103,9 @@
|
|||||||
$nbClues = count($this->boxes);
|
$nbClues = count($this->boxes);
|
||||||
foreach($this->boxes as $testBox) {
|
foreach($this->boxes as $testBox) {
|
||||||
$testBoxes = array($testBox);
|
$testBoxes = array($testBox);
|
||||||
if ($nbClues >=30)
|
if ($nbClues >= 30)
|
||||||
$testBoxes[] = $this->rows[8-$testBox->rowId][8-$testBox->columnId];
|
$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[8-$testBox->rowId][$testBox->columnId];
|
||||||
$testBoxes[] = $this->rows[$testBox->rowId][8-$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() {
|
function containsDuplicates() {
|
||||||
@ -139,17 +195,19 @@
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function countSolutions($max=2) {
|
function countSolutions($max=2) {
|
||||||
$solutions = $this->solutionsGenerator(false);
|
$solutions = $this->solutionsGenerator(false);
|
||||||
$solutionsWithoutDuplicates = array();
|
$solutionsWithoutDuplicates = array();
|
||||||
$nbSolutions = 0;
|
$nbSolutions = 0;
|
||||||
foreach($solutions as $solution) {
|
foreach($solutions as $solution) {
|
||||||
$solutionsWithoutDuplicates[$solution] = true;
|
if (!in_array($solution, $solutionsWithoutDuplicates)) {
|
||||||
$nbSolutions = count($solutionsWithoutDuplicates);
|
$solutionsWithoutDuplicates[] = $solution;
|
||||||
if ($nbSolutions >= $max) {
|
$nbSolutions ++;
|
||||||
$solutions->send(true);
|
if ($nbSolutions >= $max) {
|
||||||
break;
|
$solutions->send(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $nbSolutions;
|
return $nbSolutions;
|
||||||
@ -165,7 +223,6 @@
|
|||||||
if ($randomized) shuffle($emptyBoxes);
|
if ($randomized) shuffle($emptyBoxes);
|
||||||
usort($emptyBoxes, "easyFirst");
|
usort($emptyBoxes, "easyFirst");
|
||||||
$testBox = $emptyBoxes[0];
|
$testBox = $emptyBoxes[0];
|
||||||
$nbTries = 0;
|
|
||||||
if ($randomized) shuffle($testBox->candidates);
|
if ($randomized) shuffle($testBox->candidates);
|
||||||
$stop = null;
|
$stop = null;
|
||||||
foreach($testBox->candidates as $testBox->value) {
|
foreach($testBox->candidates as $testBox->value) {
|
||||||
|
1
img/light-bulb.svg
Normal file
1
img/light-bulb.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg id="Capa_1" enable-background="new 0 0 497 497" height="24" viewBox="0 0 497 497" width="24" xmlns="http://www.w3.org/2000/svg"><g><g><path d="m282.159 497h-67.319c-5.014 0-9.331-3.539-10.314-8.456l-9.529-47.643h107.004l-9.529 47.643c-.982 4.917-5.299 8.456-10.313 8.456z" fill="#b1abb4"/></g><g><path d="m272.002 440.901-9.529 47.643c-.983 4.917-5.3 8.456-10.314 8.456h30c5.014 0 9.331-3.539 10.314-8.456l9.529-47.643z" fill="#a29aa5"/></g><path d="m302.997 418.462c12.392 0 22.438-10.047 22.438-22.44s-10.046-22.439-22.439-22.439l-54.496-20-54.496 20c-12.393 0-22.439 10.046-22.439 22.439s10.046 22.439 22.438 22.44c-12.392.001-22.438 10.047-22.438 22.439 0 12.393 10.047 22.439 22.44 22.439h108.992c12.393 0 22.439-10.047 22.439-22.439-.001-12.392-10.046-22.439-22.439-22.439z" fill="#f6f6f6"/><path d="m302.996 418.462c12.393 0 22.439-10.047 22.439-22.44s-10.046-22.439-22.439-22.439l-13.031-10-16.969 10c12.393 0 22.439 10.046 22.439 22.439s-10.046 22.44-22.439 22.44c12.393 0 22.439 10.047 22.439 22.439 0 12.393-10.046 22.439-22.439 22.439h30c12.393 0 22.439-10.047 22.439-22.439 0-12.393-10.046-22.439-22.439-22.439z" fill="#eaeaea"/><g><path d="m405.577 157.077c0-87.128-70.938-157.688-158.209-157.073-85.121.6-154.794 69.813-155.93 154.929-.489 36.674 11.595 70.504 32.213 97.463 23.117 30.226 35.092 67.512 35.092 105.565v1.372c0 7.87 6.38 14.25 14.25 14.25h151.017c7.87 0 14.25-6.38 14.25-14.25 0-38.726 12.155-76.653 35.537-107.523 19.945-26.334 31.78-59.15 31.78-94.733z" fill="#f7e365"/></g><g><path d="m247.367.004c-4.64.033-9.23.281-13.769.713 79.668 7.598 141.978 74.702 141.978 156.359 0 35.583-11.835 68.4-31.781 94.734-23.382 30.87-35.537 68.797-35.537 107.523 0 7.87-6.38 14.25-14.25 14.25h30c7.87 0 14.25-6.38 14.25-14.25 0-38.726 12.155-76.653 35.537-107.523 19.946-26.334 31.781-59.15 31.781-94.734.001-87.128-70.938-157.687-158.209-157.072z" fill="#f0d944"/></g><path d="m326.359 109.713c-3.749-1.763-8.216-.154-9.979 3.594l-10.465 22.244c-.075-.049-.145-.103-.222-.149-35.277-21.166-79.107-21.166-114.385 0-.077.046-.147.1-.222.149l-10.465-22.244c-1.764-3.748-6.231-5.357-9.98-3.594-3.748 1.764-5.357 6.231-3.594 9.979l39.048 82.998c6.613 14.056 10.108 29.696 10.108 45.229v11.246 2.333 112.083h15v-106.914h34.594v106.916h15v-112.083-2.333-11.247c0-15.533 3.496-31.173 10.108-45.229l39.048-82.998c1.763-3.748.154-8.216-3.594-9.98zm-49.027 86.592c-7.546 16.041-11.535 33.889-11.535 51.615v3.746h-34.594v-3.746c0-17.726-3.989-35.574-11.535-51.615l-22.263-47.321c.555-.174 1.099-.408 1.62-.72 30.517-18.311 68.433-18.311 98.949 0 .521.313 1.067.541 1.623.715z" fill="#dbb72b"/></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
235
style.css
235
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 {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
background: var(--body-bc);
|
||||||
|
color: var(--body-fc)
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
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 {
|
section, div, footer {
|
||||||
@ -14,7 +80,7 @@ section, div, footer {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0.8rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
@ -28,79 +94,45 @@ div {
|
|||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
border: 1px solid black;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
cursor: url(img/ink-pen.svg) 2 22, text;
|
cursor: url(img/ink-pen.svg) 2 22, text;
|
||||||
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid td, tr {
|
.grid td, tr {
|
||||||
padding: 0;
|
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 {
|
.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 {
|
.grid tr:nth-child(3n+2) td {
|
||||||
border-top: 1px solid grey;
|
border-top: 1px solid var(--border-color);
|
||||||
border-bottom: 1px solid grey;
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid tr:nth-child(3n) td {
|
.grid tr:nth-child(3n) td {
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 2px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid td:nth-child(3n+1) {
|
.grid td:nth-child(3n+1) {
|
||||||
border-left: 1px solid black;
|
border-left: 2px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid td:nth-child(3n+2) {
|
.grid td:nth-child(3n+2) {
|
||||||
border-left: 1px solid grey;
|
border-left: 1px solid var(--border-color);
|
||||||
border-right: 1px solid grey;
|
border-right: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid td:nth-child(3n+3) {
|
.grid td:nth-child(3n+3) {
|
||||||
border-right: 1px solid black;
|
border-right: 2px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.grid input {
|
.grid input {
|
||||||
width: 2.5rem;
|
width: 2.5rem;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
font-size: 1.5rem;
|
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -120,8 +152,9 @@ input[type="number"]::-webkit-calendar-picker-indicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid input:enabled {
|
.grid input:enabled {
|
||||||
background: white;
|
font-size: 1.4rem;
|
||||||
color: darkblue;
|
color: var(--box-fc);
|
||||||
|
background: var(--box-bc);
|
||||||
cursor: inherit;
|
cursor: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,49 +165,52 @@ input[type="number"]::-webkit-calendar-picker-indicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid input:disabled {
|
.grid input:disabled {
|
||||||
color: white !important;
|
font-size: 1.3rem;
|
||||||
background: #6666ff !important;
|
font-weight: bold;
|
||||||
cursor: not-allowed !important;
|
color: var(--clue-fc);
|
||||||
|
background: var(--clue-bc);
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid input.forbidden:enabled {
|
.grid input.forbidden:enabled {
|
||||||
background: #ffffaa !important;
|
background: var(--forbidden-box-bc);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid input.same-value:enabled {
|
.grid input.same-value:enabled {
|
||||||
background: #ffff33;
|
background: var(--same-value-box-bc);
|
||||||
cursor: not-allowed !important;
|
cursor: not-allowed !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid input.forbidden:disabled {
|
.grid input.forbidden:disabled {
|
||||||
color: #ffffaa;
|
color: var(--forbidden-clue-fc);
|
||||||
background: #6666ff;
|
background: var(--forbidden-clue-bc) !important;
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid input.same-value:disabled,
|
.grid input.same-value:disabled {
|
||||||
.tools button.same-value:enabled,
|
color: var(--same-value-clue-fc) !important;
|
||||||
.tools input:enabled:checked + label {
|
background: var(--same-value-clue-bc) !important;
|
||||||
color: #ffffaa !important;
|
|
||||||
background: #00b359 !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid input.one-candidate {
|
.grid input.one-candidate {
|
||||||
cursor: help !important;
|
cursor: help !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tools div {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.tools button,
|
.tools button,
|
||||||
.tools input + label {
|
.tools input + label {
|
||||||
color: white;
|
color: var(--tool-fc);
|
||||||
text-shadow: -1px -1px #5b6c9e;
|
background: var(--clue-bc);
|
||||||
background: #8ca6f2;
|
border: 1px outset var(--clue-bc);
|
||||||
border: 2px outset #8ca6f2;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
|
font-weight: bold;
|
||||||
min-width:20px;
|
min-width:20px;
|
||||||
padding: 4px 5px 5px 5px;
|
padding: 6px;
|
||||||
margin: 0px 1px 1px 1px;
|
margin: .1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,51 +227,44 @@ input[type="number"]::-webkit-calendar-picker-indicator {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools button:enabled:hover,
|
.tools button:enabled:checked,
|
||||||
.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 input:enabled:checked + label {
|
.tools input:enabled:checked + label {
|
||||||
text-shadow: -1px -1px #005f2f;
|
border-style: inset;
|
||||||
border: 2px inset #00b359;
|
|
||||||
background: #00b359;
|
|
||||||
padding: 4px 4px 4px 5px;
|
|
||||||
margin: 1px 1px 0px 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools button:enabled:active,
|
.tools button.same-value:enabled,
|
||||||
.tools input:enabled:active + label {
|
.tools input:enabled:checked + label {
|
||||||
border-width: 4px !important;
|
background: var(--selected-tool-bc);
|
||||||
border-style: inset !important;
|
color: var(--selected-tool-fc);
|
||||||
padding: 4px 0px 0px 5px !important;
|
border-color: var(--selected-tool-bc);
|
||||||
margin: 0px 1px 0px 2px !important;
|
}
|
||||||
|
|
||||||
|
.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 button:disabled,
|
||||||
.tools input:disabled + label {
|
.tools input:disabled + label {
|
||||||
text-shadow: -1px -1px #555;
|
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
background: darkgrey;
|
background: darkgrey;
|
||||||
border: 1px outset darkgrey;
|
border-style: outset;
|
||||||
padding: 5px 6px 6px 6px;
|
border-color: darkgrey;
|
||||||
margin: 0px 1px 1px 1px;
|
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tools button:disabled * {
|
||||||
|
filter: grayscale(100%);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
.tools button.warning:hover {
|
.tools button.warning:hover {
|
||||||
background: #ff5050;
|
background: #ff5050;
|
||||||
border-color: #ff5050;
|
border-color: #ff5050;
|
||||||
@ -282,11 +311,15 @@ input[type="number"]::-webkit-calendar-picker-indicator {
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
color: var(--forbidden-clue-bc);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: var(--same-value-clue-bc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.credits {
|
.credits {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -134,11 +134,11 @@ input[type="number"]::-webkit-calendar-picker-indicator {
|
|||||||
.grid input:disabled {
|
.grid input:disabled {
|
||||||
color: white !important;
|
color: white !important;
|
||||||
background: #6666ff !important;
|
background: #6666ff !important;
|
||||||
cursor: not-allowed !important;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid input.forbidden:enabled {
|
.grid input.forbidden:enabled {
|
||||||
background: #ffffaa !important;
|
background: #f9f99f;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,14 +148,15 @@ input[type="number"]::-webkit-calendar-picker-indicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.grid input.forbidden:disabled {
|
.grid input.forbidden:disabled {
|
||||||
color: #ffffaa;
|
color: #f9f99f;
|
||||||
background: #6666ff;
|
background: #6666ff;
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid input.same-value:disabled,
|
.grid input.same-value:disabled,
|
||||||
.tools button.same-value:enabled,
|
.tools button.same-value:enabled,
|
||||||
.tools input:enabled:checked + label {
|
.tools input:enabled:checked + label {
|
||||||
color: #ffffaa !important;
|
color: #f9f99f !important;
|
||||||
background: #00b359 !important;
|
background: #00b359 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +237,12 @@ input[type="number"]::-webkit-calendar-picker-indicator {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools button.warning {
|
.tools button:disabled * {
|
||||||
|
filter: grayscale(100%);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools button.warning:hover {
|
||||||
background: #ff5050;
|
background: #ff5050;
|
||||||
border-color: #ff5050;
|
border-color: #ff5050;
|
||||||
}
|
}
|
||||||
@ -290,3 +296,13 @@ a {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
color: #eee;
|
||||||
|
background: #121212;
|
||||||
|
}
|
||||||
|
|
||||||
|
body a {
|
||||||
|
color: #809fff;
|
||||||
|
}
|
||||||
|
}
|
328
style.css.bak2
Normal file
328
style.css.bak2
Normal file
@ -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;
|
||||||
|
}
|
147
sudoku.js
147
sudoku.js
@ -10,7 +10,7 @@ let suggestionTimer = null
|
|||||||
let valueToInsert = ""
|
let valueToInsert = ""
|
||||||
let history = []
|
let history = []
|
||||||
let accessKeyModifiers = "AccessKey+"
|
let accessKeyModifiers = "AccessKey+"
|
||||||
let changesToSave = false
|
let easyBoxes = []
|
||||||
|
|
||||||
function shuffle(iterable) {
|
function shuffle(iterable) {
|
||||||
array = Array.from(iterable)
|
array = Array.from(iterable)
|
||||||
@ -103,7 +103,7 @@ function searchCandidatesOf(box) {
|
|||||||
box.title = "Aucune possibilité !"
|
box.title = "Aucune possibilité !"
|
||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
box.title = "1 possibilité [Clic-droit]"
|
box.title = "Une seule possibilité [Clic-droit]"
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
box.title = box.candidates.size + " possibilités [Clic-droit]"
|
box.title = box.candidates.size + " possibilités [Clic-droit]"
|
||||||
@ -113,6 +113,7 @@ function searchCandidatesOf(box) {
|
|||||||
|
|
||||||
function onfocus() {
|
function onfocus() {
|
||||||
if (pencilRadio.checked) {
|
if (pencilRadio.checked) {
|
||||||
|
//this.type = "text"
|
||||||
this.value = this.placeholder
|
this.value = this.placeholder
|
||||||
this.classList.add("pencil")
|
this.classList.add("pencil")
|
||||||
} else {
|
} else {
|
||||||
@ -130,9 +131,10 @@ function onclick() {
|
|||||||
this.select()
|
this.select()
|
||||||
}
|
}
|
||||||
} else if (pencilRadio.checked) {
|
} else if (pencilRadio.checked) {
|
||||||
if (valueToInsert)
|
if (valueToInsert) {
|
||||||
this.value += valueToInsert
|
this.value = Array.from(new Set(this.value + valueToInsert)).join("")
|
||||||
this.oninput()
|
this.oninput()
|
||||||
|
}
|
||||||
} else if (eraserRadio.checked) {
|
} else if (eraserRadio.checked) {
|
||||||
this.value = ""
|
this.value = ""
|
||||||
this.placeholder = ""
|
this.placeholder = ""
|
||||||
@ -143,9 +145,8 @@ function onclick() {
|
|||||||
function oninput() {
|
function oninput() {
|
||||||
history.push({ box: this, value: this.previousValue, placeholder: this.previousPlaceholder })
|
history.push({ box: this, value: this.previousValue, placeholder: this.previousPlaceholder })
|
||||||
undoButton.disabled = false
|
undoButton.disabled = false
|
||||||
changesToSave = true
|
saveButton.disabled = false
|
||||||
if (pencilRadio.checked) {
|
if (pencilRadio.checked) {
|
||||||
this.value = Array.from(new Set(this.value)).sort().join("")
|
|
||||||
this.previousValue = ""
|
this.previousValue = ""
|
||||||
this.previousPlaceholder = this.value
|
this.previousPlaceholder = this.value
|
||||||
} else {
|
} else {
|
||||||
@ -153,9 +154,6 @@ function oninput() {
|
|||||||
this.previousPlaceholder = this.placeholder
|
this.previousPlaceholder = this.placeholder
|
||||||
refreshBox(this)
|
refreshBox(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suggestionTimer) clearTimeout(suggestionTimer)
|
|
||||||
suggestionTimer = setTimeout(showSuggestion, SUGESTION_DELAY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshBox(box) {
|
function refreshBox(box) {
|
||||||
@ -165,33 +163,32 @@ function refreshBox(box) {
|
|||||||
|
|
||||||
function checkBox(box) {
|
function checkBox(box) {
|
||||||
box.neighbourhood.concat([box]).forEach(neighbour => {
|
box.neighbourhood.concat([box]).forEach(neighbour => {
|
||||||
searchCandidatesOf(neighbour)
|
|
||||||
neighbour.setCustomValidity("")
|
neighbour.setCustomValidity("")
|
||||||
|
searchCandidatesOf(neighbour)
|
||||||
|
if (neighbour.candidates.size == 0) {
|
||||||
|
neighbour.setCustomValidity("Aucun chiffre possible !")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
for (neighbour1 of box.neighbourhood) {
|
if (box.value) {
|
||||||
if (neighbour1.value) {
|
for (area of [
|
||||||
for (area of [
|
{ name: "région", neighbours: regions[box.regionId] },
|
||||||
{ name: "région", neighbours: regions[neighbour1.regionId] },
|
{ name: "ligne", neighbours: rows[box.rowId] },
|
||||||
{ name: "ligne", neighbours: rows[neighbour1.rowId] },
|
{ name: "colonne", neighbours: columns[box.columnId] },
|
||||||
{ name: "colonne", neighbours: columns[neighbour1.columnId] },
|
])
|
||||||
])
|
for (neighbour of area.neighbours)
|
||||||
for (neighbour2 of area.neighbours)
|
if (box != neighbour && box.value == neighbour.value) {
|
||||||
if (neighbour2 != neighbour1 && neighbour2.value == neighbour1.value) {
|
for (neighbour of [box, neighbour]) {
|
||||||
for (neighbour of [neighbour1, neighbour2]) {
|
neighbour.setCustomValidity(`Il y a un autre ${box.value} dans cette ${area.name}.`)
|
||||||
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.form.checkValidity()) { // Correct grid
|
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)
|
setTimeout(() => alert(`Bravo ! Vous avez résolu la grille.`), 500)
|
||||||
|
saveButton.disabled = true
|
||||||
|
}
|
||||||
} else { // Errors on grid
|
} else { // Errors on grid
|
||||||
box.form.reportValidity()
|
box.form.reportValidity()
|
||||||
}
|
}
|
||||||
@ -200,7 +197,6 @@ function checkBox(box) {
|
|||||||
function refreshUI() {
|
function refreshUI() {
|
||||||
enableRadio()
|
enableRadio()
|
||||||
highlight()
|
highlight()
|
||||||
showEasyBoxes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableRadio() {
|
function enableRadio() {
|
||||||
@ -211,62 +207,42 @@ function enableRadio() {
|
|||||||
} else {
|
} else {
|
||||||
radio.disabled = true
|
radio.disabled = true
|
||||||
radio.label.title = `Tous les ${radio.value} sont posés.`
|
radio.label.title = `Tous les ${radio.value} sont posés.`
|
||||||
if (valueToInsert == radio.value) {
|
if (valueToInsert == radio.value)
|
||||||
let nextRadio = document.querySelector(".insertRadioGroup :checked ~ input:enabled") || document.querySelector(".insertRadioGroup :enabled")
|
valueToInsert = ""
|
||||||
if (nextRadio) {
|
|
||||||
nextRadio.click()
|
|
||||||
nextRadio.onfocus()
|
|
||||||
} else {
|
|
||||||
valueToInsert = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function highlight() {
|
function highlight() {
|
||||||
|
hintButton.disabled = true
|
||||||
|
easyBoxes = []
|
||||||
boxes.forEach(box => {
|
boxes.forEach(box => {
|
||||||
if (valueToInsert && box.value == valueToInsert) {
|
if (valueToInsert && box.value == valueToInsert) {
|
||||||
box.classList.add("same-value")
|
box.classList.add("same-value")
|
||||||
box.tabIndex = -1
|
box.tabIndex = -1
|
||||||
} else {
|
} else {
|
||||||
box.classList.remove("same-value")
|
box.classList.remove("same-value")
|
||||||
if (box.disabled) {
|
if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) {
|
||||||
box.classList.add("forbidden")
|
box.classList.add("forbidden")
|
||||||
|
box.tabIndex = -1
|
||||||
} else {
|
} else {
|
||||||
if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) {
|
box.classList.remove("forbidden")
|
||||||
box.classList.add("forbidden")
|
box.tabIndex = 0
|
||||||
box.tabIndex = -1
|
|
||||||
} else {
|
|
||||||
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é")
|
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() {
|
function onblur() {
|
||||||
if (this.classList.contains("pencil")) {
|
if (this.classList.contains("pencil")) {
|
||||||
this.placeholder = this.value
|
this.placeholder = this.value
|
||||||
this.value = ""
|
this.value = ""
|
||||||
|
//this.type = "number"
|
||||||
this.classList.remove("pencil")
|
this.classList.remove("pencil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,7 +268,10 @@ function undo() {
|
|||||||
previousState.box.value = previousState.value
|
previousState.box.value = previousState.value
|
||||||
previousState.box.placeholder = previousState.placeholder
|
previousState.box.placeholder = previousState.placeholder
|
||||||
refreshBox(previousState.box)
|
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("")
|
let saveGame = boxes.map(box => box.value || UNKNOWN).join("")
|
||||||
localStorage[location.pathname] = saveGame
|
localStorage[location.pathname] = saveGame
|
||||||
fixGridLink.href = saveGame
|
fixGridLink.href = saveGame
|
||||||
changesToSave = false
|
saveButton.disabled = true
|
||||||
|
alert("Partie sauvegardée")
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onbeforeunload = function(event) {
|
window.onbeforeunload = function(event) {
|
||||||
if (changesToSave) {
|
if (!saveButton.disabled) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.returnValue = ""
|
event.returnValue = "La partie n'est pas sauvegardée. Quitter quand même ?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSuggestion() {
|
function showHint() {
|
||||||
const easyBoxes = boxes.filter(box => box.value == "" && box.candidates.size == 1)
|
|
||||||
if (easyBoxes.length) {
|
if (easyBoxes.length) {
|
||||||
let randomEasyBox = shuffle(easyBoxes)[0]
|
shuffle(easyBoxes)
|
||||||
randomEasyBox.placeholder = "💡"
|
let box = easyBoxes.pop()
|
||||||
randomEasyBox.focus()
|
box.placeholder = "💡"
|
||||||
} else {
|
box.focus()
|
||||||
clearTimeout(suggestionTimer)
|
/*value = Array.from(box.candidates)[0]
|
||||||
suggestionTimer = null
|
radio = document.getElementById("insertRadio" + value)
|
||||||
|
radio.checked = true
|
||||||
|
insert(radio)*/
|
||||||
|
return box
|
||||||
}
|
}
|
||||||
|
hintButton.disabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function oncontextmenu(event) {
|
function oncontextmenu(event) {
|
||||||
@ -348,10 +331,9 @@ function oncontextmenu(event) {
|
|||||||
li.innerText = candidate
|
li.innerText = candidate
|
||||||
li.onclick = function (event) {
|
li.onclick = function (event) {
|
||||||
contextMenu.style.display = "none"
|
contextMenu.style.display = "none"
|
||||||
box.onfocus()
|
valueToInsert = event.target.innerText
|
||||||
box.value = event.target.innerText
|
document.getElementById("insertRadio" + valueToInsert).checked = true
|
||||||
box.oninput()
|
box.onclick()
|
||||||
box.onblur()
|
|
||||||
}
|
}
|
||||||
contextMenu.appendChild(li)
|
contextMenu.appendChild(li)
|
||||||
})
|
})
|
||||||
@ -363,14 +345,13 @@ function oncontextmenu(event) {
|
|||||||
}
|
}
|
||||||
contextMenu.style.left = `${event.pageX}px`
|
contextMenu.style.left = `${event.pageX}px`
|
||||||
contextMenu.style.top = `${event.pageY}px`
|
contextMenu.style.top = `${event.pageY}px`
|
||||||
console.log(event.target)
|
|
||||||
contextMenu.style.display = "block"
|
contextMenu.style.display = "block"
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
document.onclick = function (event) {
|
document.onclick = function (event) {
|
||||||
if (contextMenu.style.display == "block")
|
|
||||||
contextMenu.style.display = "none"
|
contextMenu.style.display = "none"
|
||||||
|
document.onclick = null
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
document.onkeydown = function(event) {
|
document.onkeydown = function(event) {
|
||||||
|
26
sudoku.php
26
sudoku.php
@ -4,7 +4,7 @@
|
|||||||
$currentGrid = strip_tags($_GET['grid']);
|
$currentGrid = strip_tags($_GET['grid']);
|
||||||
$_SESSION["currentGrid"] = $currentGrid;
|
$_SESSION["currentGrid"] = $currentGrid;
|
||||||
|
|
||||||
if (!isset($_SESSION[$currentGrid])) {
|
if (!in_array($currentGrid, $validGrids)) {
|
||||||
$grid = new Grid();
|
$grid = new Grid();
|
||||||
$grid->import($currentGrid);
|
$grid->import($currentGrid);
|
||||||
if ($grid->containsDuplicates()) {
|
if ($grid->containsDuplicates()) {
|
||||||
@ -15,6 +15,7 @@
|
|||||||
$warning = "Cette grille n'a pas de solution.";
|
$warning = "Cette grille n'a pas de solution.";
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
$validGrids[] = $currentGrid;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$warning = "Cette grille a plusieurs solutions.";
|
$warning = "Cette grille a plusieurs solutions.";
|
||||||
@ -58,6 +59,18 @@
|
|||||||
<header>
|
<header>
|
||||||
<h1>Sudoku</h1>
|
<h1>Sudoku</h1>
|
||||||
</header>
|
</header>
|
||||||
|
<section class="tools">
|
||||||
|
<div>
|
||||||
|
<input id='highlighterCheckbox' type="checkbox" onclick='highlight()'/><label for='highlighterCheckbox' title='Surligner les lignes, colonnes et régions contenant déjà le chiffre sélectionné'><img src='img/highlighter.svg' alt='Surligneur'></label>
|
||||||
|
<input type='radio' id='inkPenRadio' name='tool' onclick='grid.style.cursor = "url(img/ink-pen.svg) 2 22, auto"' checked/><label for='inkPenRadio' title='Écrire un chiffre'><img src='img/ink-pen.svg' alt='Stylo indélébile'/></label>
|
||||||
|
<input type='radio' id='pencilRadio' name='tool' onclick='grid.style.cursor = "url(img/pencil.svg) 2 22, auto"'/><label for='pencilRadio' title='Prendre des notes'><img src='img/pencil.svg' alt='Crayon'/></label>
|
||||||
|
<input type='radio' id='eraserRadio' name='tool' onclick='grid.style.cursor = "url(img/eraser.svg) 2 22, auto"'/><label for='eraserRadio' title='Effacer une case'><img src='img/eraser.svg' alt='Gomme'/></label>
|
||||||
|
<button type='button' class='warning' onclick='restart()' title='Recommencer'><img src='img/restart.svg' alt='Recommencer'/></button>
|
||||||
|
<button id='undoButton' type='button' onclick='undo()' disabled title='Annuler' accesskey='z'><img src='img/undo.svg' alt='Annuler'/></button>
|
||||||
|
<button id="hintButton" type="button" onclick="showHint()" title="Afficher un indice" accesskey="h" disabled=""><img src='img/light-bulb.svg' alt='Ampoule'/></button>
|
||||||
|
<button id='saveButton' type='button' onclick='save()' disabled title='Sauvegarder' accesskey='s'><img src='img/save.svg' alt='Disquette'/></button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
<form id='sudokuForm'>
|
<form id='sudokuForm'>
|
||||||
<table id='grid' class='grid'>
|
<table id='grid' class='grid'>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -94,15 +107,6 @@
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<input id='highlighterCheckbox' type="checkbox" onclick='highlight()'/><label for='highlighterCheckbox' title='Surligner les lignes, colonnes et régions contenant déjà le chiffre sélectionné'><img src='img/highlighter.svg' alt='Surligneur'></label>
|
|
||||||
<input type='radio' id='inkPenRadio' name='tool' onclick='grid.style.cursor = "url(img/ink-pen.svg) 2 22, auto"' checked/><label for='inkPenRadio' title='Écrire un chiffre'><img src='img/ink-pen.svg' alt='Stylo indélébile'/></label>
|
|
||||||
<input type='radio' id='pencilRadio' name='tool' onclick='grid.style.cursor = "url(img/pencil.svg) 2 22, auto"'/><label for='pencilRadio' title='Prendre des notes'><img src='img/pencil.svg' alt='Crayon'/></label>
|
|
||||||
<input type='radio' id='eraserRadio' name='tool' onclick='grid.style.cursor = "url(img/eraser.svg) 2 22, auto"'/><label for='eraserRadio' title='Effacer une case'><img src='img/eraser.svg' alt='Gomme'/></label>
|
|
||||||
<button type='button' class='warning' onclick='restart()' title='Recommencer'><img src='img/restart.svg' alt='Recommencer'/></button>
|
|
||||||
<button id='undoButton' type='button' onclick='undo()' disabled title='Annuler' accesskey='z'><img src='img/undo.svg' alt='Annuler'/></button>
|
|
||||||
<button id='undoButton' type='button' onclick='save()' title='Sauvegarder' accesskey='s'><img src='img/save.svg' alt='Disquette'/></button>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<?php
|
<?php
|
||||||
@ -122,7 +126,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class='credits'>
|
<div class='credits'>
|
||||||
Icônes par <a href='https://www.flaticon.com/authors/freepik' title='Freepik'>Freepik</a> chez <a href='https://www.flaticon.com/' title='Flaticon'>www.flaticon.com</a>
|
Icônes par <a href='https://www.flaticon.com/authors/freepik' title='Freepik' target="_blank">Freepik</a> chez <a href='https://www.flaticon.com/' title='Flaticon' target="_blank">www.flaticon.com</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
$thumbnail = imagecreate($size, $size);
|
$thumbnail = imagecreate($size, $size);
|
||||||
$transparent = imagecolorallocate($thumbnail, 1, 1, 1);
|
$transparent = imagecolorallocate($thumbnail, 1, 1, 1);
|
||||||
imagecolortransparent($thumbnail, $transparent);
|
imagecolortransparent($thumbnail, $transparent);
|
||||||
$black = imagecolorallocate($thumbnail, 0, 0, 0);
|
$black = imagecolorallocate($thumbnail, 85, 91, 110);
|
||||||
$grey = imagecolorallocate($thumbnail, 128, 128, 128);
|
$grey = imagecolorallocate($thumbnail, 85, 91, 110);
|
||||||
$blue = imagecolorallocate($thumbnail, 102, 102, 255);
|
$blue = imagecolorallocate($thumbnail, 96, 210, 203);
|
||||||
$white = imagecolorallocate($thumbnail, 255, 255, 255);
|
$white = imagecolorallocate($thumbnail, 247, 255, 247);
|
||||||
|
|
||||||
if ($size <= 36) {
|
if ($size <= 36) {
|
||||||
$boxSize = floor(($size-4) / 9);
|
$boxSize = floor(($size-4) / 9);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user