Ajuda, não disputa
Diferente do go ou do gomoku, o sudoku não tem adversário. Se a IA resolver tudo sozinha, não sobra nada para a pessoa fazer. Por isso, no sudoku na web (stoku.mariago.kr), a própria pessoa resolve e só pede uma jogada à IA quando trava.

Como pedir uma jogada
- Você pede pelo botão "Jogada da IA" na tela ou pelo chat, com frases como "faça uma jogada" ou "dica".
- O servidor de chat usa o Qwen2.5-1.5B-Instruct e expressões regulares para decidir se é um pedido de jogada.
- Se for um pedido de jogada, ele envia o tabuleiro atual ao servidor de sudoku, recebe uma casa e um número e os coloca no tabuleiro da pessoa.
Hoje o servidor web não carrega o VLM e escolhe as jogadas certas apenas com código. Primeiro ele procura um bloco, linha ou coluna com uma única casa vazia e, se não houver, procura uma casa que seja o único lugar possível para algum número. Na prática, o código faz todo o trabalho que no post 4 era dividido entre o VLM e o código. A lista de modelos na tela mostra lora_move, mas quem decide a jogada de verdade é essa estratégia. Ainda não é mostrado por que aquela casa foi escolhida.
Importar por foto
O app em Qt tem uma função para importar um sudoku de papel ou de uma tela a partir de uma foto. Ele recorta a grade, junta só as casas que têm números e as entrega para o LoRA 8B de leitura de tabuleiro do post 3 ler.

Registros de jogos resolvidos na web
Em pouco mais de três semanas, 40 jogos foram abertos na web. 26 foram resolvidos até o fim, levando cerca de 12 minutos em média. 9 foram abandonados e 5 continuam em andamento. Os jogos concluídos foram 20 no nível médio, 3 no difícil, 2 no infernal e 1 no extremo.
As ações registradas foram 1.430 números colocados, 135 anotações, 62 apagamentos e 23 desfazer.

O código real desta etapa
Esta é a ordem em que o servidor web escolhe uma jogada certa.
vlm_digit_strategy.py
def pick_code_certain_move(
board: list[list[int]],
) -> tuple[int, int, int, str] | None:
"""우선순위: last-empty → digit 유일 → human solver → 탐색 강제수."""
lasts = last_empty_unit_moves(board)
if lasts:
return lasts[0]
for digit in range(1, 10):
illegal = set(illegal_cells_for_digit(board, digit))
moves = absolute_placements(board, illegal, digit)
if moves:
r, c, v, reason = moves[0]
return r, c, v, f"code_digit{digit}_{reason}"
from sudoku_human_solver import find_human_move
hm = find_human_move(board)
if hm is not None:
return hm.row, hm.col, hm.value, f"human_{hm.kind.value}"
# 극한: 국소 후보는 여러 개인데, 하나만 해가 남는 칸 (예: 2행 끝 7)
return pick_search_forced_move(board)Esta é a parte em que o servidor de chat recebe uma mensagem e, se for um pedido de jogada, a repassa ao servidor de sudoku.
chat_bridge_server.py
MOVE_RE = re.compile(
r"(한\s*수|다음\s*수|힌트|추천|어디|둬|놓|채워|풀어|solve|hint|move|next)",
re.I,
)
# ...
def handle_chat(body: dict[str, Any]) -> dict[str, Any]:
message = str(body.get("message") or "").strip()
if not message:
return {"error": "empty message"}
model_id = str(body.get("modelId") or "lora_move")
meta = TRAINED_MODELS.get(model_id, TRAINED_MODELS["lora_move"])
board = body.get("board")
puzzle = body.get("puzzle")
difficulty = body.get("difficulty") or "medium"
if not isinstance(board, list) or len(board) != 9:
return {"error": "board required"}
blanks = _empty_count(board)
intent = classify_intent(message)
llm_text = chat_llm_reply(
message,
{
"model_label": meta["label"],
"blanks": blanks,
"difficulty": difficulty,
},
)
if llm_text:
if "CMD:MOVE" in llm_text.upper() or intent == "move":
intent = "move"
reply_base = re.sub(r"CMD:\w+", "", llm_text, flags=re.I).strip()
else:
reply_base = ""
if intent == "move" or (intent == "unknown" and MOVE_RE.search(message)):
result = call_trained_vlm(board, puzzle if isinstance(puzzle, list) else None, model_id)
if not result.get("ok"):
return {
"reply": (
f"{meta['label']} 호출 실패: {result.get('error')}\n"
f"(채팅 AI → 학습 모델 명령 전달)"
),
"action": None,
"vlm": result,
}
r, c, v = result["row"], result["col"], result["value"]
spoken = reply_base or (
f"{meta['label']}에 「한 수」 명령을 전달했습니다."
)
return {
"reply": f"{spoken}\n→ ({r},{c}) = {v}",
"action": {"type": "place", "row": r, "col": c, "value": v},
"vlm": result,
}O próximo post é "6. O código-fonte completo e os modelos usados".
COMMENTS · CHAPTER 05
Uma IA que ajuda as pessoas 0 comentários
Ainda não há comentários. Deixe uma pergunta ou algo que queira acrescentar.
Para publicar um comentário, comece com o Google e registre um apelido e um e-mail.
Começar com o Google