La primera resolución que vi

La escena que describo en la página [Acerca de](/es/about/) es este modelo. La IA que resolvía sudokus iba rellenando las respuestas una a una, fila por fila, desde la primera casilla. Parecía un niño pequeño que ya se sabía las respuestas.

Datos de entrenamiento hechos con la hoja de soluciones

Los primeros datos de entrenamiento se hicieron con dataset_sudoku.py. El motor genera el puzzle junto con su solución, y escribir el número correcto en la primera casilla vacía del tablero (fila por fila desde arriba a la izquierda) se guarda como un problema. Se rellena esa casilla, se pasa a la siguiente casilla vacía y se repite hasta llenar el tablero.

De 300 tableros fáciles, medios y difíciles salieron 13.497 problemas, con los que se entrenó Qwen3-VL-2B-Instruct mediante LoRA (lora_stoku).

En el prompt también se incluía la lista de jugadas posibles. Pero la respuesta correcta era siempre la primera casilla vacía. Aunque en (0,3) cupieran tanto el 5 como el 9, si la hoja de soluciones decía 9, la respuesta era 9. Por qué era 9 no aparecía en ninguna parte de los datos.

Orden en que se rellenó un puzzle difícil. Las casillas rojas no se podían determinar en ese momento solo con el tablero
Orden en que se rellenó un puzzle difícil. Las casillas rojas no se podían determinar en ese momento solo con el tablero

¿Se podía resolver esa casilla en ese momento?

Revisé las casillas de respuesta de los 13.497 problemas usando solo el tablero de ese momento.

  • Casillas con un solo número candidato: 9.502 (70%)
  • Casillas que eran el único sitio para ese número dentro de su fila, columna o caja: 2.127 (16%)
  • Casillas que ninguno de los dos métodos podía decidir y que necesitaban la hoja de soluciones: 1.868 (14%)

Las casillas que necesitaban la hoja de soluciones eran alrededor del 21% en difícil, el 12% en medio y el 5% en fácil. Una persona se saltaría esas casillas y rellenaría primero las seguras. El modelo aprendió a escribir en la misma posición el número de la hoja de soluciones.

Si la casilla de respuesta se podía determinar en ese momento, por dificultad
Si la casilla de respuesta se podía determinar en ese momento, por dificultad

El código real de este paso

Esta es la función que genera los primeros datos de entrenamiento. empties[0] es la primera casilla vacía contando desde arriba a la izquierda, y el valor se toma de engine.solution.

dataset_sudoku.pypython · 42 líneas
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

La siguiente entrada es "2. No pudo aprender por ensayo y error".