网页版数独:自动生成题目、点格子填数、实时校验、一键看答案。核心是回溯求解器,顺便练 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 就重新生成。

💡 改进方向:①唯一解校验;②难度选择;③宫格高亮与错误提示;④计时;⑤提示功能。