Spaces:
Sleeping
Sleeping
| """TrafficPattern task - Medium task with 4 aircraft separation management.""" | |
| import math | |
| from typing import TYPE_CHECKING | |
| from .base import Task | |
| if TYPE_CHECKING: | |
| from rl_env.environment import ATCEnv | |
| class TrafficPatternTask(Task): | |
| """ | |
| Medium task: 4 aircraft, separation management. | |
| Scenario: Four aircraft approach from different directions. | |
| The agent must sequence them for landing while maintaining | |
| separation standards and avoiding runway incursions. | |
| Scoring: | |
| - 0.4 for all landed | |
| - 0.3 for no collisions | |
| - 0.2 for no runway incursions | |
| - 0.1 for avg time < threshold | |
| """ | |
| AVG_TIME_THRESHOLD_SECONDS = 400.0 | |
| def setup(self, env: "ATCEnv") -> None: | |
| """Configure environment for traffic pattern task.""" | |
| env.reset(task="traffic_pattern", skip_spawn=True) | |
| upwind_gates = env._select_upwind_gates() | |
| gates = ( | |
| [g for g in ["N", "S", "E", "W"] if g in upwind_gates] | |
| if upwind_gates | |
| else ["N", "S", "E", "W"] | |
| ) | |
| if len(gates) < 4: | |
| gates = (gates * 4)[:4] | |
| ac_types = ["B737", "A320", "B777", "E190"] | |
| weight_classes = ["Heavy", "Medium", "Light"] | |
| for i in range(4): | |
| gate = gates[i % len(gates)] | |
| payload = { | |
| "callsign": f"RL{i + 1:03d}", | |
| "ac_type": ac_types[i], | |
| "weight_class": weight_classes[i % len(weight_classes)], | |
| "gate": gate, | |
| "altitude": 8000 + (i * 1000), | |
| "heading": None, | |
| "speed": 250, | |
| } | |
| env.add_pending_spawn( | |
| spawn_time=i * 20.0, method="add_aircraft", payload=payload | |
| ) | |
| env._initial_aircraft_count = 4 | |
| env._previous_observation = env._build_observation() | |
| def grade(self, env: "ATCEnv") -> float: | |
| """ | |
| Calculate score for traffic pattern task. | |
| Returns: | |
| Score between 0.0 and 1.0 | |
| """ | |
| score = 0.0 | |
| all_landed = self._all_landed(env) | |
| collision = self._has_collision(env) | |
| runway_incursion = self._has_runway_incursion(env) | |
| sim_time = env.engine.simulation_time if env.engine else 0.0 | |
| if all_landed: | |
| score += 0.4 | |
| if not collision: | |
| score += 0.3 | |
| if not runway_incursion: | |
| score += 0.2 | |
| if all_landed and not collision and not runway_incursion: | |
| if sim_time <= self.AVG_TIME_THRESHOLD_SECONDS: | |
| score += 0.1 | |
| else: | |
| time_ratio = max( | |
| 0.0, | |
| 1.0 | |
| - (sim_time - self.AVG_TIME_THRESHOLD_SECONDS) | |
| / self.AVG_TIME_THRESHOLD_SECONDS, | |
| ) | |
| score += 0.1 * time_ratio | |
| return max(0.0, min(1.0, score)) | |
| def is_complete(self, env: "ATCEnv") -> bool: | |
| """Check if episode should end.""" | |
| if env.engine is None: | |
| return False | |
| if env.engine.is_terminal: | |
| return True | |
| if self._all_landed(env): | |
| return True | |
| if self._has_collision(env): | |
| return True | |
| if self._has_exited_airspace(env): | |
| return True | |
| return False | |
| def _all_landed(self, env: "ATCEnv") -> bool: | |
| """Check if all aircraft have landed.""" | |
| assert env.engine is not None | |
| if not env.engine.aircrafts: | |
| return False | |
| for ac in env.engine.aircrafts.values(): | |
| if ac.state not in ("LANDING", "TAXIING"): | |
| return False | |
| return True | |
| def _has_collision(self, env: "ATCEnv") -> bool: | |
| """Check if collision occurred.""" | |
| assert env.engine is not None | |
| for event in env.engine.event_buffer: | |
| if event.get("type") == "CRASH": | |
| return True | |
| if event.get("type") == "SEPARATION_VIOLATION": | |
| return True | |
| return False | |
| def _has_runway_incursion(self, env: "ATCEnv") -> bool: | |
| """Check if runway incursion occurred.""" | |
| assert env.engine is not None | |
| for event in env.engine.event_buffer: | |
| if event.get("type") == "RUNWAY_INCURSION": | |
| return True | |
| if event.get("type") == "RUNWAY_CONFLICT": | |
| return True | |
| return False | |
| def _has_exited_airspace(self, env: "ATCEnv") -> bool: | |
| """Check if aircraft has exited airspace.""" | |
| assert env.engine is not None | |
| for ac in env.engine.aircrafts.values(): | |
| if ac.altitude < 0 or ac.altitude > 45000: | |
| return True | |
| dist = math.sqrt(ac.x**2 + ac.y**2) | |
| if dist > 100: | |
| return True | |
| return False | |
| def difficulty(self) -> str: | |
| """Return difficulty level.""" | |
| return "medium" | |