refactor(config): centralize scenario configuration defaults
Browse files- README.md +0 -13
- app.py +36 -14
- config.py +45 -0
- fire_rescue_mcp/mcp_server.py +10 -5
- models.py +11 -6
- service.py +7 -3
- simulation.py +15 -10
README.md
CHANGED
|
@@ -165,19 +165,6 @@ Prompts for every stage live in `prompts.yaml`, making it easy to retune instruc
|
|
| 165 |
- [uv](https://github.com/astral-sh/uv) (optional but fastest)
|
| 166 |
- HuggingFace API token with access to the OpenAI-compatible inference endpoint (set as `HF_TOKEN`)
|
| 167 |
|
| 168 |
-
### Installation
|
| 169 |
-
|
| 170 |
-
```bash
|
| 171 |
-
# Clone
|
| 172 |
-
git clone https://github.com/ArkaiAriza/fire-rescue-mcp.git
|
| 173 |
-
cd fire-rescue-mcp
|
| 174 |
-
|
| 175 |
-
# Install deps (recommended)
|
| 176 |
-
uv sync
|
| 177 |
-
# or fall back to pip
|
| 178 |
-
pip install -e .
|
| 179 |
-
```
|
| 180 |
-
|
| 181 |
### Environment Variables
|
| 182 |
|
| 183 |
```bash
|
|
|
|
| 165 |
- [uv](https://github.com/astral-sh/uv) (optional but fastest)
|
| 166 |
- HuggingFace API token with access to the OpenAI-compatible inference endpoint (set as `HF_TOKEN`)
|
| 167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
### Environment Variables
|
| 169 |
|
| 170 |
```bash
|
app.py
CHANGED
|
@@ -11,6 +11,7 @@ import uuid
|
|
| 11 |
import gradio as gr
|
| 12 |
from typing import Optional
|
| 13 |
|
|
|
|
| 14 |
from service import (
|
| 15 |
ADVISOR_MODEL_CHOICES,
|
| 16 |
DEFAULT_ADVISOR_MODEL_CHOICE,
|
|
@@ -800,7 +801,7 @@ def deploy_at_cell(
|
|
| 800 |
_get_combined_advisor_messages(service),
|
| 801 |
service.get_event_log_text(),
|
| 802 |
render_status_html(state, is_thinking, thinking_stage),
|
| 803 |
-
] + updates
|
| 804 |
|
| 805 |
# Handle fire placement
|
| 806 |
if selection == "🔥 Fire":
|
|
@@ -890,14 +891,19 @@ def refresh_display(
|
|
| 890 |
thinking_stage = service.get_thinking_stage()
|
| 891 |
|
| 892 |
# Freeze background UI once a win/lose overlay has already been shown
|
|
|
|
|
|
|
| 893 |
overlay_state = changes.get("result_state", "")
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
|
|
|
| 899 |
display_cache["result_freeze_state"] = ""
|
| 900 |
|
|
|
|
|
|
|
| 901 |
# Timer control - stop when game ends
|
| 902 |
timer_update = gr.update()
|
| 903 |
if status in ["success", "fail"]:
|
|
@@ -1847,29 +1853,45 @@ def create_app() -> gr.Blocks:
|
|
| 1847 |
with gr.Accordion("⚙️ Settings & Controls", open=False):
|
| 1848 |
with gr.Row():
|
| 1849 |
with gr.Column(scale=1):
|
|
|
|
| 1850 |
fire_count = gr.Slider(
|
| 1851 |
-
minimum=
|
|
|
|
|
|
|
|
|
|
| 1852 |
label="🔥 Initial Fire Count",
|
| 1853 |
-
info="Number of fire starting points
|
| 1854 |
)
|
| 1855 |
with gr.Column(scale=1):
|
|
|
|
| 1856 |
fire_intensity = gr.Slider(
|
| 1857 |
-
minimum=
|
|
|
|
|
|
|
|
|
|
| 1858 |
label="🌡️ Fire Intensity",
|
| 1859 |
-
info="Initial fire strength
|
| 1860 |
)
|
| 1861 |
with gr.Row():
|
| 1862 |
with gr.Column(scale=1):
|
|
|
|
| 1863 |
building_count = gr.Slider(
|
| 1864 |
-
minimum=
|
|
|
|
|
|
|
|
|
|
| 1865 |
label="🏢 Building Count",
|
| 1866 |
info="Number of buildings (connected cluster)"
|
| 1867 |
)
|
| 1868 |
with gr.Column(scale=1):
|
|
|
|
| 1869 |
max_units = gr.Slider(
|
| 1870 |
-
minimum=
|
|
|
|
|
|
|
|
|
|
| 1871 |
label="🚒 Max Units",
|
| 1872 |
-
info="Maximum deployable units
|
| 1873 |
)
|
| 1874 |
with gr.Row():
|
| 1875 |
with gr.Column(scale=1):
|
|
@@ -2044,7 +2066,7 @@ def create_app() -> gr.Blocks:
|
|
| 2044 |
# Event handlers for grid buttons (click to place)
|
| 2045 |
for x, y, btn in grid_buttons:
|
| 2046 |
btn.click(
|
| 2047 |
-
fn=lambda sel, _x=x, _y=y
|
| 2048 |
inputs=[place_selector, service_state, display_cache_state],
|
| 2049 |
outputs=[result_popup, advisor_display, event_log_display, status_display] + all_buttons + [service_state, display_cache_state],
|
| 2050 |
)
|
|
|
|
| 11 |
import gradio as gr
|
| 12 |
from typing import Optional
|
| 13 |
|
| 14 |
+
from config import SCENARIO_DEFAULTS
|
| 15 |
from service import (
|
| 16 |
ADVISOR_MODEL_CHOICES,
|
| 17 |
DEFAULT_ADVISOR_MODEL_CHOICE,
|
|
|
|
| 801 |
_get_combined_advisor_messages(service),
|
| 802 |
service.get_event_log_text(),
|
| 803 |
render_status_html(state, is_thinking, thinking_stage),
|
| 804 |
+
] + updates + [service, display_cache]
|
| 805 |
|
| 806 |
# Handle fire placement
|
| 807 |
if selection == "🔥 Fire":
|
|
|
|
| 891 |
thinking_stage = service.get_thinking_stage()
|
| 892 |
|
| 893 |
# Freeze background UI once a win/lose overlay has already been shown
|
| 894 |
+
# but still allow the final tick (when the overlay first appears) to push
|
| 895 |
+
# its grid updates so the user sees the true end state.
|
| 896 |
overlay_state = changes.get("result_state", "")
|
| 897 |
+
cached_overlay_state = display_cache["result_freeze_state"]
|
| 898 |
+
overlay_first_tick = bool(overlay_state) and not cached_overlay_state
|
| 899 |
+
|
| 900 |
+
if overlay_state:
|
| 901 |
+
display_cache["result_freeze_state"] = overlay_state
|
| 902 |
+
elif cached_overlay_state:
|
| 903 |
display_cache["result_freeze_state"] = ""
|
| 904 |
|
| 905 |
+
freeze_background = bool(display_cache["result_freeze_state"]) and not overlay_first_tick
|
| 906 |
+
|
| 907 |
# Timer control - stop when game ends
|
| 908 |
timer_update = gr.update()
|
| 909 |
if status in ["success", "fail"]:
|
|
|
|
| 1853 |
with gr.Accordion("⚙️ Settings & Controls", open=False):
|
| 1854 |
with gr.Row():
|
| 1855 |
with gr.Column(scale=1):
|
| 1856 |
+
fire_count_defaults = SCENARIO_DEFAULTS["fire_count"]
|
| 1857 |
fire_count = gr.Slider(
|
| 1858 |
+
minimum=fire_count_defaults.minimum,
|
| 1859 |
+
maximum=fire_count_defaults.maximum,
|
| 1860 |
+
value=fire_count_defaults.value,
|
| 1861 |
+
step=fire_count_defaults.step,
|
| 1862 |
label="🔥 Initial Fire Count",
|
| 1863 |
+
info="Number of fire starting points"
|
| 1864 |
)
|
| 1865 |
with gr.Column(scale=1):
|
| 1866 |
+
fire_intensity_defaults = SCENARIO_DEFAULTS["fire_intensity"]
|
| 1867 |
fire_intensity = gr.Slider(
|
| 1868 |
+
minimum=fire_intensity_defaults.minimum,
|
| 1869 |
+
maximum=fire_intensity_defaults.maximum,
|
| 1870 |
+
value=fire_intensity_defaults.value,
|
| 1871 |
+
step=fire_intensity_defaults.step,
|
| 1872 |
label="🌡️ Fire Intensity",
|
| 1873 |
+
info="Initial fire strength"
|
| 1874 |
)
|
| 1875 |
with gr.Row():
|
| 1876 |
with gr.Column(scale=1):
|
| 1877 |
+
building_count_defaults = SCENARIO_DEFAULTS["building_count"]
|
| 1878 |
building_count = gr.Slider(
|
| 1879 |
+
minimum=building_count_defaults.minimum,
|
| 1880 |
+
maximum=building_count_defaults.maximum,
|
| 1881 |
+
value=building_count_defaults.value,
|
| 1882 |
+
step=building_count_defaults.step,
|
| 1883 |
label="🏢 Building Count",
|
| 1884 |
info="Number of buildings (connected cluster)"
|
| 1885 |
)
|
| 1886 |
with gr.Column(scale=1):
|
| 1887 |
+
max_units_defaults = SCENARIO_DEFAULTS["max_units"]
|
| 1888 |
max_units = gr.Slider(
|
| 1889 |
+
minimum=max_units_defaults.minimum,
|
| 1890 |
+
maximum=max_units_defaults.maximum,
|
| 1891 |
+
value=max_units_defaults.value,
|
| 1892 |
+
step=max_units_defaults.step,
|
| 1893 |
label="🚒 Max Units",
|
| 1894 |
+
info="Maximum deployable units"
|
| 1895 |
)
|
| 1896 |
with gr.Row():
|
| 1897 |
with gr.Column(scale=1):
|
|
|
|
| 2066 |
# Event handlers for grid buttons (click to place)
|
| 2067 |
for x, y, btn in grid_buttons:
|
| 2068 |
btn.click(
|
| 2069 |
+
fn=lambda sel, svc, cache, _x=x, _y=y: deploy_at_cell(_x, _y, sel, svc, cache),
|
| 2070 |
inputs=[place_selector, service_state, display_cache_state],
|
| 2071 |
outputs=[result_popup, advisor_display, event_log_display, status_display] + all_buttons + [service_state, display_cache_state],
|
| 2072 |
)
|
config.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Centralized configuration defaults for scenario controls."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from typing import Final
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@dataclass(frozen=True)
|
| 10 |
+
class SliderDefaults:
|
| 11 |
+
"""Slider configuration along with a helper to clamp values."""
|
| 12 |
+
|
| 13 |
+
minimum: float
|
| 14 |
+
maximum: float
|
| 15 |
+
value: float
|
| 16 |
+
step: float
|
| 17 |
+
|
| 18 |
+
def clamp(self, candidate: float) -> float:
|
| 19 |
+
"""Clamp ``candidate`` to this slider's min/max bounds."""
|
| 20 |
+
return max(self.minimum, min(self.maximum, candidate))
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
SCENARIO_DEFAULTS: Final[dict[str, SliderDefaults]] = {
|
| 24 |
+
"fire_count": SliderDefaults(minimum=1, maximum=40, value=20, step=1),
|
| 25 |
+
"fire_intensity": SliderDefaults(minimum=0.2, maximum=0.9, value=0.6, step=0.05),
|
| 26 |
+
"building_count": SliderDefaults(minimum=1, maximum=35, value=20, step=1),
|
| 27 |
+
"max_units": SliderDefaults(minimum=1, maximum=20, value=10, step=1),
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def range_text(key: str) -> str:
|
| 32 |
+
"""
|
| 33 |
+
Return a human-readable ``min-max`` string for any configured scenario slider.
|
| 34 |
+
Primarily used in docstrings / README notes to avoid diverging documentation.
|
| 35 |
+
"""
|
| 36 |
+
defaults = SCENARIO_DEFAULTS[key]
|
| 37 |
+
step_value = float(defaults.step)
|
| 38 |
+
if step_value.is_integer():
|
| 39 |
+
min_val = int(defaults.minimum)
|
| 40 |
+
max_val = int(defaults.maximum)
|
| 41 |
+
else:
|
| 42 |
+
min_val = defaults.minimum
|
| 43 |
+
max_val = defaults.maximum
|
| 44 |
+
return f"{min_val}-{max_val}"
|
| 45 |
+
|
fire_rescue_mcp/mcp_server.py
CHANGED
|
@@ -28,6 +28,7 @@ from mcp.server.fastmcp import FastMCP
|
|
| 28 |
# Add parent directory to path for imports
|
| 29 |
sys.path.insert(0, str(Path(__file__).parent.parent))
|
| 30 |
|
|
|
|
| 31 |
from models import CellType, UnitType
|
| 32 |
from simulation import SimulationEngine, SimulationConfig
|
| 33 |
|
|
@@ -49,6 +50,10 @@ def attach_engine(engine: SimulationEngine, session_id: str) -> None:
|
|
| 49 |
# Unit effective ranges (matching SimulationConfig, Chebyshev/square distance)
|
| 50 |
FIRE_TRUCK_RANGE = 1 # Square coverage radius (includes 8 neighbors)
|
| 51 |
HELICOPTER_RANGE = 2 # Square coverage radius (extends two cells in all directions)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
|
| 54 |
def detach_engine(session_id: str) -> None:
|
|
@@ -175,15 +180,15 @@ def reset_scenario(
|
|
| 175 |
max_units: int = 10,
|
| 176 |
session_id: Optional[str] = None,
|
| 177 |
) -> dict:
|
| 178 |
-
"""
|
| 179 |
Reset and initialize a new fire rescue simulation scenario.
|
| 180 |
|
| 181 |
Args:
|
| 182 |
seed: Random seed for reproducibility (optional)
|
| 183 |
-
fire_count: Number of initial fire points (
|
| 184 |
-
fire_intensity: Initial fire intensity (
|
| 185 |
-
building_count: Number of buildings to place (
|
| 186 |
-
max_units: Maximum deployable units (
|
| 187 |
|
| 188 |
Returns:
|
| 189 |
Status, summary, and emoji map of the initial state
|
|
|
|
| 28 |
# Add parent directory to path for imports
|
| 29 |
sys.path.insert(0, str(Path(__file__).parent.parent))
|
| 30 |
|
| 31 |
+
from config import range_text
|
| 32 |
from models import CellType, UnitType
|
| 33 |
from simulation import SimulationEngine, SimulationConfig
|
| 34 |
|
|
|
|
| 50 |
# Unit effective ranges (matching SimulationConfig, Chebyshev/square distance)
|
| 51 |
FIRE_TRUCK_RANGE = 1 # Square coverage radius (includes 8 neighbors)
|
| 52 |
HELICOPTER_RANGE = 2 # Square coverage radius (extends two cells in all directions)
|
| 53 |
+
FIRE_COUNT_RANGE_TEXT = range_text("fire_count")
|
| 54 |
+
FIRE_INTENSITY_RANGE_TEXT = range_text("fire_intensity")
|
| 55 |
+
BUILDING_COUNT_RANGE_TEXT = range_text("building_count")
|
| 56 |
+
MAX_UNITS_RANGE_TEXT = range_text("max_units")
|
| 57 |
|
| 58 |
|
| 59 |
def detach_engine(session_id: str) -> None:
|
|
|
|
| 180 |
max_units: int = 10,
|
| 181 |
session_id: Optional[str] = None,
|
| 182 |
) -> dict:
|
| 183 |
+
f"""
|
| 184 |
Reset and initialize a new fire rescue simulation scenario.
|
| 185 |
|
| 186 |
Args:
|
| 187 |
seed: Random seed for reproducibility (optional)
|
| 188 |
+
fire_count: Number of initial fire points ({FIRE_COUNT_RANGE_TEXT}, default: 10)
|
| 189 |
+
fire_intensity: Initial fire intensity ({FIRE_INTENSITY_RANGE_TEXT}, default: 0.5)
|
| 190 |
+
building_count: Number of buildings to place ({BUILDING_COUNT_RANGE_TEXT}, default: 20)
|
| 191 |
+
max_units: Maximum deployable units ({MAX_UNITS_RANGE_TEXT}, default: 10)
|
| 192 |
|
| 193 |
Returns:
|
| 194 |
Status, summary, and emoji map of the initial state
|
models.py
CHANGED
|
@@ -9,6 +9,11 @@ from enum import Enum
|
|
| 9 |
from typing import Optional
|
| 10 |
import random
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
class UnitType(str, Enum):
|
| 14 |
"""Types of firefighting units available."""
|
|
@@ -146,23 +151,23 @@ class WorldState:
|
|
| 146 |
fire_intensity: float = 0.6,
|
| 147 |
building_count: int = 16
|
| 148 |
):
|
| 149 |
-
"""
|
| 150 |
Initialize the grid with terrain and initial fires.
|
| 151 |
|
| 152 |
Args:
|
| 153 |
seed: Random seed for reproducibility
|
| 154 |
-
fire_count: Number of initial fire points (
|
| 155 |
fire_intensity: Initial fire intensity (0.0-1.0)
|
| 156 |
-
building_count: Number of buildings to place (
|
| 157 |
"""
|
| 158 |
if seed is not None:
|
| 159 |
random.seed(seed)
|
| 160 |
self.seed = seed
|
| 161 |
|
| 162 |
-
# Clamp values
|
| 163 |
-
fire_count =
|
| 164 |
fire_intensity = max(0.0, min(1.0, fire_intensity))
|
| 165 |
-
building_count =
|
| 166 |
|
| 167 |
# Generate random building positions (connected cluster)
|
| 168 |
self.building_positions = self._generate_building_positions(building_count)
|
|
|
|
| 9 |
from typing import Optional
|
| 10 |
import random
|
| 11 |
|
| 12 |
+
from config import SCENARIO_DEFAULTS
|
| 13 |
+
|
| 14 |
+
FIRE_COUNT_DEFAULTS = SCENARIO_DEFAULTS["fire_count"]
|
| 15 |
+
BUILDING_COUNT_DEFAULTS = SCENARIO_DEFAULTS["building_count"]
|
| 16 |
+
|
| 17 |
|
| 18 |
class UnitType(str, Enum):
|
| 19 |
"""Types of firefighting units available."""
|
|
|
|
| 151 |
fire_intensity: float = 0.6,
|
| 152 |
building_count: int = 16
|
| 153 |
):
|
| 154 |
+
f"""
|
| 155 |
Initialize the grid with terrain and initial fires.
|
| 156 |
|
| 157 |
Args:
|
| 158 |
seed: Random seed for reproducibility
|
| 159 |
+
fire_count: Number of initial fire points ({int(FIRE_COUNT_DEFAULTS.minimum)}-{int(FIRE_COUNT_DEFAULTS.maximum)})
|
| 160 |
fire_intensity: Initial fire intensity (0.0-1.0)
|
| 161 |
+
building_count: Number of buildings to place ({int(BUILDING_COUNT_DEFAULTS.minimum)}-{int(BUILDING_COUNT_DEFAULTS.maximum)})
|
| 162 |
"""
|
| 163 |
if seed is not None:
|
| 164 |
random.seed(seed)
|
| 165 |
self.seed = seed
|
| 166 |
|
| 167 |
+
# Clamp values using shared configuration
|
| 168 |
+
fire_count = int(FIRE_COUNT_DEFAULTS.clamp(fire_count))
|
| 169 |
fire_intensity = max(0.0, min(1.0, fire_intensity))
|
| 170 |
+
building_count = int(BUILDING_COUNT_DEFAULTS.clamp(building_count))
|
| 171 |
|
| 172 |
# Generate random building positions (connected cluster)
|
| 173 |
self.building_positions = self._generate_building_positions(building_count)
|
service.py
CHANGED
|
@@ -24,11 +24,15 @@ from agent import (
|
|
| 24 |
PlanResult,
|
| 25 |
CycleSummary,
|
| 26 |
)
|
|
|
|
| 27 |
from fire_rescue_mcp.mcp_client import LocalFastMCPClient
|
| 28 |
from fire_rescue_mcp.mcp_server import attach_engine, detach_engine, mcp as fastmcp_server
|
| 29 |
from models import SimulationStatus, CellType
|
| 30 |
from simulation import SimulationEngine
|
| 31 |
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
ADVISOR_MODEL_CHOICES = {
|
| 34 |
"GPT-OSS · HuggingFace (openai/gpt-oss-120b)": {
|
|
@@ -256,14 +260,14 @@ class SimulationService:
|
|
| 256 |
session_id: Optional[str] = None,
|
| 257 |
on_update: Optional[Callable] = None
|
| 258 |
) -> dict:
|
| 259 |
-
"""
|
| 260 |
Start a new simulation.
|
| 261 |
|
| 262 |
Args:
|
| 263 |
seed: Random seed for reproducibility
|
| 264 |
-
fire_count: Number of initial fire points (
|
| 265 |
fire_intensity: Initial fire intensity (0.0-1.0)
|
| 266 |
-
building_count: Number of buildings to place (
|
| 267 |
on_update: Callback function called on state changes
|
| 268 |
|
| 269 |
Returns:
|
|
|
|
| 24 |
PlanResult,
|
| 25 |
CycleSummary,
|
| 26 |
)
|
| 27 |
+
from config import range_text
|
| 28 |
from fire_rescue_mcp.mcp_client import LocalFastMCPClient
|
| 29 |
from fire_rescue_mcp.mcp_server import attach_engine, detach_engine, mcp as fastmcp_server
|
| 30 |
from models import SimulationStatus, CellType
|
| 31 |
from simulation import SimulationEngine
|
| 32 |
|
| 33 |
+
FIRE_COUNT_RANGE_TEXT = range_text("fire_count")
|
| 34 |
+
BUILDING_COUNT_RANGE_TEXT = range_text("building_count")
|
| 35 |
+
|
| 36 |
|
| 37 |
ADVISOR_MODEL_CHOICES = {
|
| 38 |
"GPT-OSS · HuggingFace (openai/gpt-oss-120b)": {
|
|
|
|
| 260 |
session_id: Optional[str] = None,
|
| 261 |
on_update: Optional[Callable] = None
|
| 262 |
) -> dict:
|
| 263 |
+
f"""
|
| 264 |
Start a new simulation.
|
| 265 |
|
| 266 |
Args:
|
| 267 |
seed: Random seed for reproducibility
|
| 268 |
+
fire_count: Number of initial fire points ({FIRE_COUNT_RANGE_TEXT})
|
| 269 |
fire_intensity: Initial fire intensity (0.0-1.0)
|
| 270 |
+
building_count: Number of buildings to place ({BUILDING_COUNT_RANGE_TEXT})
|
| 271 |
on_update: Callback function called on state changes
|
| 272 |
|
| 273 |
Returns:
|
simulation.py
CHANGED
|
@@ -7,16 +7,21 @@ Handles fire spread, unit behavior, and win/lose conditions.
|
|
| 7 |
import random
|
| 8 |
from typing import Optional
|
| 9 |
|
|
|
|
| 10 |
from models import (
|
| 11 |
-
WorldState,
|
| 12 |
-
Cell,
|
| 13 |
-
CellType,
|
| 14 |
-
Unit,
|
| 15 |
-
UnitType,
|
| 16 |
SimulationStatus,
|
| 17 |
-
Event
|
| 18 |
)
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
class SimulationConfig:
|
| 22 |
"""Configuration parameters for the simulation."""
|
|
@@ -64,15 +69,15 @@ class SimulationEngine:
|
|
| 64 |
building_count: int = 16,
|
| 65 |
max_units: int = 10
|
| 66 |
) -> WorldState:
|
| 67 |
-
"""
|
| 68 |
Reset and initialize a new simulation.
|
| 69 |
|
| 70 |
Args:
|
| 71 |
seed: Random seed for reproducibility
|
| 72 |
-
fire_count: Number of initial fire points (
|
| 73 |
fire_intensity: Initial fire intensity (0.0-1.0)
|
| 74 |
-
building_count: Number of buildings to place (
|
| 75 |
-
max_units: Maximum number of deployable units (
|
| 76 |
"""
|
| 77 |
self.world = WorldState(
|
| 78 |
width=self.config.GRID_WIDTH,
|
|
|
|
| 7 |
import random
|
| 8 |
from typing import Optional
|
| 9 |
|
| 10 |
+
from config import range_text
|
| 11 |
from models import (
|
| 12 |
+
WorldState,
|
| 13 |
+
Cell,
|
| 14 |
+
CellType,
|
| 15 |
+
Unit,
|
| 16 |
+
UnitType,
|
| 17 |
SimulationStatus,
|
| 18 |
+
Event,
|
| 19 |
)
|
| 20 |
|
| 21 |
+
FIRE_COUNT_RANGE_TEXT = range_text("fire_count")
|
| 22 |
+
BUILDING_COUNT_RANGE_TEXT = range_text("building_count")
|
| 23 |
+
MAX_UNITS_RANGE_TEXT = range_text("max_units")
|
| 24 |
+
|
| 25 |
|
| 26 |
class SimulationConfig:
|
| 27 |
"""Configuration parameters for the simulation."""
|
|
|
|
| 69 |
building_count: int = 16,
|
| 70 |
max_units: int = 10
|
| 71 |
) -> WorldState:
|
| 72 |
+
f"""
|
| 73 |
Reset and initialize a new simulation.
|
| 74 |
|
| 75 |
Args:
|
| 76 |
seed: Random seed for reproducibility
|
| 77 |
+
fire_count: Number of initial fire points ({FIRE_COUNT_RANGE_TEXT})
|
| 78 |
fire_intensity: Initial fire intensity (0.0-1.0)
|
| 79 |
+
building_count: Number of buildings to place ({BUILDING_COUNT_RANGE_TEXT})
|
| 80 |
+
max_units: Maximum number of deployable units ({MAX_UNITS_RANGE_TEXT})
|
| 81 |
"""
|
| 82 |
self.world = WorldState(
|
| 83 |
width=self.config.GRID_WIDTH,
|