217 lines
6.4 KiB
JavaScript
217 lines
6.4 KiB
JavaScript
const VALUES = "123456789"
|
|
|
|
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 highlightedValue = ""
|
|
let history = []
|
|
|
|
window.onload = function() {
|
|
let rowId = 0
|
|
for (row of grid.getElementsByTagName('tr')) {
|
|
let columnId = 0
|
|
for (box of row.getElementsByTagName('input')) {
|
|
let regionId = rowId - rowId%3 + Math.floor(columnId/3)
|
|
if (!box.readOnly) {
|
|
box.onfocus = onfocus
|
|
box.oninput = oninput
|
|
}
|
|
box.rowId = rowId
|
|
box.columnId = columnId
|
|
box.regionId = regionId
|
|
boxes.push(box)
|
|
rows[rowId].push(box)
|
|
columns[columnId].push(box)
|
|
regions[regionId].push(box)
|
|
columnId++
|
|
}
|
|
rowId++
|
|
}
|
|
boxes.forEach(box => {
|
|
box.neighbourhood = new Set(rows[box.rowId].concat(columns[box.columnId]).concat(regions[box.regionId]))
|
|
box.neighbourhood.delete(box)
|
|
box.neighbourhood = Array.from(box.neighbourhood)
|
|
})
|
|
boxes.forEach(searchCandidatesOf)
|
|
enableButtons()
|
|
boxes.forEach(showCandidatesOn)
|
|
for(box of boxes) {
|
|
if (!box.readOnly) {
|
|
box.focus()
|
|
break
|
|
}
|
|
}
|
|
suggestionTimer = setTimeout(showSuggestion, 30000)
|
|
}
|
|
|
|
function searchCandidatesOf(box) {
|
|
box.candidates = new Set(VALUES)
|
|
box.neighbourhood.forEach(neighbour => box.candidates.delete(neighbour.value))
|
|
}
|
|
|
|
function showCandidatesOn(box) {
|
|
if (!box.disabled) {
|
|
while (box.list.firstChild) {
|
|
box.list.firstChild.remove()
|
|
}
|
|
if (box.candidates.size) {
|
|
const candidatesArray = Array.from(box.candidates).sort()
|
|
candidatesArray.forEach(candidate => {
|
|
option = document.createElement('option')
|
|
option.value = candidate
|
|
box.list.appendChild(option)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
function onfocus() {
|
|
this.previousValue = this.value
|
|
this.select()
|
|
}
|
|
|
|
function oninput() {
|
|
history.push({input: this, value: this.previousValue})
|
|
undoButton.disabled = false
|
|
refresh(this)
|
|
}
|
|
|
|
function refresh(box) {
|
|
box.style.color = colorPicker.value
|
|
|
|
box.neighbourhood.concat([box]).forEach(neighbour => {
|
|
searchCandidatesOf(neighbour)
|
|
showCandidatesOn(neighbour)
|
|
neighbour.setCustomValidity("")
|
|
})
|
|
|
|
for (neighbour1 of box.neighbourhood) {
|
|
neighbour1.setCustomValidity("")
|
|
if (neighbour1.value.length) {
|
|
if (neighbour1.candidates.size) {
|
|
for (area of [
|
|
{name: "région", neighbours: regions[neighbour1.regionId]},
|
|
{name: "ligne", neighbours: rows[neighbour1.rowId]},
|
|
{name: "colonne", neighbours: columns[neighbour1.columnId]},
|
|
])
|
|
for (neighbour2 of area.neighbours)
|
|
if (neighbour2 != neighbour1 && neighbour2.value == neighbour1.value) {
|
|
for (neighbour of [neighbour1, neughbour2]) {
|
|
neighbour.setCustomValidity(`Il y a un autre ${neighbour.value} dans cette ${area.name}.`)
|
|
}
|
|
}
|
|
}
|
|
} else if (neighbour1.candidates.size == 0) {
|
|
console.log("rezgzgzg")
|
|
neighbour1.setCustomValidity("Aucun value possible !")
|
|
}
|
|
}
|
|
|
|
enableButtons()
|
|
highlightAndTab()
|
|
|
|
|
|
if (box.form.checkValidity()) { // Correct grid
|
|
if (boxes.filter(box => box.value == "").length == 0) {
|
|
alert(`Bravo ! Vous avez résolu la grille.`)
|
|
} else {
|
|
if (suggestionTimer) clearTimeout(suggestionTimer)
|
|
suggestionTimer = setTimeout(showSuggestion, 30000)
|
|
}
|
|
} else { // Errors on grid
|
|
box.reportValidity()
|
|
box.select()
|
|
}
|
|
}
|
|
|
|
function undo() {
|
|
if (history.length) {
|
|
previousState = history.pop()
|
|
previousState.input.value = previousState.value
|
|
refresh(previousState.input)
|
|
if (history.length < 1) undoButton.disabled = true
|
|
}
|
|
}
|
|
|
|
function enableButtons() {
|
|
for (button of buttons.getElementsByTagName("button")) {
|
|
if (boxes.filter(box => box.value == "").some(box => box.candidates.has(button.textContent))) {
|
|
button.disabled = false
|
|
} else {
|
|
button.disabled = true
|
|
if (highlightedValue == button.textContent) highlightedValue = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
function moveOn(area, position, direction) {
|
|
if (area.filter(box => box.disabled).length < 9) {
|
|
do {
|
|
position = (position + direction) % 9
|
|
} while (area[position].disabled)
|
|
area[position].focus()
|
|
}
|
|
}
|
|
|
|
function highlight(value) {
|
|
if (value == highlightedValue) {
|
|
highlightedValue = ""
|
|
} else {
|
|
highlightedValue = value
|
|
}
|
|
for (button of buttons.getElementsByTagName("button")) {
|
|
if (button.textContent == highlightedValue) button.className = "same-value"
|
|
else button.className = ""
|
|
}
|
|
highlightAndTab()
|
|
boxes.filter(box => box.value == "" && box.tabIndex == 0)[0].focus()
|
|
}
|
|
|
|
function highlightAndTab() {
|
|
if (highlightedValue) {
|
|
boxes.forEach(box => {
|
|
if (box.value == highlightedValue) {
|
|
box.className = "same-value"
|
|
box.tabIndex = -1
|
|
}
|
|
else if (box.candidates.has(highlightedValue)) {
|
|
box.className = ""
|
|
box.tabIndex = 0
|
|
} else {
|
|
box.className = "forbidden-value"
|
|
box.tabIndex = -1
|
|
}
|
|
})
|
|
} else {
|
|
boxes.forEach(box => box.className = "")
|
|
}
|
|
}
|
|
|
|
function shuffle(iterable) {
|
|
array = Array.from(iterable)
|
|
if (array.length > 1) {
|
|
let i, j, tmp
|
|
for (i = array.length - 1; i > 0; i--) {
|
|
j = Math.floor(Math.random() * (i+1))
|
|
tmp = array[i]
|
|
array[i] = array[j]
|
|
array[j] = tmp
|
|
}
|
|
}
|
|
return array
|
|
}
|
|
|
|
easyFirst = (box1, box2) => box1.candidates.size - box2.candidates.size
|
|
|
|
function showSuggestion() {
|
|
const emptyBoxes = boxes.filter(box => box.value == "" && box.candidates.size == 1)
|
|
if (emptyBoxes.length) {
|
|
shuffle(emptyBoxes).placeholder = "!"
|
|
} else {
|
|
clearTimeout(suggestionTimer)
|
|
suggestionTimer = null
|
|
}
|
|
}
|