The first solve we saw
The scene described in the [About](/en/about/) page is this model. The sudoku-solving AI filled in answers one by one, row by row, starting from the first cell. It was like a young child who already knew the answers.
Training data made from the answer sheet
The first training data was made with dataset_sudoku.py. The engine generates a puzzle together with its solution, and writing the correct digit into the frontmost empty cell (row by row from the top left) is saved as one problem. That cell is filled, then it moves on to the next empty cell, repeating until the board is full.
300 easy, medium and hard boards produced 13,497 problems, which were used to train Qwen3-VL-2B-Instruct with LoRA (lora_stoku).
The prompt also included the list of possible moves. But the answer was always the first empty cell. Even if both 5 and 9 could go in (0,3), if the answer sheet said 9, then 9 was the answer. Why it was 9 appeared nowhere in the data.

Could the cell be solved at that moment?
We rechecked the answer cells of the 13,497 problems using only the board at that moment.
- Cells with only one candidate digit: 9,502 (70%)
- Cells that were the only place for that digit within a row, column or box: 2,127 (16%)
- Cells that neither method could decide and that required the answer sheet: 1,868 (14%)
Cells that required the answer sheet were about 21% in hard, 12% in medium and 5% in easy. A person would skip such cells and fill the certain ones first. The model learned to write the answer sheet's digit in the same spot.

The actual code for this step
This is the function that builds the first training data. empties[0] is the first empty cell counted from the top left, and the value comes from engine.solution.
dataset_sudoku.py
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 samplesThe next post is "2. It could not learn by trial and error".
COMMENTS · CHAPTER 01
An AI that solves like a child who knows the answers 0 comments
No comments yet. Leave a question or anything you would like to add.
To post a comment, continue with Google and register a nickname and email address.
Continue with Google