Help, not a match
Unlike Go or Gomoku, sudoku has no opponent. If the AI solves everything by itself, there is nothing left for the person to do. So in the web sudoku (stoku.mariago.kr), people solve the puzzle themselves and ask the AI for a move only when they get stuck.

Ways to ask for a move
- Ask with the "AI move" button on the screen or in the chat, with phrases like "make a move" or "hint".
- The chat server uses Qwen2.5-1.5B-Instruct and regular expressions to tell whether it is a move request.
- If it is a move request, it sends the current board to the sudoku server, receives one cell and one digit, and places it on the person's board.
The web server currently does not load the VLM and picks certain moves with code alone. It first looks for a box, row or column with one empty cell left, and if there is none, finds a cell that is the only place for some digit. In effect, the code does all the work that the VLM and the code shared in post 4. The model list on the screen shows lora_move, but the actual move is decided by this strategy. It does not yet show why that cell was chosen.
Importing from a photo
The Qt app can import a sudoku from paper or a screen as a photo. It crops the grid, gathers only the cells with digits and has the board-reading 8B LoRA from post 3 read them.

Records of solving on the web
Over a little more than three weeks, 40 games were opened on the web. 26 were solved to the end, taking about 12 minutes on average. 9 were abandoned and 5 remain in progress. The completed games were 20 medium, 3 hard, 2 hell and 1 extreme.
The recorded actions were 1,430 digit placements, 135 notes, 62 erasures and 23 undos.

The actual code for this step
This is the order in which the web server picks a certain move.
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)This is the part where the chat server receives a message and, if it is a move request, passes it to the sudoku server.
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,
}The next post is "6. The full source code and the models used".
COMMENTS · CHAPTER 05
An AI that helps people 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