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
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
from coolrl_lost_cities.games.classic.game import Card, GameState, LostCitiesConfig
|
||||
|
||||
from coolrl_lost_cities.games.classic.bots import (
|
||||
LostCitiesBot,
|
||||
RandomBot,
|
||||
SafeHeuristicBot,
|
||||
play_game,
|
||||
)
|
||||
from coolrl_lost_cities.games.classic.game import Card, GameState, LostCitiesConfig
|
||||
from coolrl_lost_cities.games.classic.bots.heuristic import draw_from_discard_action
|
||||
|
||||
|
||||
@@ -162,14 +163,17 @@ def test_safe_heuristic_avoids_opening_weak_fifth_color() -> None:
|
||||
state.hands[0] = [weak_open, Card(color=4, rank=7), Card(color=0, rank=6)]
|
||||
state.sort_hand(0)
|
||||
|
||||
assert bot._should_open_expedition(
|
||||
state=state,
|
||||
player=0,
|
||||
color=4,
|
||||
opening_card=weak_open,
|
||||
derived=bot._derived(state),
|
||||
deck_left=config.deck_size,
|
||||
) is False
|
||||
assert (
|
||||
bot._should_open_expedition(
|
||||
state=state,
|
||||
player=0,
|
||||
color=4,
|
||||
opening_card=weak_open,
|
||||
derived=bot._derived(state),
|
||||
deck_left=config.deck_size,
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_safe_heuristic_prefers_followup_on_started_expedition() -> None:
|
||||
|
||||
@@ -3,10 +3,9 @@ import random
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import coolrl_lost_cities.games.classic as classic
|
||||
from coolrl_lost_cities.games.classic.game import Card, GameState, LostCitiesConfig
|
||||
|
||||
import coolrl_lost_cities.games.classic as classic
|
||||
|
||||
FIXTURE_DIR = Path(classic.__file__).resolve().parent / "fixtures"
|
||||
|
||||
@@ -43,13 +42,9 @@ def test_new_game_from_deck_uses_explicit_internal_deck_order() -> None:
|
||||
|
||||
def test_snapshot_roundtrip_preserves_json_state() -> None:
|
||||
state = GameState.new_game(LostCitiesConfig(seed=5))
|
||||
first_action = next(
|
||||
index for index, legal in enumerate(state.unified_legal_mask()) if legal
|
||||
)
|
||||
first_action = next(index for index, legal in enumerate(state.unified_legal_mask()) if legal)
|
||||
state.apply_unified_action(first_action)
|
||||
second_action = next(
|
||||
index for index, legal in enumerate(state.unified_legal_mask()) if legal
|
||||
)
|
||||
second_action = next(index for index, legal in enumerate(state.unified_legal_mask()) if legal)
|
||||
state.apply_unified_action(second_action)
|
||||
|
||||
payload = json.loads(json.dumps(state.to_snapshot()))
|
||||
@@ -108,11 +103,7 @@ def test_random_games_preserve_python_core_invariants() -> None:
|
||||
steps = 0
|
||||
while not state.terminal:
|
||||
state.validate_invariants()
|
||||
legal = [
|
||||
index
|
||||
for index, is_legal in enumerate(state.unified_legal_mask())
|
||||
if is_legal
|
||||
]
|
||||
legal = [index for index, is_legal in enumerate(state.unified_legal_mask()) if is_legal]
|
||||
state.apply_unified_action(rng.choice(legal))
|
||||
steps += 1
|
||||
assert steps < 1000
|
||||
@@ -129,8 +120,6 @@ def test_same_seed_and_action_sequence_are_deterministic() -> None:
|
||||
if left.terminal:
|
||||
break
|
||||
|
||||
action = next(
|
||||
index for index, is_legal in enumerate(left.unified_legal_mask()) if is_legal
|
||||
)
|
||||
action = next(index for index, is_legal in enumerate(left.unified_legal_mask()) if is_legal)
|
||||
left.apply_unified_action(action)
|
||||
right.apply_unified_action(action)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import numpy as np
|
||||
from coolrl_lost_cities.games.classic.game import Card, GameState, LostCitiesConfig
|
||||
|
||||
from coolrl_lost_cities.games.classic.env import LostCitiesEnv
|
||||
from coolrl_lost_cities.games.classic.game import Card, GameState, LostCitiesConfig
|
||||
|
||||
|
||||
def test_env_observation_uses_fixed_unified_mask() -> None:
|
||||
@@ -17,8 +17,8 @@ def test_env_observation_uses_fixed_unified_mask() -> None:
|
||||
|
||||
assert env.phase == "draw"
|
||||
assert obs["legal_mask"].shape == (config.action_size,)
|
||||
assert np.all(obs["legal_mask"][:config.card_action_size] == 0)
|
||||
assert np.any(obs["legal_mask"][config.card_action_size:])
|
||||
assert np.all(obs["legal_mask"][: config.card_action_size] == 0)
|
||||
assert np.any(obs["legal_mask"][config.card_action_size :])
|
||||
|
||||
|
||||
def test_env_step_accepts_legacy_draw_action_ids() -> None:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from coolrl_lost_cities.games.classic.bots import RandomBot
|
||||
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))
|
||||
@@ -39,8 +40,8 @@ def test_unified_legal_mask_has_fixed_shape_across_phases() -> None:
|
||||
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:])
|
||||
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:
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import pytest
|
||||
|
||||
from coolrl_lost_cities.games.classic.game import Card, GameState, IllegalMoveError, LostCitiesConfig, build_deck
|
||||
from coolrl_lost_cities.games.classic.game import (
|
||||
Card,
|
||||
GameState,
|
||||
IllegalMoveError,
|
||||
LostCitiesConfig,
|
||||
build_deck,
|
||||
)
|
||||
|
||||
|
||||
def test_deck_generation_count() -> None:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
import random
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from coolrl_lost_cities.games.classic.game import GameState, LostCitiesConfig, build_deck
|
||||
|
||||
import coolrl_lost_cities.games.classic as classic
|
||||
from coolrl_lost_cities.games.classic.game import GameState, LostCitiesConfig, build_deck
|
||||
from coolrl_lost_cities.games.classic.backends.rust import RUST_CORE_DIR
|
||||
|
||||
|
||||
LOST_CITIES_DIR = Path(classic.__file__).resolve().parent
|
||||
FIXTURE_DIR = LOST_CITIES_DIR / "fixtures"
|
||||
|
||||
@@ -81,11 +81,7 @@ def test_rust_randomized_fixture_traces_match_python_core(tmp_path: Path) -> Non
|
||||
for _ in range(64):
|
||||
if state.terminal:
|
||||
break
|
||||
legal = [
|
||||
index
|
||||
for index, is_legal in enumerate(state.unified_legal_mask())
|
||||
if is_legal
|
||||
]
|
||||
legal = [index for index, is_legal in enumerate(state.unified_legal_mask()) if is_legal]
|
||||
action = rng.choice(legal)
|
||||
state.apply_unified_action(action)
|
||||
steps.append({"action": action})
|
||||
@@ -154,9 +150,7 @@ def test_rust_grpc_contract_is_checked_from_python() -> None:
|
||||
|
||||
def test_rust_core_has_no_native_tests_left() -> None:
|
||||
rust_files = [
|
||||
path
|
||||
for path in (RUST_CORE_DIR / "src").rglob("*.rs")
|
||||
if "target" not in path.parts
|
||||
path for path in (RUST_CORE_DIR / "src").rglob("*.rs") if "target" not in path.parts
|
||||
]
|
||||
for path in rust_files:
|
||||
text = path.read_text()
|
||||
|
||||
Reference in New Issue
Block a user