第一次看到的解法

我在[关于](/zh/about/)页面中写到的场景,就是这个模型。解数独的AI从第一个格子开始,横向一个接一个地填入答案。就像一个早已知道答案的小孩子。

用答案表做的训练数据

第一批训练数据是用dataset_sudoku.py制作的。引擎同时生成谜题和答案,把在盘面最前面的空格(从左上角开始按横向顺序)写入正确数字作为一道题保存下来。填上那个格子后再移到下一个空格,反复进行直到盘面填满。

从简单、普通、困难共300局中得到了13,497道题,用LoRA在Qwen3-VL-2B-Instruct上进行了训练(lora_stoku)。

提示词中也加入了可走着法的列表。但正确答案总是第一个空格。即使(0,3)里5和9都能填,只要答案表上是9,9就是正确答案。至于为什么是9,数据中哪里都没有。

一局困难谜题的填写顺序。红色格子是在那一刻仅凭盘面无法确定的格子
一局困难谜题的填写顺序。红色格子是在那一刻仅凭盘面无法确定的格子

那一刻是能解出的格子吗

我们仅凭当时的盘面,重新检查了13,497道题的正确答案格子。

  • 候选数字只有一个的格子:9,502个(70%)
  • 在行、列、宫内该数字只有一个位置可填的格子:2,127个(16%)
  • 两种方法都无法确定、必须依靠答案表的格子:1,868个(14%)

必须依靠答案表的格子,在困难中约占21%,在普通中约占12%,在简单中约占5%。如果是人,会跳过这样的格子,先填确定的格子。而模型学会的是在同样的位置写下答案表上的数字。

按难度区分,正确答案格子在那一刻是否可以确定
按难度区分,正确答案格子在那一刻是否可以确定

这一步的实际代码

这是生成第一批训练数据的函数。empties[0]是从左上角开始数的第一个空格,值取自engine.solution。

dataset_sudoku.pypython · 42 行
def generate_game_samples(
    engine: SudokuEngine,
    difficulty: str,
    game_id: int,
    image_dir: Path,
) -> list[dict]:
    engine.new_game(difficulty)
    board = [row[:] for row in engine.puzzle]
    given_mask = [[engine.is_given(r, c) for c in range(9)] for r in range(9)]
    samples: list[dict] = []
    step = 0

    while True:
        empties = [(r, c) for r in range(9) for c in range(9) if board[r][c] == 0]
        if not empties:
            break

        r, c = empties[0]
        value = engine.solution[r][c]

        img = board_to_image(board, given_mask)
        img_name = f"g{game_id:05d}_s{step:02d}.png"
        img_path = image_dir / img_name
        img.save(img_path)

        answer = json.dumps({"row": r, "col": c, "value": value}, ensure_ascii=False)
        prompt = build_sudoku_prompt(board, given_mask)

        samples.append(
            {
                "id": f"g{game_id:05d}_s{step:02d}",
                "image_path": str(img_path.resolve()),
                "prompt": prompt,
                "answer": answer,
                "difficulty": difficulty,
            }
        )

        board[r][c] = value
        step += 1

    return samples

下一篇是“2. 靠试错学不会”。