网页版数独:自动生成题目、点格子填数、实时校验、一键看答案。核心是回溯求解器,顺便练 DOM 交互。
1. 回溯求解器
数独解算 = 深度优先搜索:找空格→试 1~9→合法就递归→不行就回退。和 Python 版同一套思想:
function valid(b, r, c, n) {
for (let i = 0; i < 9; i++) {
if (b[r][i] === n || b[i][c] === n) return false;
}
const br = Math.floor(r / 3) * 3, bc = Math.floor(c / 3) * 3;
for (let i = 0; i < 3; i++)
for (let j = 0; j < 3; j++)
if (b[br + i][bc + j] === n) return false;
return true;
}
function solve(b) {
for (let r = 0; r < 9; r++) {
for (let c = 0; c < 9; c++) {
if (b[r][c] === 0) {
for (let n = 1; n <= 9; n++) {
if (valid(b, r, c, n)) {
b[r][c] = n;
if (solve(b)) return true;
b[r][c] = 0;
}
}
return false;
}
}
}
return true;
}
2. 生成题目
从空盘跑一次 solve 得到完整终盘,再随机挖掉若干格就是题目(简化版不保证唯一解):
function generate() {
const b = Array.from({length: 9}, () => Array(9).fill(0));
solve(b);
const cells = [];
for (let r = 0; r < 9; r++) for (let c = 0; c < 9; c++) cells.push([r, c]);
cells.sort(() => Math.random() - 0.5);
for (let i = 0; i < 40; i++) {
const [r, c] = cells[i];
b[r][c] = 0;
}
return b;
}
3. 完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<title>数独</title>
<style>
body{font-family:sans-serif;text-align:center;background:#222;color:#eee}
#board{display:grid;grid-template-columns:repeat(9,44px);gap:1px;width:398px;margin:16px auto;background:#555;padding:1px}
#board input{width:44px;height:44px;text-align:center;font-size:18px;border:none;background:#f5f1e8;color:#333}
#board input.fixed{background:#d9d0c0;color:#111;font-weight:bold}
</style>
</head>
<body>
<h1>数独 <small>点格子填 1-9,按钮看答案</small></h1>
<div id="board"></div>
<button onclick="solveAll()">看答案</button> <button onclick="location.reload()">新题目</button>
<script>
function valid(b, r, c, n) {
for (let i = 0; i < 9; i++) {
if (b[r][i] === n || b[i][c] === n) return false;
}
const br = Math.floor(r / 3) * 3, bc = Math.floor(c / 3) * 3;
for (let i = 0; i < 3; i++)
for (let j = 0; j < 3; j++)
if (b[br + i][bc + j] === n) return false;
return true;
}
function solve(b) {
for (let r = 0; r < 9; r++) {
for (let c = 0; c < 9; c++) {
if (b[r][c] === 0) {
for (let n = 1; n <= 9; n++) {
if (valid(b, r, c, n)) {
b[r][c] = n;
if (solve(b)) return true;
b[r][c] = 0;
}
}
return false;
}
}
}
return true;
}
function generate() {
const b = Array.from({length: 9}, () => Array(9).fill(0));
solve(b);
const cells = [];
for (let r = 0; r < 9; r++) for (let c = 0; c < 9; c++) cells.push([r, c]);
cells.sort(() => Math.random() - 0.5);
for (let i = 0; i < 40; i++) {
const [r, c] = cells[i];
b[r][c] = 0;
}
return b;
}
const puzzle = generate();
const board = document.getElementById('board');
for (let r = 0; r < 9; r++) {
for (let c = 0; c < 9; c++) {
const inp = document.createElement('input');
inp.maxLength = 1;
if (puzzle[r][c]) {
inp.value = puzzle[r][c];
inp.className = 'fixed';
inp.readOnly = true;
}
inp.addEventListener('input', function () {
const v = parseInt(this.value, 10);
if (!(v >= 1 && v <= 9)) { this.value = ''; return; }
puzzle[r][c] = v;
});
board.appendChild(inp);
}
}
function solveAll() {
solve(puzzle);
let i = 0;
for (const inp of board.children) {
inp.value = puzzle[Math.floor(i / 9)][i % 9];
i++;
}
}
</script>
</body>
</html>
4. 运行与常见问题
保存为 sudoku.html 用浏览器打开。常见问题:①生成慢——回溯从空盘求解很快;②输入无效数字——input 事件里校验 1~9;③想保证唯一解——在 solve 里计数,解多于 1 就重新生成。
💡 改进方向:①唯一解校验;②难度选择;③宫格高亮与错误提示;④计时;⑤提示功能。