from __future__ import annotations from config import load_settings from .models import LegalMove from .storage import read_json def load_game_graph() -> dict: settings = load_settings() return read_json(settings.game_graph_path) def all_junction_ids() -> list[int]: graph = load_game_graph() return sorted(int(node["id"]) for node in graph.get("nodes", [])) def adjacent_junctions(junction_id: int) -> list[int]: graph = load_game_graph() adjacency = graph.get("adjacency", {}).get(str(junction_id), []) return sorted({int(item["destination"]) for item in adjacency}) def legal_moves_from(junction_id: int, blocked_edges: list[dict] | None = None) -> list[LegalMove]: graph = load_game_graph() blocked_edges = blocked_edges or [] moves: list[LegalMove] = [] for edge in graph.get("edges", []): source = int(edge["source"]) target = int(edge["target"]) if junction_id not in (source, target): continue destination = target if source == junction_id else source for mode in edge.get("modes", []): blocked = _is_blocked(junction_id, destination, mode, blocked_edges) moves.append(LegalMove(destination=destination, via=(junction_id, destination), mode=mode, blocked=blocked)) return sorted(moves, key=lambda move: (move.destination, move.mode)) def _is_blocked(source: int, target: int, mode: str, blocks: list[dict]) -> bool: for block in blocks: block_type = block.get("block_type") if block_type == "mode_block" and block.get("mode") == mode: return True if block_type == "junction_block" and block.get("junction_id") in (source, target): return True if block_type == "edge_block": same_edge = {block.get("from_junction"), block.get("to_junction")} == {source, target} same_mode = block.get("mode") in (None, mode) if same_edge and same_mode: return True return False