Compare commits

..

No commits in common. "fa970170d4ec384a11d77d12fbc631868ed6553a" and "ca22cb129ddedcff389cd2a16059ba969b93d299" have entirely different histories.

28 changed files with 1540 additions and 2434 deletions

22
400.php
View File

@ -1,22 +0,0 @@
<?php http_response_code(400); ?>
<!DOCTYPE html>
<html lang='fr'>
<head>
<?php require("head.php"); ?>
<title>Requête incorrecte</title>
</head>
<body>
<header>
<h1>Requête incorrecte</h1>
</header>
L'adresse URL doit être de la forme :<br/>
<?=$dirUrl?>/?<em>grille</em><br/>
<em>grille</em> étant une suite de 81 caractères représentant la grille de gauche à droite puis de haut en bas, soit :
<ul>
<li>un chiffre entre 1 et 9 pour les cases connues</li>
<li>un point pour les case vides</li>
</ul>
Exemple :<br/>
<a href='<?=$newGridUrl?>'><?=$newGridUrl?></a>
</body>
</html>

54
404.php Normal file
View File

@ -0,0 +1,54 @@
<?php
require("classes.php");
$grid = new Grid();
$grid->generate();
header("HTTP/1.0 404 Not Found", true, 404);
$urlDir = $_SERVER["REQUEST_SCHEME"] . "://" . $_SERVER["HTTP_HOST"] . dirname($_SERVER["DOCUMENT_URI"]);
$urlExample = $urlDir . "/" . $grid->toString();
?>
<!DOCTYPE html>
<html lang='fr'>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width' />
<title>Sudoku non trouvé</title>
<link rel='stylesheet' type='text/css' href='style.css' />
<link rel="apple-touch-icon" href="thumbnail.php?size=57" sizes="57x57">
<link rel="apple-touch-icon" href="thumbnail.php?size=114" sizes="114x114">
<link rel="apple-touch-icon" href="thumbnail.php?size=72" sizes="72x72">
<link rel="apple-touch-icon" href="thumbnail.php?size=144" sizes="144x144">
<link rel="apple-touch-icon" href="thumbnail.php?size=60" sizes="60x60">
<link rel="apple-touch-icon" href="thumbnail.php?size=120" sizes="120x120">
<link rel="apple-touch-icon" href="thumbnail.php?size=76" sizes="76x76">
<link rel="apple-touch-icon" href="thumbnail.php?size=152" sizes="152x152">
<link rel="icon" type="image/png" href="thumbnail.php?size=196" sizes="196x196">
<link rel="icon" type="image/png" href="thumbnail.php?size=160" sizes="160x160">
<link rel="icon" type="image/png" href="thumbnail.php?size=96" sizes="96x96">
<link rel="icon" type="image/png" href="thumbnail.php?size=16" sizes="16x16">
<link rel="icon" type="image/png" href="thumbnail.php?size=32" sizes="32x32">
<link rel="manifest" href="manifest.php">
<meta property="og:title" content="Sudoku"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="<?=$_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].$_SERVER["DOCUMENT_URI"]?>"/>
<meta property="og:image" content="<?=$_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].dirname($_SERVER["DOCUMENT_URI"])?>/thumbnail.php?size=200"/>
<meta property="og:image:width" content="200"/>
<meta property="og:image:height" content="200"/>
<meta property="og:description" content="Remplissez la grille de sorte que chaque ligne, colonne et région (carré de 3×3 cases) contienne tous les chiffres de 1 à 9."/>
<meta property="og:locale" content="fr_FR"/>
<meta property="og:site_name" content="<?=$_SERVER["HTTP_HOST"]?>"/>
</head>
<body>
<header>
<h1>#404</h1>
</header>
L'adresse URL doit être de la forme : <?=$urlDir?>/<em>grille</em>,<br/>
<em>grille</em> étant une suite de 81 caractères représentant la grille de gauche à droite puis de haut en bas, soit :
<ul>
<li>un chiffre entre 1 et 9 pour les cases connues</li>
<li>un point pour les case vides</li>
</ul>
Exemple : <a href='<?=$urlExample?>'><?=$urlExample?></a><br/>
</body>
</html>

View File

