Spaces:
Sleeping
Sleeping
| """Canonical local environment API for the satellite submission.""" | |
| from typing import Any, Dict, Tuple | |
| from uuid import uuid4 | |
| try: | |
| from .constellation import SatelliteConstellationEnv | |
| from .models import ( | |
| SatelliteAction, | |
| SatelliteEnvState, | |
| SatelliteObservation, | |
| SatelliteReward, | |
| SatelliteState, | |
| ) | |
| from .tasks import EasyTask, HardTask, MediumTask, Task | |
| except ImportError: # pragma: no cover - flat-module fallback | |
| from constellation import SatelliteConstellationEnv | |
| from models import ( | |
| SatelliteAction, | |
| SatelliteEnvState, | |
| SatelliteObservation, | |
| SatelliteReward, | |
| SatelliteState, | |
| ) | |
| from tasks import EasyTask, HardTask, MediumTask, Task | |
| TASK_PRESETS = { | |
| "easy": EasyTask, | |
| "medium": MediumTask, | |
| "hard": HardTask, | |
| } | |
| class SatelliteTaskEnv: | |
| """Canonical typed environment with reset/step/state methods.""" | |
| def __init__( | |
| self, | |
| task_name: str = "medium", | |
| num_satellites: int | None = None, | |
| max_steps: int | None = None, | |
| ): | |
| if task_name not in TASK_PRESETS: | |
| raise ValueError( | |
| f"Unsupported task_name '{task_name}'. Expected one of {sorted(TASK_PRESETS)}." | |
| ) | |
| self.task_name = task_name | |
| self.task: Task = TASK_PRESETS[task_name]() | |
| default_satellites = num_satellites or 5 | |
| default_max_steps = max_steps or 100 | |
| self._sim = SatelliteConstellationEnv( | |
| num_satellites=default_satellites, | |
| max_steps=default_max_steps, | |
| ) | |
| self.episode_id = str(uuid4()) | |
| self.done = False | |
| self.last_info: Dict[str, Any] = {} | |
| self.last_reward = SatelliteReward(value=0.0, components={}) | |
| self.task.setup_environment(self._sim) | |
| def list_tasks() -> Dict[str, str]: | |
| """List selectable task presets.""" | |
| return {name: task_cls().description for name, task_cls in TASK_PRESETS.items()} | |
| def set_task(self, task_name: str) -> None: | |
| """Switch to a different task preset.""" | |
| if task_name not in TASK_PRESETS: | |
| raise ValueError( | |
| f"Unsupported task_name '{task_name}'. Expected one of {sorted(TASK_PRESETS)}." | |
| ) | |
| self.task_name = task_name | |
| self.task = TASK_PRESETS[task_name]() | |
| self.task.setup_environment(self._sim) | |
| def reset(self) -> SatelliteObservation: | |
| """Reset the active task and return the initial observation.""" | |
| self.episode_id = str(uuid4()) | |
| self.done = False | |
| self.last_info = {"task_name": self.task_name, "reset": True} | |
| self.last_reward = SatelliteReward(value=0.0, components={}) | |
| self.task.setup_environment(self._sim) | |
| observation_dict = self._sim.reset() | |
| return self._observation_from_dict( | |
| observation_dict, | |
| reward=self.last_reward.value, | |
| done=self.done, | |
| metadata=self.last_info, | |
| ) | |
| def step( | |
| self, action: SatelliteAction | |
| ) -> Tuple[SatelliteObservation, SatelliteReward, bool, Dict[str, Any]]: | |
| """Execute one action and return (observation, reward, done, info).""" | |
| observation_dict, reward_value, done, info = self._sim.step(action.satellite_actions) | |
| self.done = done | |
| self.last_info = {"task_name": self.task_name, **info} | |
| self.last_reward = SatelliteReward( | |
| value=reward_value, | |
| components=info.get("reward_components", {}), | |
| ) | |
| observation = self._observation_from_dict( | |
| observation_dict, | |
| reward=reward_value, | |
| done=done, | |
| metadata=self.last_info, | |
| ) | |
| return observation, self.last_reward, done, self.last_info | |
| def state(self) -> SatelliteEnvState: | |
| """Return a typed state snapshot for the current episode.""" | |
| observation_dict = self._sim._get_observation() | |
| satellites = [SatelliteState(**sat) for sat in observation_dict["satellites"]] | |
| return SatelliteEnvState( | |
| episode_id=self.episode_id, | |
| task_name=self.task_name, | |
| step_count=self._sim.current_step, | |
| max_steps=self._sim.max_steps, | |
| seed=self._sim.seed + self._sim.episode_index, | |
| done=self.done, | |
| total_reward=observation_dict["total_reward"], | |
| metrics={key: float(value) for key, value in self._sim.metrics.items()}, | |
| satellites=satellites, | |
| ground_stations=observation_dict["ground_stations"], | |
| weather_conditions=observation_dict["weather_conditions"], | |
| pending_tasks=observation_dict["pending_tasks"], | |
| ) | |
| def _observation_from_dict( | |
| self, | |
| observation_dict: Dict[str, Any], | |
| reward: float, | |
| done: bool, | |
| metadata: Dict[str, Any], | |
| ) -> SatelliteObservation: | |
| satellites = [SatelliteState(**sat) for sat in observation_dict["satellites"]] | |
| return SatelliteObservation( | |
| satellites=satellites, | |
| time_step=observation_dict["time_step"], | |
| ground_stations=observation_dict["ground_stations"], | |
| weather_conditions=observation_dict["weather_conditions"], | |
| pending_tasks=observation_dict["pending_tasks"], | |
| total_reward=observation_dict["total_reward"], | |
| reward=reward, | |
| done=done, | |
| metadata=metadata, | |
| ) | |