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

예제 소스

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

test_nolimit_recheck.py

from __future__ import annotations

import asyncio
import random
import sys
import unittest
from pathlib import Path

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

import scripts.monitor_mariago_web_play as mon  # noqa: E402
from scripts.monitor_mariago_web_play import (  # noqa: E402
    BLACK,
    BOARD_SIZE,
    EMPTY,
    SAFETY_PLIES,
    WHITE,
    _fills_own_region,
    empty_board,
    heuristic_move,
    idx,
    recheck_pass,
    resolve_max_moves,
)
from vlm_server.cross_lessons import effective_winner, midgame_pass_counts  # noqa: E402
from vlm_server.dataset_passfix import collect, pass_positions  # noqa: E402


def board_with(*, black=(), white=()):
    board = empty_board()
    for r, c in black:
        board[idx(r, c)] = BLACK
    for r, c in white:
        board[idx(r, c)] = WHITE
    return board


class NoLimitTest(unittest.TestCase):
    def test_zero_means_unlimited_with_safety_cap(self):
        self.assertEqual(resolve_max_moves(0), SAFETY_PLIES)
        self.assertEqual(resolve_max_moves(-1), SAFETY_PLIES)
        self.assertEqual(resolve_max_moves(150), 150)
        self.assertEqual(resolve_max_moves(500), 200)

    def test_heuristic_does_not_fill_own_corner(self):
        wall = [(3, c) for c in range(4)] + [(r, 3) for r in range(3)]
        board = board_with(black=wall, white=[(10, 10)])
        self.assertTrue(_fills_own_region(board, 1, 1, BLACK, None))
        self.assertFalse(_fills_own_region(board, 9, 9, BLACK, None))

    def test_heuristic_passes_when_only_own_eyes_remain(self):
        board = [BLACK] * (BOARD_SIZE * BOARD_SIZE)
        for r in range(1, BOARD_SIZE, 4):
            for c in range(1, BOARD_SIZE, 4):
                board[idx(r, c)] = EMPTY
        _r, _c, is_pass = heuristic_move(board, BLACK, None, random.Random(3))
        self.assertTrue(is_pass)

    def test_heuristic_still_plays_in_the_opening(self):
        _r, _c, is_pass = heuristic_move(empty_board(), BLACK, None, random.Random(3))
        self.assertFalse(is_pass)


class PassRecheckTest(unittest.TestCase):
    def _run(self, reply: dict):
        async def fake(*_args, **_kwargs):
            return reply

        orig = mon.ws_request_move
        mon.ws_request_move = fake
        try:
            board = board_with(black=[(3, 3)], white=[(15, 15)])
            return asyncio.run(
                recheck_pass("ws://x", board, None, 1, [], 1.0, ply=10, first_dt=0.5)
            )
        finally:
            mon.ws_request_move = orig

    def test_confirmed_pass(self):
        flip, rec = self._run({"ok": True, "pass": True})
        self.assertIsNone(flip)
        self.assertEqual(rec["result"], "pass_confirmed")
        self.assertEqual(rec["phase"], "midgame")

    def test_flipped_to_legal_move(self):
        flip, rec = self._run({"ok": True, "pass": False, "row": 9, "col": 9})
        self.assertEqual(flip, (9, 9))
        self.assertEqual(rec["result"], "pass_flipped")

    def test_flipped_to_illegal_move_counts_as_pass(self):
        flip, rec = self._run({"ok": True, "pass": False, "row": 3, "col": 3})
        self.assertIsNone(flip)
        self.assertEqual(rec["result"], "flipped_illegal")


class ForfeitWeightTest(unittest.TestCase):
    MOVES = [
        "1. B D16 (3,3)",
        "2. W PASS (2.0s)",
        "3. B Q16 (3,15)",
        "4. W PASS (2.0s)",
        "5. B D4 (15,3)",
        "6. W PASS (2.0s)",
    ]

    def test_midgame_passes_are_counted(self):
        self.assertEqual(midgame_pass_counts(self.MOVES), {"black": 0, "white": 3})

    def test_win_over_repeated_passes_gets_no_winner_weight(self):
        self.assertIsNone(effective_winner(self.MOVES, "black"))

    def test_normal_win_keeps_weight(self):
        moves = ["1. B D16 (3,3)", "2. W Q4 (15,15)"]
        self.assertEqual(effective_winner(moves, "black"), "black")
        self.assertEqual(effective_winner(self.MOVES, "white"), "white")


class PassfixPositionsTest(unittest.TestCase):
    def test_replay_yields_board_before_each_white_pass(self):
        moves = ForfeitWeightTest.MOVES + ["7. W Q4 (15,15) (1.0s, recheck)"]
        got = list(pass_positions(moves))
        self.assertEqual([ply for ply, _ in got], [1, 3, 5])
        _, last = got[-1]
        self.assertEqual(last[3 * BOARD_SIZE + 3], BLACK)
        self.assertEqual(last[15 * BOARD_SIZE + 3], BLACK)
        self.assertEqual(sum(1 for v in last if v), 3)

    def test_collect_dedups_and_filters_endgame(self):
        import json
        import tempfile

        with tempfile.TemporaryDirectory() as td:
            games = Path(td) / "games.jsonl"
            rec = {"game_id": 1, "moves": ForfeitWeightTest.MOVES}
            games.write_text(json.dumps(rec) + "\n" + json.dumps(rec) + "\n", encoding="utf-8")
            cases = Path(td) / "pass_cases.jsonl"
            full = [BLACK] * (BOARD_SIZE * BOARD_SIZE - 10) + [EMPTY] * 10
            cases.write_text(
                json.dumps({"game_id": 2, "board": full, "result": "pass_confirmed"}) + "\n",
                encoding="utf-8",
            )
            pos = collect([games], [cases], min_empty=120)
        self.assertEqual(len(pos), 3)
        self.assertTrue(all(p["empty"] >= 120 for p in pos))


if __name__ == "__main__":
    unittest.main()