@ -85,6 +85,49 @@
array_unset_value($box->value, $neighbour->candidates);
}
}
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();
// Remove clues while there is still a unique solution
shuffle($this->boxes);
$nbClues = count($this->boxes);
foreach($this->boxes as $testBox) {
$testBoxes = array($testBox);
if ($nbClues >= 30)
$testBoxes[] = $this->rows[8-$testBox->rowId][8-$testBox->columnId];
if ($nbClues >= 61) {
$testBoxes[] = $this->rows[8-$testBox->rowId][$testBox->columnId];
$testBoxes[] = $this->rows[$testBox->rowId][8-$testBox->columnId];
}
$testBoxes = array_filter($testBoxes, "isKnown");
$erasedValues = array();
forEach($testBoxes as $testBox) {
$erasedValues[] = $testBox->value;
$testBox->value = UNKNOWN;
forEach($testBox->neighbourhood as $neighbour)
$neighbour->searchCandidates();
}
if ($this->isValid()) {
$nbClues -= count($testBoxes);
} else {
forEach($testBoxes as $i => $testBox) {
$testBox->value = $erasedValues[$i];
forEach($testBox->neighbourhood as $neighbour) array_unset_value($testBox->value, $neighbour->candidates);
}
}
}
$validGrids[] = $this->toString();
}
function generate() {
// Init with a shuffle row

File diff suppressed because one or more lines are too long

2018
css/bootstrap-icons.css vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,135 +0,0 @@
body {
width: min-content;
margin: auto;
}
input[type="number"]::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
margin: 0 !important;
}
input[type="number"]::-webkit-calendar-picker-indicator {
display: none !important;
}
table {
border-collapse: separate;
border-spacing: 0;
}
table input {
width: 2.5rem !important;
height: 2.5rem !important;
font-size: 1.3rem !important;
padding: 0 !important;
margin: 0 !important;
text-align: center;
-moz-appearance: textfield !important;
border-radius: 0 !important;
}
table td.table-primary input,
table td.table-active input,
table input:not([disabled]) {
background: transparent !important;
}
tr:nth-child(3n+1) td input {
border-top-width: 3px !important;
}
tr:last-child td input {
border-bottom-width: 3px !important;
}
td:nth-child(3n+1) input {
border-left-width: 3px !important;
}
td:last-child input {
border-right-width: 3px !important;
}
tr:first-child td:first-child {
border-top-left-radius: .7rem !important;
}
tr:first-child td:first-child input {
border-top-left-radius: .5rem !important;
}
tr:first-child td:last-child {
border-top-right-radius: .7rem !important;
}
tr:first-child td:last-child input {
border-top-right-radius: .5rem !important;
}
tr:last-child td:first-child {
border-bottom-left-radius: .7rem !important;
}
tr:last-child td:first-child input {
border-bottom-left-radius: .5rem !important;
}
tr:last-child td:last-child {
border-bottom-right-radius: .7rem !important;
}
tr:last-child td:last-child input {
border-bottom-right-radius: .5rem !important;
}
td {
padding: 0 !important;
margin: 0 !important;
transition: background-color .4s, box-shadow .4s !important;
border: 0 !important;
}
.context-menu li {
cursor: default;
}
.table-active {
cursor: not-allowed !important;
}
table {
cursor: text;
}
button,
label {
cursor: pointer;
}
button:disabled,
:disabled+label {
cursor: not-allowed !important;
}
table input:enabled {
cursor: inherit;
}
.modal-content {
z-index: 1000;
}
.bi::before {
vertical-align: 0;
}
.pencil {
color: var(--bs-secondary-color) !important;
}
@media (prefers-color-scheme:dark) {
.pencil {
color: #5a5a5a !important;
}
}

Binary file not shown.

Binary file not shown.

View File

@ -1,36 +0,0 @@
<meta charset="utf-8" />
<title>Sudoku</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="css/bootstrap-dark.min.css" rel="stylesheet" type="text/css" title="Automatique" />
<link href="css/bootstrap.min.css" rel="alternate stylesheet" type="text/css" title="Clair">
<link href="css/bootstrap-night.min.css" rel="alternate stylesheet" type="text/css" title="Sombre">
<link rel="stylesheet" type="text/css" href="css/bootstrap-icons.css" />
<link rel="stylesheet" type="text/css" href="css/style.css" />
<link rel="apple-touch-icon" href="thumbnail.php?size=57" sizes="57x57">
<link rel="apple-touch-icon" href="thumbnail.php?size=114" sizes="114x114">
<link rel="apple-touch-icon" href="thumbnail.php?size=72" sizes="72x72">
<link rel="apple-touch-icon" href="thumbnail.php?size=144" sizes="144x144">
<link rel="apple-touch-icon" href="thumbnail.php?size=60" sizes="60x60">
<link rel="apple-touch-icon" href="thumbnail.php?size=120" sizes="120x120">
<link rel="apple-touch-icon" href="thumbnail.php?size=76" sizes="76x76">
<link rel="apple-touch-icon" href="thumbnail.php?size=152" sizes="152x152">
<link rel="icon" type="image/png" href="thumbnail.php?size=196" sizes="196x196">
<link rel="icon" type="image/png" href="thumbnail.php?size=160" sizes="160x160">
<link rel="icon" type="image/png" href="thumbnail.php?size=96" sizes="96x96">
<link rel="icon" type="image/png" href="thumbnail.php?size=16" sizes="16x16">
<link rel="icon" type="image/png" href="thumbnail.php?size=32" sizes="32x32">
<link rel="manifest" href="manifest.php">
<meta property="og:title" content="Sudoku" />
<meta property="og:type" content="website" />
<meta property="og:url"
content="<?=$_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].$_SERVER["DOCUMENT_URI"]?>" />
<meta property="og:image"
content="<?=$_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].dirname($_SERVER["DOCUMENT_URI"])?>/thumbnail.php?size=200" />
<meta property="og:image:width" content="200" />
<meta property="og:image:height" content="200" />
<meta property="og:description"
content="Remplissez la grille de sorte que chaque ligne, colonne et région (carré de 3×3 cases) contienne tous les chiffres de 1 à 9." />
<meta property="og:locale" content="fr_FR" />
<meta property="og:site_name" content="<?=$_SERVER["HTTP_HOST"]?>" />
<script src='js/sudoku.js'></script>
<script src="js/bootstrap.bundle.min.js" integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N" crossorigin="anonymous"></script>

1
img/eraser.svg Normal file
View File

