我正在编写简单的Sudoku求解器作为练习,我认为我将通过在9x9网格上逐步显示它来显示整个过程。
我正在使用带有嵌套div的HTML表来显示游戏板(可能不相关),并使用递归函数来解决预置的Sudoku。我的解决方案现在处于“意大利面条”状态,因此下面我为您提供了我所拥有的伪代码:
function fillBoard() {
for(let i = 0; i < 9; i++) {
for(let j = 0; j < 9; j++) {
const cell = document.querySelector(`div[data-row="${i}"][data-column="${j}"]`);
cell.innerHTML = gameState.solution[i][j];
}
}
}
function solve(row, column) {
/* simplified, it's working :) */
for(let guess = 1; guess < 10; guess++) {
this.solution[row][column] = guess; <--- SHOW THIS STEP TO USER
let conflicts = checkConflicts(row, column)
if(!conflicts) {
let emptyCell = this.findNextEmptyCell(row, column);
if(emptyCell) {
let result = this.solve(emptyCell.i, emptyCell.j);
if(!result) continue;
else return true;
}
return true;
}
else continue;
}
}
我尝试将fillBoard()
函数调用放入solve()
内,但这显然没有用,因为我只得到了已解决网格形式的最终结果。我也尝试过使用setInterval(fillBoard, 100)
,但是solve()
函数的执行速度太快。
如何通过在每次solve()
调用之后更新HTML来实现整个求解过程的“增量”显示?
我正在尝试获得以下内容:https://www.youtube.com/watch?v=ChjQRIhH414,但我是从左至右,从上至下填充板子
当前解决方案:Codepen
答案 0 :(得分:1)
您将希望放慢html渲染过程,这会向我建议动画。您可以研究这种方法,并且可以根据需要进行调整。没有完整的示例,我无法确定是否运行它。作为响应,我将为您提供一些伪代码,以测试它的开始方式,并希望它将为您指明正确的方向:
function fillBoard(i=0, j=0) {
const cell = document.querySelector(`div[data-row="${i}"][data-column="${j}"]`);
cell.innerHTML = gameState.solution[i][j];
j++;
if( j >= 9) { j = 0; i++; }
if(i < 9)
requestAnimationFrame(function() { fillBoard(i,j) });
}
如果需要,可以在给定的延迟上用setTimeout替换requestAnimationFrame。您可以将其设置为1-2秒,或者以其他方式开始查看它是否能为您提供所需的结果。
答案 1 :(得分:0)
未优化但有效的示例,方法是遵循上述注释中的@loctrice建议。仍在寻找解决此问题的其他方法。
console.clear();
let states = [];
let x = 0;
document.addEventListener("DOMContentLoaded", function() {
const start = document.getElementById("start");
const check = document.getElementById("check");
const sudoku = document.getElementById("sudoku");
function fillBoard(index) {
console.log(`Displaying ${index}/${states.length}`);
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const cell = document.querySelector(
`div[data-row="${i}"][data-column="${j}"]`
);
cell.innerHTML =
states[index][i][j] == 0 ? "" : states[index][i][j];
}
}
}
class Sudoku {
checkRow(row) {
for (let i = 0; i < 9; i++) {
let number = this.solution[row][i];
if (!number) continue;
for (let j = 0; j < 9; j++) {
if (i == j) continue;
const challenge = this.solution[row][j];
if (number == challenge) return number;
}
}
return false;
}
checkColumn(column) {
for (let i = 0; i < 9; i++) {
let number = this.solution[i][column];
if (!number) continue;
for (let j = 0; j < 9; j++) {
if (i == j) continue;
const challenge = this.solution[j][column];
if (number == challenge) return number;
}
}
return false;
}
checkBox(box) {
const rowModifier = Math.floor(box / 3) * 3;
const colModifier = (box % 3) * 3;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let number = this.solution[i + rowModifier][j + colModifier];
if (!number) continue;
for (let x = 0; x < 3; x++) {
for (let y = 0; y < 3; y++) {
if (x == i && y == j) continue;
const challenge = this.solution[x + rowModifier][y + colModifier];
if (number == challenge) return number;
}
}
}
}
return false;
}
solve(row, column, array) {
for (let guess = 1; guess < 11; guess++) {
if (guess == 10) {
this.solution[row][column] = 0;
return false;
}
this.solution[row][column] = guess; // <=== SHOW ENTIRE BOARD HERE
let state = this.solution.map(a => [...a]);
array.push(state);
const rowError = this.checkRow(row);
const columnError = this.checkColumn(column);
const boxError = this.checkBox(
3 * Math.floor(row / 3) + Math.floor(column / 3)
);
if (!rowError && !columnError && !boxError) {
// find next empty cell
let emptyCell = this.findNextEmptyCell(row, column);
if (emptyCell) {
let result = this.solve(emptyCell.i, emptyCell.j, array);
if (!result) continue;
else return true;
}
return true;
} else continue;
}
}
findNextEmptyCell(row, column) {
for (let i = row; i < 9; i++) {
if (column == 8) column = 0;
for (let j = column; j < 9; j++) {
if (this.solution[i][j]) continue;
else return { i: i, j: j };
}
column = 0;
}
return false;
}
generateBox(boxNumber) {
let numbers = [];
for (let i = 0; i < 9; i++) {
numbers.push(i);
}
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
const length = numbers.length;
const index = Math.floor(Math.random() * length);
const number = numbers.splice(index, 1);
const row = i + boxNumber * 3 / 4;
const col = j + boxNumber * 3 / 4;
this.solution[row][col] = parseInt(number) + 1;
}
}
}
initialize() {
this.solution = [];
for (let i = 0; i < 9; i++) {
this.solution.push([]);
for (let j = 0; j < 9; j++) {
this.solution[i][j] = 0;
}
}
for (let i = 0; i < 3; i++) {
this.generateBox(i * 4);
}
}
}
let gameState = new Sudoku();
gameState.initialize();
start.onclick = function() {
gameState.solve(0, 3, states);
window.setInterval(function() {if(x < states.length) fillBoard(x++);}, 15);
};
check.onclick = function() {
for (let i = 0; i < 9; i++) {
let error = gameState.checkRow(i);
if (error) {
for (let j = 0; j < 9; j++) {
const cell = document.querySelector(
`div[data-row="${i}"][data-column="${j}"]`
);
cell.classList.add("incorrect-area");
if (cell.innerHTML == error) cell.classList.add("incorrect");
}
return;
}
error = gameState.checkColumn(i);
if (error) {
const cells = document.querySelectorAll(`div[data-column="${i}"]`);
cells.forEach(c => {
c.classList.add("incorrect-area");
if (c.innerHTML == error) c.classList.add("incorrect");
});
return;
}
error = gameState.checkBox(i);
if (error) {
const cells = document.querySelectorAll(`div[data-box="${i}"]`);
cells.forEach(c => {
c.classList.add("incorrect-area");
if (c.innerHTML == error) c.classList.add("incorrect");
});
return;
}
}
};
for (let i = 0; i < 9; i++) {
const row = document.createElement("tr");
row.classList.add("row");
for (let j = 0; j < 9; j++) {
let td = document.createElement("td");
let cell = document.createElement("div");
cell.classList.add("cell");
cell.dataset.row = i;
cell.dataset.column = j;
let a = Math.floor(i / 3);
let b = Math.floor(j / 3);
cell.dataset.box = 3 * a + b;
cell.innerHTML =
gameState.solution[i][j] == "0" ? "" : gameState.solution[i][j];
if (!cell.innerHTML)
cell.onclick = function(e) {
const row = e.target.dataset.row;
const col = e.target.dataset.column;
gameState.solution[row][col] =
++gameState.solution[row][col] > 9
? 0
: gameState.solution[row][col];
cell.innerHTML =
gameState.solution[row][col] == "0"
? ""
: gameState.solution[row][col];
document
.querySelectorAll("div.cell")
.forEach(c =>
c.classList.remove("correct", "incorrect", "incorrect-area")
);
};
td.appendChild(cell);
row.appendChild(td);
}
sudoku.appendChild(row);
}
});
.incorrect-area {
background-color: #A55 !important;
border-color: #F00 !important;
}
.correct {
background-color: #1A1 !important;
border-color: #0F0 !important;
}
.incorrect {
background-color: #A11 !important;
border-color: #F00 !important;
}
div[data-box='1'],
div[data-box='3'],
div[data-box='5'],
div[data-box='7'] {
background-color: #444;
}
.button {
display: inline-block;
min-height: 30px;
width: 120px;
background-color: white;
font-size: 32px;
cursor: pointer;
}
#buttons {
text-align: center;
}
div {
padding: 0px;
}
body {
background-color: #000;
}
#game {
width: 500px;
margin-left: auto;
margin-right: auto;
margin-top: 0px;
}
#sudoku {
width: 500px;
height: 500px;
margin-left: auto;
margin-right: auto;
background-color: #111;
border: dashed 1px white;
color: white;
}
.row {
border: 1px solid yellow;
}
.cell {
cursor: default;
height: 40px;
width: 40px;
padding: 0;
margin: 0;
border: solid white 1px;
text-align: center;
font-weight: bold;
display: table-cell;
vertical-align: middle;
user-select: none;
}
.cell:hover {
background-color: #765;
transform: scale(1.3);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>SudoQ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
<script src="main.js"></script>
</head>
<body>
<div id="game">
<div id="buttons">
<div id="start" class="button">SOLVE</div>
<div id="check" class="button">CHECK</div>
</div>
<table id="sudoku">
</table>
</div>
</body>
</html>