Files
coorl-lost-cities/tests/games/classic/test_masks.py
T
coolguy 12b59211b2 Python 포맷팅과 pre-commit 설정
맥락:
- Python 코드만 대상으로 Ruff 기반 format/lint와 pre-commit hook을 도입한다.
- Cython game.pyx는 Ruff 대상에서 제외해 포맷터 충돌을 피한다.

변경:
- ruff와 pre-commit dev dependency 및 Ruff 설정을 추가했다.
- pre-commit config와 Python format/check 스크립트를 추가했다.
- Ruff format/check --fix 결과로 Python import 정렬과 포맷을 적용했다.

확인:
- uv sync --extra gui
- uv run pre-commit run --all-files
- scripts/check-python.sh
- uv run lost-cities-classic
2026-05-06 19:59:07 +09:00

60 lines
2.0 KiB
Python

from coolrl_lost_cities.games.classic.game import Card, GameState, LostCitiesConfig
from coolrl_lost_cities.games.classic.bots import RandomBot
def test_legal_mask_has_action_in_nonterminal_phases() -> None:
state = GameState.new_game(LostCitiesConfig(seed=3))
while not state.terminal:
assert any(state.legal_mask())
action = RandomBot(11).act(state)
state.apply_action(action)
def test_empty_hand_slots_are_masked() -> None:
state = GameState.empty(LostCitiesConfig())
state.hands[0] = [Card(0, 1)]
mask = state.legal_card_mask()
assert mask[0] is True
assert mask[1] is True
assert all(value is False for value in mask[2:])
def test_empty_discard_pile_draw_is_illegal() -> None:
state = GameState.empty(LostCitiesConfig())
state.phase = "draw"
state.deck = [Card(0, 1)]
mask = state.legal_draw_mask()
assert mask[0] is True
assert all(mask[1 + color] is False for color in range(state.config.n_colors))
def test_unified_legal_mask_has_fixed_shape_across_phases() -> None:
config = LostCitiesConfig()
state = GameState.new_game(config, seed=1)
assert len(state.unified_legal_mask()) == config.action_size
action = next(index for index, legal in enumerate(state.legal_mask()) if legal)
state.apply_action(action)
mask = state.unified_legal_mask()
assert state.phase == "draw"
assert len(mask) == config.action_size
assert all(value is False for value in mask[: config.card_action_size])
assert any(mask[config.card_action_size :])
def test_random_fuzz_invariants() -> None:
config = LostCitiesConfig(n_colors=3, n_ranks=5, n_handshakes=1, hand_size=5)
bot = RandomBot(99)
for seed in range(1000):
state = GameState.new_game(config, seed=seed)
steps = 0
while not state.terminal:
mask = state.legal_mask()
assert any(mask)
action = bot.act(state)
state.apply_action(action)
steps += 1
assert steps < 1000