@ -0,0 +1 @@
<svg id="Capa_1" enable-background="new 0 0 512 512" height="24" viewBox="0 0 512 512" width="24" xmlns="http://www.w3.org/2000/svg"><g><path d="m214.28 498.762c4.098 0 8.029-1.628 10.927-4.526l278.144-278.144c11.398-11.398 11.398-29.879 0-41.277l-152.928-152.928c-11.398-11.398-29.878-11.398-41.277 0l-300.597 300.598c-11.398 11.398-11.398 29.878 0 41.277l130.474 130.474c2.898 2.898 6.828 4.526 10.927 4.526z" fill="#d3e1f5"/><path d="m275.208 386.418-78.91 78.91c-2.898 2.898-6.828 4.526-10.927 4.526h-64.331c-4.098 0-8.029-1.628-10.927-4.526l28.909 28.909c2.898 2.898 6.829 4.526 10.927 4.526h64.331c4.098 0 8.029-1.628 10.927-4.526l78.91-78.91z" fill="#c0d6f2"/><path d="m505.059 176.523-156.344-156.344c-10.455-10.455-28.935-8.925-41.277 3.417l-228.75 228.75 194.205 194.204 228.75-228.75c12.341-12.342 13.871-30.822 3.416-41.277z" fill="#0473ce"/><path d="m187.955 245.061 134.545-134.545c4.023-4.023 10.546-4.023 14.569 0l77.653 77.653c4.023 4.023 4.023 10.546 0 14.569l-134.545 134.545c-4.023 4.023-10.546 4.023-14.569 0l-77.653-77.653c-4.023-4.023-4.023-10.546 0-14.569z" fill="#0055a3"/><path d="m505.059 176.523-28.908-28.909c10.455 10.455 8.925 28.935-3.417 41.277l-228.75 228.75 28.909 28.909 228.75-228.75c12.341-12.342 13.871-30.822 3.416-41.277z" fill="#0067c5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

44
img/highlighter.svg Normal file
View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<polygon style="fill:#FFCC75;" points="47.202,417.53 94.404,464.732 47.202,511.945 0,464.743 "/>
<polygon style="fill:#082947;" points="236.149,414.811 212.545,438.4 111.564,368.892 166.634,313.823 "/>
<polygon style="fill:#274B6D;" points="182.37,329.559 127.301,384.628 73.529,299.384 97.126,275.788 "/>
<polygon style="fill:#FF9D49;" points="512,118.078 452.926,59.003 294.044,186.413 166.634,345.296 251.886,430.548 "/>
<path style="fill:#274B6D;" d="M143.037,368.892l69.508,69.508c0,0-61.136,0.802-102.397,42.063l-39.35-39.333l20.383-51.855
L143.037,368.892z"/>
<polygon style="fill:#FFCC75;" points="393.977,0.055 81.39,260.051 166.634,345.296 452.926,59.003 "/>
<path style="fill:#185F8D;" d="M73.529,299.384l69.508,69.508l-72.239,72.239L31.458,401.79
C72.719,360.529,73.529,299.384,73.529,299.384z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

48
img/ink-pen.svg Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 511.989 511.989" style="enable-background:new 0 0 511.989 511.989;" xml:space="preserve" width="24" height="24">
<rect x="408.399" y="26.854" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 819.7593 -231.0359)" style="fill:#9EC8E8;" width="98.659" height="54.811"/>
<polygon style="fill:#082947;" points="430.604,174.402 186.029,418.976 124.017,372.468 376.343,120.141 "/>
<polygon style="fill:#274B6D;" points="384.094,127.892 131.769,380.219 93.012,325.958 337.586,81.384 "/>
<polygon style="fill:#4284B2;" points="477.112,127.892 430.604,174.402 376.343,120.141 430.604,81.384 "/>
<rect x="20.377" y="439.543" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 -273.1405 837.2019)" style="fill:#082947;" width="32.886" height="71.254"/>
<polygon style="fill:#4284B2;" points="186.029,418.976 69.757,488.74 46.502,465.484 77.509,403.473 139.52,372.468 "/>
<g>
<polygon style="fill:#6DA8D6;" points="139.52,372.468 46.502,465.484 23.248,442.23 93.012,325.958 "/>
<rect x="351.209" y="48.511" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 713.2485 -132.6418)" style="fill:#6DA8D6;" width="65.773" height="65.773"/>
</g>
<polygon style="fill:#9EC8E8;" points="178.681,193.781 155.426,170.527 302.704,23.249 442.229,162.774 418.975,186.028
302.704,69.757 "/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

1
img/light-bulb.svg Normal file
View 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

45
img/pencil.svg Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="24" height="24">
<polygon style="fill:#FF9D49;" points="393.29,213.665 142.441,464.524 87.044,424.954 345.817,150.364 "/>
<polygon style="fill:#FFCC75;" points="353.73,158.277 87.044,424.954 47.473,369.557 298.333,118.708 "/>
<polygon style="fill:#BD1515;" points="512,94.965 440.78,166.185 377.475,118.708 464.522,47.486 "/>
<polygon style="fill:#F2484B;" points="464.516,47.482 385.382,126.615 345.817,71.224 417.038,0.002 "/>
<g>
<rect x="335.974" y="75.292" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 731.5841 -18.1559)" style="fill:#082947;" width="67.157" height="134.292"/>
<polygon style="fill:#082947;" points="71.22,488.261 0,511.998 23.736,440.776 63.305,448.692 "/>
</g>
<polygon style="fill:#F2EBD9;" points="47.473,369.557 23.736,440.776 71.22,488.261 142.441,464.524 "/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

