게시판SGF 기보로 학습하는 바둑 LLM 인공지능예제 소스

예제 소스

SGF 기보로 학습하는 바둑 LLM 인공지능 · 파일 169개

hogu_scan_logs.py

#!/usr/bin/env python3
"""운영 VLM 로그에서 AI 가 둔 수 중 호구 관련 수를 센다.

  python scripts/hogu_scan_logs.py [--log ~/Library/Logs/MariGO/run-vlm-serve.log] [--since 2026-09-01] [--show 3]
"""

from __future__ import annotations

import argparse
import re
import sys
from collections import Counter
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from vlm_server.shapes import hogu_verdict, tiger_mouth  # noqa: E402

N = 19
REQ = re.compile(r"^(\S+ \S+) INFO GO19 request stones B=(\d+) W=(\d+) side=([BW])")
ROW = re.compile(r"^\s*(\d{1,2})  ([.XO ]+)$")
PICK = re.compile(r"INFO 계가선택 \((\d+),(\d+)\).*urgent=([Yn]) side=([BW])")


def show(cells: bytearray, mark: tuple[int, int]) -> str:
    lines = []
    for r in range(N):
        row = []
        for c in range(N):
            ch = ".XO"[cells[r * N + c]]
            row.append("*" if (r, c) == mark else ch)
        lines.append(f"{r:2d} " + " ".join(row))
    return "\n".join(lines)


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--log", default=str(Path.home() / "Library/Logs/MariGO/run-vlm-serve.log"))
    ap.add_argument("--since", default="")
    ap.add_argument("--show", type=int, default=3)
    a = ap.parse_args()

    stats: Counter = Counter()
    examples: dict[str, list[str]] = {"hogu_entry": [], "hogu_fill": [], "hogu_fill_edge": []}
    cells: bytearray | None = None
    side = 0
    ts = ""
    rows_read = 0
    with open(a.log, encoding="utf-8", errors="replace") as f:
        for line in f:
            m = REQ.match(line)
            if m:
                ts = m.group(1)
                if a.since and ts < a.since:
                    cells = None
                    continue
                cells = bytearray(N * N)
                side = 1 if m.group(4) == "B" else 2
                rows_read = 0
                continue
            if cells is not None and rows_read < N:
                mr = ROW.match(line.rstrip("\n"))
                if mr and int(mr.group(1)) == rows_read:
                    toks = mr.group(2).split()
                    for c, t in enumerate(toks[:N]):
                        cells[rows_read * N + c] = {".": 0, "X": 1, "O": 2}[t]
                    rows_read += 1
                continue
            mp = PICK.search(line)
            if mp and cells is not None and rows_read == N:
                r, c = int(mp.group(1)), int(mp.group(2))
                if (1 if mp.group(4) == "B" else 2) != side:
                    continue
                stats["moves"] += 1
                v = hogu_verdict(cells, r, c, side)
                info = tiger_mouth(cells, r, c)
                if info is not None:
                    stats["into_any_hogu_shape"] += 1
                if v:
                    key = v + ("_edge" if v == "hogu_fill" and info and info.walls else "")
                    stats[key] += 1
                    stats[f"{key}_urgent_{mp.group(3)}"] += 1
                    if len(examples.setdefault(key, [])) < a.show:
                        examples[key].append(f"{ts} side={'BW'[side - 1]} move=({r},{c})\n{show(cells, (r, c))}")
                cells = None

    print(dict(stats))
    n = max(1, stats["moves"])
    for k in ("hogu_entry", "hogu_fill", "hogu_fill_edge"):
        print(f"{k}: {stats[k]} ({100 * stats[k] / n:.2f}% of AI moves)")
    for k, ex in examples.items():
        for e in ex:
            print(f"\n--- {k}\n{e}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())