게시판장기로 이해하는 규칙 엔진과 평가 함수 탐색예제 소스

예제 소스

장기로 이해하는 규칙 엔진과 평가 함수 탐색 · 파일 59개

rules.py

"""장기 행마 (장군 여부는 따지지 않는 pseudo-legal). 기보 복원용.

판: 90칸 리스트, 칸 = '' 또는 'cR' 처럼 (진영 c/h + 종류 K A R C H E P), index = rank*9 + file
rank 0..2 = 한 궁성, 7..9 = 초 궁성.
"""

from __future__ import annotations

PALACE_DIAGONALS = [
    [(3, 0), (4, 1), (5, 2)],
    [(5, 0), (4, 1), (3, 2)],
    [(3, 7), (4, 8), (5, 9)],
    [(5, 7), (4, 8), (3, 9)],
]
ORTHO = [(1, 0), (-1, 0), (0, 1), (0, -1)]


def inside(f: int, r: int) -> bool:
    return 0 <= f < 9 and 0 <= r < 10


def in_palace(f: int, r: int, side: str | None = None) -> bool:
    if not 3 <= f <= 5:
        return False
    if side == "h":
        return r <= 2
    if side == "c":
        return r >= 7
    return r <= 2 or r >= 7


def diag_steps(f: int, r: int) -> list[tuple[int, int]]:
    """궁성 대각선 위에서 한 칸 이웃."""
    out = []
    for line in PALACE_DIAGONALS:
        if (f, r) in line:
            i = line.index((f, r))
            for j in (i - 1, i + 1):
                if 0 <= j < 3:
                    out.append(line[j])
    return out


def diag_rays(f: int, r: int) -> list[list[tuple[int, int]]]:
    """궁성 대각선을 따라 한쪽 방향으로 이어지는 칸들."""
    rays = []
    for line in PALACE_DIAGONALS:
        if (f, r) in line:
            i = line.index((f, r))
            if i < 2:
                rays.append(line[i + 1 :])
            if i > 0:
                rays.append(list(reversed(line[:i])))
    return rays


def targets(board: list[str], idx: int) -> list[int]:
    piece = board[idx]
    if not piece:
        return []
    side, kind = piece[0], piece[1]
    f, r = idx % 9, idx // 9
    out: list[int] = []

    def at(ff: int, rr: int) -> str:
        return board[rr * 9 + ff]

    def add(ff: int, rr: int) -> None:
        if inside(ff, rr) and (not at(ff, rr) or at(ff, rr)[0] != side):
            out.append(rr * 9 + ff)

    if kind in ("K", "A"):
        for df, dr in ORTHO:
            if in_palace(f + df, r + dr, side):
                add(f + df, r + dr)
        for ff, rr in diag_steps(f, r):
            if in_palace(ff, rr, side):
                add(ff, rr)

    elif kind == "R":
        rays = [[(f + df * k, r + dr * k) for k in range(1, 10)] for df, dr in ORTHO] + diag_rays(f, r)
        for ray in rays:
            for ff, rr in ray:
                if not inside(ff, rr):
                    break
                add(ff, rr)
                if at(ff, rr):
                    break

    elif kind == "C":
        rays = [[(f + df * k, r + dr * k) for k in range(1, 10)] for df, dr in ORTHO] + diag_rays(f, r)
        for ray in rays:
            jumped = False
            for ff, rr in ray:
                if not inside(ff, rr):
                    break
                p = at(ff, rr)
                if not jumped:
                    if p:
                        if p[1] == "C":
                            break
                        jumped = True
                    continue
                if p:
                    if p[1] != "C":
                        add(ff, rr)
                    break
                add(ff, rr)

    elif kind in ("H", "E"):
        for df, dr in ORTHO:
            ff, rr = f + df, r + dr
            if not inside(ff, rr) or at(ff, rr):
                continue
            diags = [(df, 1), (df, -1)] if df else [(1, dr), (-1, dr)]
            for sf, sr in diags:
                if kind == "H":
                    add(ff + sf, rr + sr)
                    continue
                mf, mr = ff + sf, rr + sr
                if inside(mf, mr) and not at(mf, mr):
                    add(mf + sf, mr + sr)

    elif kind == "P":
        fwd = -1 if side == "c" else 1
        for df, dr in ((0, fwd), (1, 0), (-1, 0)):
            add(f + df, r + dr)
        for ff, rr in diag_steps(f, r):
            if rr - r == fwd:
                add(ff, rr)

    return out