55
img/restart.svg Normal file
View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="574.859px" height="574.86px" viewBox="0 0 574.859 574.86" style="enable-background:new 0 0 574.859 574.86;"
xml:space="preserve">
<g>
<path d="M181.688,521.185V353.841H19.125v167.344c0,10.566,13.34,23.906,23.906,23.906h124.312
C177.91,545.091,181.688,531.751,181.688,521.185z M66.938,502.06c0,2.64-2.142,4.781-4.781,4.781s-4.781-2.142-4.781-4.781
V377.748c0-2.64,2.142-4.781,4.781-4.781s4.781,2.142,4.781,4.781V502.06z M105.188,502.06c0,2.64-2.142,4.781-4.781,4.781
s-4.781-2.142-4.781-4.781V377.748c0-2.64,2.142-4.781,4.781-4.781s4.781,2.142,4.781,4.781V502.06z M143.438,502.06
c0,2.64-2.142,4.781-4.781,4.781s-4.781-2.142-4.781-4.781V377.748c0-2.64,2.142-4.781,4.781-4.781s4.781,2.142,4.781,4.781V502.06
z"/>
<path d="M19.125,334.716h162.562v-19.125h19.125v-19.125h-57.375c0-10.566-6.828-19.125-15.243-19.125H77.399
c-8.415,0-15.243,8.559-15.243,19.125H0v19.125h19.125V334.716z"/>
<path d="M357.007,191.556C370.968,329.811,243.892,542.08,243.892,542.08c145.235-78.212,169.189-207.363,169.189-207.363
c42.333,66.479,44.475,228.305,44.475,228.305c80.995-194.109,0-377.049,0-377.049l117.304,48.874
c-19.546-74.014-141.047-125.68-141.047-125.68c-110.322,50.27-249.974,44.686-249.974,44.686
C259.249,226.469,357.007,191.556,357.007,191.556z"/>
<circle cx="369.782" cy="55.128" r="43.29"/>
<path d="M94.43,229.529c5.977-2.391,27.492-13.148,28.764,0c1.271,13.148,11.876,9.562,19.048,0s3.586-25.102,11.953-23.906
s15.539-10.758,17.93-21.735c2.391-10.978-22.711-18.905-33.469-21.458s-20.32,13.321-27.492,13.321s-17.93-20.33-25.102-10.768
s-11.953,40.641-11.953,40.641c-10.758-5.977-21.516,7.172-25.102,16.734S88.453,231.919,94.43,229.529z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

43
img/save.svg Executable file
View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#DCF4FF;" d="M512,492H0V20h403.213L512,128.787V492z"/>
<polygon style="fill:#CCEFFF;" points="403.213,20 256,20 256,492 512,492 512,128.787 "/>
<path style="fill:#0A4EAF;" d="M403.213,20H396v175H116V20H0v472h116V315c0-16.568,13.431-30,30-30h220c16.569,0,30,13.432,30,30
v177h116V128.787L403.213,20z"/>
<g>
<rect x="296" y="20" style="fill:#063E8B;" width="40" height="115"/>
<path style="fill:#063E8B;" d="M403.213,20H396v175H256v90h110c16.569,0,30,13.432,30,30v177h116V128.787L403.213,20z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

46
img/undo.svg Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="299.021px" height="299.021px" viewBox="0 0 299.021 299.021" style="enable-background:new 0 0 299.021 299.021;"
xml:space="preserve">
<g>
<g>
<path d="M292.866,254.432c-2.288,0-4.443-1.285-5.5-3.399c-0.354-0.684-28.541-52.949-146.169-54.727v51.977
c0,2.342-1.333,4.48-3.432,5.513c-2.096,1.033-4.594,0.793-6.461-0.63L2.417,154.392C0.898,153.227,0,151.425,0,149.516
c0-1.919,0.898-3.72,2.417-4.888l128.893-98.77c1.87-1.426,4.365-1.667,6.461-0.639c2.099,1.026,3.432,3.173,3.432,5.509v54.776
c3.111-0.198,7.164-0.37,11.947-0.37c43.861,0,145.871,13.952,145.871,143.136c0,2.858-1.964,5.344-4.75,5.993
C293.802,254.384,293.34,254.432,292.866,254.432z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,40 +1,10 @@
<?php
<?php
require("classes.php");
session_start();
$fullUrl = $_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].$_SERVER["DOCUMENT_URI"];
$dirUrl = dirname($fullUrl);
$currentGrid = strip_tags($_SERVER['QUERY_STRING']);
if (preg_match("/^[1-9.]{81}$/", $currentGrid)) {
if (!isset($_SESSION[$currentGrid]) || $_SESSION[$currentGrid] != "checked") {
$grid = new Grid();
$grid->import($currentGrid);
if ($grid->containsDuplicates()) {
$warning = "Cette grille contient des doublons.";
} else {
switch($grid->countSolutions(2)) {
case 0:
$warning = "Cette grille n'a pas de solution.";
break;
case 1:
$validGrids[] = $currentGrid;
break;
default:
$warning = "Cette grille a plusieurs solutions.";
}
}
}
require("sudoku.php");
} else {
$grid = new Grid();
$grid->generate();
$gridAsString = $grid->toString();
$newGridUrl = "$dirUrl/?$gridAsString";
$_SESSION[$gridAsString] = "checked";
if (!$currentGrid) {
header("Location: $newGridUrl");
} else {
require("400.php");
}
}
?>
$grid = new Grid();
$grid->generate();
$currentGrid = $grid->toString();
$_SESSION[$currentGrid] = "checked";
header("Location: ".$_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].dirname($_SERVER["DOCUMENT_URI"])."/".$currentGrid);
exit();
?>

File diff suppressed because one or more lines are too long

29
nginx-example.conf Normal file
View File

