예제 소스
장기로 이해하는 규칙 엔진과 평가 함수 탐색 · 파일 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