@ -0,0 +1,29 @@
location /sudoku/ {
alias /var/www/sudoku/;
if ($scheme = http) {
rewrite ^ https://$server_name$request_uri? permanent;
}
try_files $uri $uri/;
location ~ [^/]\.php(/|$) {
try_files $uri =404;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_pass unix:/var/run/php/php7.3-fpm-sudoku.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
error_page 404 /sudoku/404.php;
location /sudoku/404.php {
internal;
}
}
location ~ "^/sudoku/([1-9.]{81})$" {
rewrite "^/sudoku/([1-9.]{81})\??(.*)?(#.*)?$" /sudoku/sudoku.php?grid=$1&$2$3 last;
}

325
style.css Normal file
View File

@ -0,0 +1,325 @@
: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;
}

308
style.css.bak Normal file
View File

@ -0,0 +1,308 @@
body {
font-family: sans-serif;
width: min-content;
margin: auto;
}
h1 {
text-align: center;
margin: 1rem;
}
section, div, footer {
align-items: center;
align-items: center;
justify-content: center;
text-align: center;
margin: 0.8rem 0;
}
div {
display: flex;
flex-wrap: wrap;
row-gap: 0.5rem;
column-gap: 0.3rem;
margin: 0.5rem auto;
}
.grid {
border-spacing: 0;
border: 1px solid black;
border-radius: 6px;
margin: auto;
cursor: url(img/ink-pen.svg) 2 22, text;
}
.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;
}
.grid tr:nth-child(3n+2) td {
border-top: 1px solid grey;
border-bottom: 1px solid grey;
}
.grid tr:nth-child(3n) td {
border-bottom: 1px solid black;
}
.grid td:nth-child(3n+1) {
border-left: 1px solid black;
}
.grid td:nth-child(3n+2) {
border-left: 1px solid grey;
border-right: 1px solid grey;
}
.grid td:nth-child(3n+3) {
border-right: 1px solid black;
}
.grid input {
width: 2.5rem;
height: 2.5rem;
font-size: 1.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 {
background: white;
color: darkblue;
cursor: inherit;
}
.grid input.pencil,
.grid input::placeholder {
color: #666 !important;
font-size: 0.9rem !important;
}
.grid input:disabled {
color: white !important;
background: #6666ff !important;
cursor: default;
}
.grid input.forbidden:enabled {
background: #f9f99f;
cursor: not-allowed;
}
.grid input.same-value:enabled {
background: #ffff33;
cursor: not-allowed !important;
}
.grid input.forbidden:disabled {
color: #f9f99f;
background: #6666ff;
cursor: not-allowed;
}
.grid input.same-value:disabled,
.tools button.same-value:enabled,
.tools input:enabled:checked + label {
color: #f9f99f !important;
background: #00b359 !important;
}
.grid input.one-candidate {
cursor: help !important;
}
.tools button,
.tools input + label {
color: white;
text-shadow: -1px -1px #5b6c9e;
background: #8ca6f2;
border: 2px outset #8ca6f2;
border-radius: 4px;
font-size: 1.3rem;
min-width:20px;
padding: 4px 5px 5px 5px;
margin: 0px 1px 1px 1px;
cursor: pointer;
}
.tools img {
display: block;
width: 24px;
height: 24px;
}
.tools input {
position: fixed;
opacity: 0;
width: 0;
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 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;
}
.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: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;
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;
}
.credits {
font-size: 0.8rem;
margin: 0;
}
@media (prefers-color-scheme: dark) {
body {
color: #eee;
background: #121212;
}
body a {
color: #809fff;
}
}

328
style.css.bak2 Normal file
View 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;
}

View File

@ -6,6 +6,7 @@ let boxes = []
let rows = Array.from(Array(9), x => [])
let columns = Array.from(Array(9), x => [])
let regions = Array.from(Array(9), x => [])
let suggestionTimer = null
let valueToInsert = ""
let history = []
let accessKeyModifiers = "AccessKey+"
@ -25,7 +26,7 @@ function shuffle(iterable) {
return array
}
window.onload = function() {
window.onload = function () {
let rowId = 0
for (let row of grid.getElementsByTagName('tr')) {
let columnId = 0
@ -52,9 +53,6 @@ window.onload = function() {
rowId++
}
if (localStorage["highlighterCheckbox.checked"]) {
highlighterCheckbox.checked = true
}
loadSavedGame()
boxes.forEach(box => {
@ -84,7 +82,7 @@ window.onload = function() {
}
function loadSavedGame() {
const savedGame = localStorage[location.search]
const savedGame = localStorage[location.pathname]
if (savedGame) {
boxes.forEach((box, i) => {
if (!box.disabled && savedGame[i] != UNKNOWN) {
@ -92,7 +90,7 @@ function loadSavedGame() {
box.previousValue = savedGame[i]
}
})
fixGridLink.href = "?" + savedGame
fixGridLink.href = savedGame
}
}
@ -121,7 +119,7 @@ function onfocus() {
} else {
this.select()
}
this.style.caretColor = valueToInsert ? "transparent" : "auto"
this.style.caretColor = valueToInsert? "transparent": "auto"
}
function onclick() {
@ -145,14 +143,9 @@ function onclick() {
}
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
saveButton.disabled = false
restartButton.disabled = false
if (pencilRadio.checked) {
this.previousValue = ""
this.previousPlaceholder = this.value
@ -171,7 +164,6 @@ function refreshBox(box) {
function checkBox(box) {
box.neighbourhood.concat([box]).forEach(neighbour => {
neighbour.setCustomValidity("")
neighbour.classList.remove("is-invalid")
searchCandidatesOf(neighbour)
if (neighbour.candidates.size == 0) {
neighbour.setCustomValidity("Aucun chiffre possible !")
@ -179,32 +171,23 @@ function checkBox(box) {
})
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}.`)
neighbour.classList.add("is-invalid")
}
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) {
setTimeout(() => alert(`Bravo ! Vous avez résolu la grille.`), 500)
saveButton.disabled = true
setTimeout(() => {
if (confirm(`Bravo ! Vous avez résolu la grille. En voulez-vous une autre ?`))
location = "."
}, 400)
}
} else { // Errors on grid
box.form.reportValidity()
@ -235,33 +218,31 @@ function highlight() {
easyBoxes = []
boxes.forEach(box => {
if (valueToInsert && box.value == valueToInsert) {
box.parentElement.classList.add("table-primary")
box.classList.add("same-value")
box.tabIndex = -1
} else {
box.parentElement.classList.remove("table-primary")
box.tabIndex = 0
box.classList.remove("same-value")
if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) {
box.classList.add("forbidden")
box.tabIndex = -1
} else {
box.classList.remove("forbidden")
box.tabIndex = 0
}
}
if (valueToInsert && highlighterCheckbox.checked && !box.candidates.has(valueToInsert)) {
box.parentElement.classList.add("table-active")
box.tabIndex = -1
} else {
box.parentElement.classList.remove("table-active")
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 onblur() {
if (this.classList.contains("pencil")) {
this.placeholder = this.value
this.value = ""
//this.type = "number"
//this.type = "number"
this.classList.remove("pencil")
}
}
@ -273,7 +254,11 @@ function insert(radio) {
} else {
valueToInsert = radio.value
}
grid.style.cursor = valueToInsert ? "copy" : "text"
if (inkPenRadio.checked) customCursor = "url(img/ink-pen.svg) 2 22"
if (pencilRadio.checked) customCursor = "url(img/pencil.svg) 2 22"
if (eraserRadio.checked) customCursor = "url(img/eraser.svg) 2 22"
fallbackCursor = valueToInsert? "copy": "text"
grid.style.cursor = `${customCursor}, ${fallbackCursor}`
highlight()
}
@ -301,7 +286,6 @@ function restart() {
})
let history = []
undoButton.disabled = true
restartButton.disabled = true
boxes.forEach(searchCandidatesOf)
refreshUI()
}
@ -309,14 +293,13 @@ function restart() {
function save() {
let saveGame = boxes.map(box => box.value || UNKNOWN).join("")
localStorage[location.search] = saveGame
fixGridLink.href = "?" + saveGame
localStorage[location.pathname] = saveGame
fixGridLink.href = saveGame
saveButton.disabled = true
alert("Partie sauvegardée")
}
window.onbeforeunload = function(event) {
localStorage["highlighterCheckbox.checked"] = highlighterCheckbox.checked
if (!saveButton.disabled) {
event.preventDefault()
event.returnValue = "La partie n'est pas sauvegardée. Quitter quand même ?"
@ -329,6 +312,10 @@ function showHint() {
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
@ -342,18 +329,12 @@ function oncontextmenu(event) {
Array.from(box.candidates).sort().forEach(candidate => {
li = document.createElement("li")
li.innerText = candidate
li.classList = "list-group-item list-group-item-action"
li.onclick = function(e) {
li.onclick = function (event) {
contextMenu.style.display = "none"
valueToInsert = e.target.innerText
grid.style.cursor = "copy"
valueToInsert = event.target.innerText
document.getElementById("insertRadio" + valueToInsert).checked = true
box.onclick()
}
li.oncontextmenu = function(e) {
e.preventDefault()
li.onclick(e)
}
contextMenu.appendChild(li)
})
} else {
@ -366,7 +347,7 @@ function oncontextmenu(event) {
contextMenu.style.top = `${event.pageY}px`
contextMenu.style.display = "block"
document.onclick = function(event) {
document.onclick = function (event) {
contextMenu.style.display = "none"
document.onclick = null
}
@ -378,4 +359,4 @@ document.onkeydown = function(event) {
event.preventDefault()
contextMenu.style.display = "none"
}
}
}

View File

@ -1,102 +1,133 @@
<?php
require("classes.php");
session_start();
$currentGrid = strip_tags($_GET['grid']);
$_SESSION["currentGrid"] = $currentGrid;
if (!in_array($currentGrid, $validGrids)) {
$grid = new Grid();
$grid->import($currentGrid);
if ($grid->containsDuplicates()) {
$warning = "Cette grille contient des doublons.";
} else {
switch($grid->countSolutions(2)) {
case 0:
$warning = "Cette grille n'a pas de solution.";
break;
case 1:
$validGrids[] = $currentGrid;
break;
default:
$warning = "Cette grille a plusieurs solutions.";
}
}
}
?>
<!DOCTYPE html>
<html lang='fr' prefix="og: https://ogp.me/ns#">
<head>
<?php require_once("head.php") ?>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width' />
<title>Sudoku</title>
<link rel='stylesheet' type='text/css' href='style.css' />
<script src='sudoku.js'></script>
<link rel="apple-touch-icon" href="thumbnail.php?size=57" sizes="57x57">
<link rel="apple-touch-icon" href="thumbnail.php?size=114" sizes="114x114">
<link rel="apple-touch-icon" href="thumbnail.php?size=72" sizes="72x72">
<link rel="apple-touch-icon" href="thumbnail.php?size=144" sizes="144x144">
<link rel="apple-touch-icon" href="thumbnail.php?size=60" sizes="60x60">
<link rel="apple-touch-icon" href="thumbnail.php?size=120" sizes="120x120">
<link rel="apple-touch-icon" href="thumbnail.php?size=76" sizes="76x76">
<link rel="apple-touch-icon" href="thumbnail.php?size=152" sizes="152x152">
<link rel="icon" type="image/png" href="thumbnail.php?size=196" sizes="196x196">
<link rel="icon" type="image/png" href="thumbnail.php?size=160" sizes="160x160">
<link rel="icon" type="image/png" href="thumbnail.php?size=96" sizes="96x96">
<link rel="icon" type="image/png" href="thumbnail.php?size=16" sizes="16x16">
<link rel="icon" type="image/png" href="thumbnail.php?size=32" sizes="32x32">
<link rel="manifest" href="manifest.php">
<meta property="og:title" content="Sudoku"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="<?=$_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].$_SERVER["DOCUMENT_URI"]?>"/>
<meta property="og:image" content="<?=$_SERVER["REQUEST_SCHEME"]."://".$_SERVER["HTTP_HOST"].dirname($_SERVER["DOCUMENT_URI"])?>/thumbnail.php?size=200"/>
<meta property="og:image:width" content="200"/>
<meta property="og:image:height" content="200"/>
<meta property="og:description" content="Remplissez la grille de sorte que chaque ligne, colonne et région (carré de 3×3 cases) contienne tous les chiffres de 1 à 9."/>
<meta property="og:locale" content="fr_FR"/>
<meta property="og:site_name" content="<?=$_SERVER["HTTP_HOST"]?>"/>
</head>
<body class="text-center">
<body>
<header>
<h1 class="display-4 mb-3">Sudoku</h1>
<h1>Sudoku</h1>
</header>
<div class='d-flex justify-content-between mb-2'>
<div class='radioGroup btn-group'>
<input type='radio' id='inkPenRadio' class='btn-check' name='tool' checked />
<label for='inkPenRadio' class='btn btn-primary' title='Écrire un chiffre'>
<i class="bi bi-pen-fill"></i>
</label>
<input type='radio' id='pencilRadio' class='btn-check' name='tool' />
<label for='pencilRadio' class='btn btn-primary' title='Prendre des notes'>
<i class="bi bi-pencil-fill"></i>
</label>
<input type='radio' id='eraserRadio' class='btn-check' name='tool' '/>
<label for='eraserRadio' class='btn btn-primary' title='Effacer une case'>
<i class="bi bi-eraser-fill"></i>
</label>
<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>
<input id='highlighterCheckbox' type="checkbox" class='btn-check' onclick='highlight()' />
<label for='highlighterCheckbox' class='btn btn-primary' title='Surligner les lignes, colonnes et régions contenant déjà le chiffre sélectionné'>
<i class="bi bi-magic"></i>
</label>
<button id="hintButton" type="button" class='btn btn-primary' onclick="showHint()" title="Montrer une case avec une seule possibilité" accesskey="H" disabled="">
<i class="bi bi-lightbulb-fill"></i>
</button>
<button id='restartButton' type='button' class='btn btn-primary' onclick='restart()' disabled title='Recommencer'>
<i class="bi bi-x-circle-fill"></i></i>
</button>
<button id='undoButton' type='button' class='btn btn-primary' onclick='undo()' disabled title='Annuler' accesskey='Z'>
<i class="bi bi-arrow-counterclockwise"></i>
</button>
<button id='saveButton' type='button' class='btn btn-primary' onclick='save()' disabled title='Sauvegarder' accesskey='S'>
<i class="bi bi-save-fill"></i>
</button>
</div>
<form id='sudokuForm' class='needs-validation' novalidate>
<table id='grid' class='table mb-2'>
</section>
<form id='sudokuForm'>
<table id='grid' class='grid'>
<tbody>
<?php
for ($row = 0; $row < 81; $row += 9) {
<?php
for ($row = 0; $row < 9; $row++) {
?>
<tr class="input-group d-inline-block">
<?php
<tr>
<?php
for ($column = 0; $column < 9; $column++) {
$value = $currentGrid[$row+$column];
$value = $currentGrid[9*$row+$column];
if ($value == UNKNOWN) {
?>
<td><input type='number' min='1' max='9' step='1' value='' class='form-control'
title='Valeurs possibles [Clic-droit]' /></td>
<?php
<td><input type='number' min='1' max='9' step='1' value='' title='Valeurs possibles [Clic-droit]'/></td>
<?php
} else {
?>
<td><input type='number' min='1' max='9' step='1' value='<?=$value?>' class='form-control' disabled /></td>
<?php
<td><input type='number' min='1' max='9' step='1' value='<?=$value?>' disabled/></td>
<?php
}
}
?>
</tr>
<?php
<?php
}
?>
</tbody>
</table>
</form>
<div class='d-flex mb-2'>
<div id='insertRadioGroup' class='radioGroup btn-group flex-fill'>
<?php
<section class='tools'>
<div id='insertRadioGroup' class='insertRadioGroup'>
<?php
for($value=1; $value<=9; $value++) {
echo " <input type='radio'class='btn-check' id='insertRadio$value' value='$value' name='insertRadioGroup' onclick='insert(this)' accesskey='$value'/><label for='insertRadio$value' class='btn btn-primary' title='Insérer un $value'>$value</label>\n";
echo " <input type='radio' id='insertRadio$value' value='$value' name='insertRadioGroup' onclick='insert(this)' accesskey='$value'/><label for='insertRadio$value' title='Insérer un $value'>$value</label>\n";
}
?>
</div>
</div>
<div class='mb-3'>
<?php
</section>
<section>
<?php
if (isset($warning))
echo(" <strong>⚠️ $warning ⚠️</strong><br/>\n");
else
echo(" Remplissez la grille de sorte que chaque ligne, colonne et région (carré de 3×3 cases) contienne tous les chiffres de 1 à 9.\n")
?>
</div>
<ul id='contextMenu' class='context-menu modal-content shadow list-group w-auto position-absolute'></ul>
</section>
<ul id='contextMenu' class='context-menu'></ul>
<footer>
<div id='links' class='list-group mb-2'>
<a href='.' class='list-group-item list-group-item-action'>Nouvelle grille</a>
<a href='' class='list-group-item list-group-item-action'>Lien vers cette grille</a>
<a href='?.................................................................................' class='list-group-item list-group-item-action'>Grille
vierge</a>
<a href='' id='fixGridLink' class='list-group-item list-group-item-action'>Figer la grille enregistrée</a>
<div id='links'>
<a href=''>Lien vers cette grille</a><br/>
<a href='.'>Nouvelle grille</a><br/>
<a href='.................................................................................'>Grille vierge</a><br/>
<a href='' id='fixGridLink'>Figer cette grille</a>
</div>
<div class='credits'>
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>
</footer>
</body>
</html>
</html>

View File

@ -14,11 +14,10 @@
$thumbnail = imagecreate($size, $size);
$transparent = imagecolorallocate($thumbnail, 1, 1, 1);
imagecolortransparent($thumbnail, $transparent);
$darkerBorder = imagecolorallocate($thumbnail, 150, 155, 160);
$lighterBorder = imagecolorallocate($thumbnail, 210, 225, 230);
$emptyBoxBC = imagecolorallocate($thumbnail, 255, 255, 255);
$clueBC = imagecolorallocate($thumbnail, 255, 255, 255);
$clueFC = imagecolorallocate($thumbnail, 150, 155, 160);
$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);
@ -28,8 +27,8 @@
$lineStart = $start + 1;
$lineEnd = $end - 2;
for ($i = $start; $i < $end; $i += 3*$boxSize + 1) {
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $darkerBorder);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $darkerBorder);
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $black);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $black);
}
$x = $start;
$y = $start;
@ -38,9 +37,9 @@
if ($i % 3 == 0) $x++;
if ($i % 27 == 0) $y++;
if ($value == UNKNOWN) {
$bgColor = $emptyBoxBC;
$bgColor = $white;
} else {
$bgColor = $clueFC;
$bgColor = $blue;
}
imagefilledrectangle($thumbnail, $x, $y, $x+$boxSizeMinusOne, $y+$boxSizeMinusOne, $bgColor);
$x += $boxSize;
@ -57,21 +56,21 @@
$lineStart = $start + 1;
$lineEnd = $end - 2;
for ($i = $start + $boxSize; $i < $end - $boxSize; $i += $boxSize) {
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $lighterBorder);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $lighterBorder);
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $grey);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $grey);
}
for ($i = $start; $i < $end; $i += 3*$boxSize) {
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $darkerBorder);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $darkerBorder);
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $black);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $black);
}
$x = $start + 1;
$y = $start + 1;
$boxSizeMinusTwo = $boxSize - 2;
foreach(str_split($currentGrid) as $i => $value) {
if ($value == UNKNOWN) {
$bgColor = $emptyBoxBC;
$bgColor = $white;
} else {
$bgColor = $clueFC;
$bgColor = $blue;
}
imagefilledrectangle($thumbnail, $x, $y, $x+$boxSizeMinusTwo, $y+$boxSizeMinusTwo, $bgColor);
$x += $boxSize;
@ -90,26 +89,26 @@
$fontSize = floor($boxSize/2) - 4;
$fdx = floor(($boxSize - imagefontwidth($fontSize)) / 2);
$fdy = ceil(($boxSize - imagefontheight($fontSize)) / 2) - 1;
$fontColor = $emptyBoxBC;
$fontColor = $white;
for ($i = $start + $boxSize; $i < $end - $boxSize; $i += $boxSize) {
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $lighterBorder);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $lighterBorder);
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $grey);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $grey);
}
for ($i = $start; $i < $end; $i += 3*$boxSize) {
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $darkerBorder);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $darkerBorder);
ImageLine($thumbnail, $lineStart, $i, $lineEnd, $i, $black);
ImageLine($thumbnail, $i, $lineStart, $i, $lineEnd, $black);
}
$x = $start + 1;
$y = $start + 1;
$boxSizeMinusTwo = $boxSize - 2;
foreach(str_split($currentGrid) as $i => $value) {
if ($value == UNKNOWN) {
$bgColor = $emptyBoxBC;
$bgColor = $white;
} else {
$bgColor = $clueBC;
$bgColor = $blue;
}
imagefilledrectangle($thumbnail, $x, $y, $x+$boxSizeMinusTwo, $y+$boxSizeMinusTwo, $bgColor);
if ($value != UNKNOWN) imagestring($thumbnail, $fontSize, $x + $fdx, $y + $fdy, $value, $clueFC);
if ($value != UNKNOWN) imagestring($thumbnail, $fontSize, $x + $fdx, $y + $fdy, $value, $fontColor);
$x += $boxSize;
if ($i % 9 == 8) {
$y += $boxSize;