Wildfire Containment Interface

wildfire_env

🌲 Wildfire Environment



Autonomous wildfire-control simulation for reinforcement-learning agents, built on the [OpenEnv](https://github.com/openenv) framework.
Agents must contain spreading fires using water, firebreaks, and timing strategies under changing wind and humidity conditions.

[![Docker](https://img.shields.io/badge/docker-ready-blue)](https://hub.docker.com/)
[![Python](https://img.shields.io/badge/python-3.10+-green)](https://www.python.org/)
[![FastAPI](https://img.shields.io/badge/backend-fastapi-teal)](https://fastapi.tiangolo.com/)
[![License](https://img.shields.io/badge/license-MIT-lightgrey)](LICENSE)

---

📋 Table of Contents



1. [Why Wildfire Simulation?](#-why-wildfire-simulation)
2. [Quick Start](#-quick-start)
3. [Environment Overview](#-environment-overview)
4. [Grid Format & Encoding](#-grid-format--encoding)
5. [Actions](#-actions)
6. [Observations](#-observations)
7. [Reward Structure](#-reward-structure)
8. [Fire Spread Mechanics](#-fire-spread-mechanics)
9. [Configuration](#-configuration)
10. [Installation & Usage](#-installation--usage)
11. [API Reference](#-api-reference)
12. [Examples](#-examples)
13. [Web Interface](#-web-interface)
14. [Troubleshooting](#-troubleshooting)
15. [References](#-references)

---

🔥 Why Wildfire Simulation?



Wildland fires are intensifying globally due to climate change — increasing the urgency for AI-assisted decision-making.
This environment explores how intelligent systems can control fire spread in real time, under limited resources.

Research Motivation


✅ Based on real wildfire science inspired by:
  • Rothermel Surface Fire Spread Model (USDA Forest Service)

  • MITRE Fireline's SimFire — physics-informed RL fire simulator

  • SimHarness — RL evaluation for disaster response


  • Application Goals


    | Research Theme | Role in This Environment |
    |---|---|
    | Resource-Constrained Planning | Finite water + firebreak budgets |
    | Fire Spread + Containment Strategy | Directional wind & moisture effects |
    | Disaster Response RL | Safety-focused reward design |
    | LLM Agents for Control Tasks | Text-based action decision making |

    This makes WildfireEnv a fast, controllable, and open benchmark for applied RL and LLM reasoning.

    ---

    🚀 Quick Start



    Using Docker (Recommended)



    Build base image (first time only)


    docker build -t openenv-base:latest -f src/core/containers/images/Dockerfile .

    Build wildfire environment


    docker build -t wildfire-env:latest -f src/envs/wildfire_env/server/Dockerfile .

    Run container


    docker run -p 8000:8000 -e ENABLE_WEB_INTERFACE=true wildfire-env:latest


    Note: The web interface can be enabled with ENABLE_WEB_INTERFACE=true. Access it at http://localhost:8000/web when enabled.

    Basic Python Client



    from envs.wildfire_env import WildfireEnv, WildfireAction

    Connect to running server


    env = WildfireEnv(base_url="http://localhost:8000")

    Reset environment


    result = env.reset()
    obs = result.observation
    print(f"Grid: {obs.width}x{obs.height}, Fires: {obs.burning_count}, Water: {obs.remaining_water}")

    Take action (water a burning cell)


    result = env.step(WildfireAction(action="water", x=10, y=15))
    print(f"Reward: {result.reward:.2f}, Burning: {result.observation.burning_count}")

    Create firebreak


    result = env.step(WildfireAction(action="break", x=12, y=15))

    Wait (fire spreads)


    result = env.step(WildfireAction(action="wait"))

    env.close()


    ---

    🔥 Environment Overview



    This environment models forest-fire dynamics influenced by:
  • Wind direction (8 directions + calm) - accelerates fire spread in wind direction

  • Humidity (0.0-1.0) - suppresses ignition probability

  • Fuel type and spread rate - vegetation burns and spreads to neighbors

  • Limited resources (water units, break materials) - strategic resource management

  • Time pressure (each step costs small reward penalty)


  • The goal is to minimize fire spread and total burned area while using resources efficiently.

    Episode Termination



    An episode ends when:
  • All fires are extinguished (burning_count == 0) - Success!

  • Maximum steps reached (step_count >= max_steps) - Time limit exceeded


  • ---

    🧱 Grid Format & Encoding



    Grid Structure



    The grid is returned as a flat 1D array in the observation. To access cell at position (x, y):

    index = y * width + x
    cell_value = observation.grid[index]


    Example: For a 32×32 grid, cell at (10, 15):
    index = 15 * 32 + 10  # = 490
    cell_value = observation.grid[490]


    Cell Encoding



    | Code | Meaning | Color (Visualization) | Behavior |
    |------|----------------|-----------------------|----------|
    | 0 | Ash (burned) | Black ⚫ | Burned out, cannot reignite |
    | 1 | Fuel | Green 🟩 | Healthy vegetation, can ignite |
    | 2 | Burning | Red 🔥 | Currently on fire, spreads to neighbors |
    | 3 | Firebreak | Brown 🟫 | Barrier, fire cannot cross |
    | 4 | Water/Damp | Blue 🔵 | Dampened, immune to ignition temporarily |

    Grid Visualization Example



    import numpy as np

    obs = env.reset().observation
    grid_2d = np.array(obs.grid).reshape(obs.height, obs.width)

    Now grid_2d[y][x] gives the cell value at position (x, y)


    print(grid_2d[15][10]) # Cell at x=10, y=15


    ---

    🎮 Actions



    Action Types



    #### 1. water - Apply Water
    Extinguishes burning cells and dampens fuel to prevent ignition.

    WildfireAction(action="water", x=10, y=15)


    Effects:
  • Burning cell (2): Extinguishes → becomes Water/Damp (4), gives +0.25 reward

  • Fuel cell (1): Dampens → becomes Water/Damp (4), gives -0.10 reward (preventive, slight penalty)

  • Water/Damp cell (4): Redundant watering, gives -0.05 reward

  • Ash/Break (0, 3): Wasteful, gives -0.05 reward


  • Resource Cost: 1 water unit per action
    Requires: remaining_water > 0 and valid coordinates

    Best Use: Extinguish active fires before they spread

    ---

    #### 2. break - Create Firebreak
    Builds a fire-resistant barrier that stops fire spread.

    WildfireAction(action="break", x=12, y=15)


    Effects:
  • Fuel/Water cell (1, 4): Creates firebreak → becomes Firebreak (3), gives +0.15 reward

  • Burning cell (2): Extinguishes → becomes Firebreak (3), gives -0.02 reward (less effective than water)

  • Firebreak (3): Redundant, gives -0.01 reward

  • Ash (0): Wasteful, gives -0.02 reward


  • Resource Cost: 1 firebreak material per action
    Requires: remaining_breaks > 0 and valid coordinates

    Best Use: Create barriers ahead of fire front to contain spread

    ---

    #### 3. wait - Do Nothing
    Let natural fire dynamics occur (fire spreads).

    WildfireAction(action="wait")


    Effects:
  • No resource cost

  • No coordinate required

  • Fire spreads naturally to neighboring cells

  • Small time penalty (-0.01 reward per step)


  • Best Use: When fire is contained, waiting for it to burn out

    ---

    Invalid Actions



    Actions that fail (give -0.05 reward):
  • Invalid coordinates (out of bounds)

  • Using water when remaining_water == 0

  • Using break when remaining_breaks == 0

  • Missing required coordinates for water/break actions


  • ---

    👁️ Observations



    WildfireObservation



    Returned after every reset() or step():

    @dataclass
    class WildfireObservation(Observation):
    grid: List[int] # Flat array: [1,1,2,1,...] length = width × height
    width: int # Grid width (default: 32)
    height: int # Grid height (default: 32)
    step: int # Current step number (0 at reset)
    wind_dir: str # "N", "NE", "E", "SE", "S", "SW", "W", "NW", "CALM"
    humidity: float # [0.0, 1.0] - higher = less fire spread
    burning_count: int # Number of cells currently on fire
    burned_count: int # Total number of ash cells (cumulative)
    remaining_water: int # Water units left
    remaining_breaks: int # Firebreak materials left
    reward_hint: float # Shaping reward (for debugging)
    done: bool # Episode ended?
    reward: float # Step reward


    Example Observation



    result = env.reset()
    obs = result.observation

    print(f"Step: {obs.step}") # 0
    print(f"Grid size: {obs.width}x{obs.height}") # 32x32
    print(f"Grid cells: {len(obs.grid)}") # 1024
    print(f"Active fires: {obs.burning_count}") # 2
    print(f"Wind: {obs.wind_dir}") # "NE"
    print(f"Humidity: {obs.humidity:.2f}") # 0.24
    print(f"Water left: {obs.remaining_water}") # 8
    print(f"Breaks left: {obs.remaining_breaks}") # 50


    ---

    💰 Reward Structure



    Step Rewards



    | Action | Condition | Reward |
    |--------|-----------|--------|
    | Water burning cell | Extinguishes fire | +0.25 |
    | Water fuel cell | Preventive dampening | -0.10 |
    | Create firebreak | From fuel/water | +0.15 |
    | Fire spreads | Each new burning cell | -0.15 per cell |
    | Fire shrinks | Each extinguished cell | +0.10 per cell |
    | New burned area | Each cell turns to ash | -0.05 per cell |
    | Time penalty | Every step | -0.01 |
    | Invalid action | Out of bounds, no resources | -0.05 |
    | Redundant action | Watering already damp cell | -0.05 |

    Episode End Bonuses



    When episode terminates (done == True):

  • Fire contained (burning_count == 0):

  • - +0.5 base bonus
    - +0.5 × saved_ratio bonus (proportion of cells not burned)

  • Fallback reward:

  • - +0.2 × (1.0 - burned_ratio) bonus

    Example: Perfect containment (no burned cells):
    Reward = +0.5 + 0.5 × 1.0 = +1.0


    Reward Interpretation



  • Positive rewards: Good containment actions, extinguishing fires

  • Negative rewards: Fire spread, resource waste, time penalty

  • Goal: Maximize cumulative reward = minimize fire damage


  • ---

    🌪️ Fire Spread Mechanics



    Spread Model



    Fire spreads using an 8-directional neighbor model:

    1. Burning cells persist for burn_lifetime = 3 ticks before turning to ash
    2. Each burning cell can ignite neighboring fuel cells (8 directions)
    3. Spread probability depends on:
    - Base ignition probability: 0.30 (30% chance)
    - Humidity factor: (1.0 - humidity) - higher humidity = less spread
    - Wind multiplier:
    - +2.0x in wind direction
    - +0.5x against wind
    - +1.0x perpendicular
    - Diagonal factor: 0.6x for diagonal neighbors (slower spread)

    4. Water/Damp cells (4) are immune to ignition while damp
    5. Firebreaks (3) cannot be crossed by fire
    6. Ash cells (0) cannot reignite

    Wind Effects



    | Wind Direction | Effect on Fire Spread |
    |----------------|----------------------|
    | In wind direction | 2× faster ignition probability |
    | Against wind | 0.5× slower ignition probability |
    | Perpendicular | Normal (1×) ignition probability |
    | CALM | No directional bias |

    Water Dampening Duration



    Watered cells (4) remain damp for 6 ticks before reverting to fuel (1).

    Example Fire Spread



    Step 0:     Step 1:     Step 2:
    🟩🟩🟩 🟩🟥🟩 🟫🟥🟫
    🟩🟥🟩 → 🟥🟥🟥 → 🟥🟥🟥 (Wind: E, spreading east)
    🟩🟩🟩 🟩🟥🟩 🟫🟥🟫


    ---

    ⚙️ Configuration



    Environment Variables



    Set these before starting the server:

    | Variable | Description | Default | Range |
    |-----------|-------------|---------|-------|
    | WILDFIRE_WIDTH | Grid width in cells | 32 | 8-128 |
    | WILDFIRE_HEIGHT | Grid height in cells | 32 | 8-128 |
    | WILDFIRE_HUMIDITY | Initial humidity level | 0.25 | 0.0-1.0 |
    | WILDFIRE_WIND | Wind direction (fixed) | Random | N, NE, E, SE, S, SW, W, NW, CALM |
    | WILDFIRE_SEED | Random seed | 3407 | Any integer |
    | WILDFIRE_MAX_STEPS | Max steps per episode | 128 | 10-1000 |
    | WILDFIRE_WATER_CAPACITY | Initial water units | 8 | 1-100 |
    | WILDFIRE_BREAK_CAPACITY | Initial firebreak materials | 50 | 1-200 |

    Python API Configuration



    from envs.wildfire_env.server.wildfire_environment import WildfireEnvironment

    env = WildfireEnvironment(
    width=64,
    height=64,
    humidity=0.3,
    init_sources=3, # Number of initial fires
    max_steps=200,
    water_capacity=10,
    break_capacity=75,
    seed=42
    )


    Docker Configuration



    docker run -p 8000:8000 \
    -e WILDFIRE_WIDTH=64 \
    -e WILDFIRE_HEIGHT=64 \
    -e WILDFIRE_HUMIDITY=0.4 \
    -e WILDFIRE_WIND=N \
    -e WILDFIRE_WATER_CAPACITY=12 \
    wildfire-env:latest


    Custom Configuration



    Build and run with custom configuration


    docker build -t openenv-base:latest -f src/core/containers/images/Dockerfile .
    docker build -t wildfire-env:latest -f src/envs/wildfire_env/server/Dockerfile .
    docker run -p 8000:8000 \
    -e ENABLE_WEB_INTERFACE=true \
    -e WILDFIRE_WIDTH=64 \
    -e WILDFIRE_HEIGHT=64 \
    -e WILDFIRE_HUMIDITY=0.5 \
    wildfire-env:latest


    ---

    🚀 Installation & Usage



    Option 1: Docker (Recommended)



    Manual setup:

    Build base image (first time only)


    docker build -t openenv-base:latest -f src/core/containers/images/Dockerfile .

    Build wildfire environment


    docker build -t wildfire-env:latest -f src/envs/wildfire_env/server/Dockerfile .

    Run container


    docker run -p 8000:8000 -e ENABLE_WEB_INTERFACE=true wildfire-env:latest


    This approach:
  • Builds the base image if needed

  • Rebuilds the wildfire image

  • Starts the container

  • Shows logs in real-time


  • Alternative: Using build_docker.sh script:

    Build base image (first time only)


    docker build -t openenv-base:latest -f src/core/containers/images/Dockerfile .

    Build wildfire environment using the script


    cd src/envs/wildfire_env/server
    ./build_docker.sh

    Run container


    docker run -d -p 8000:8000 --name wildfire-env-container wildfire-env:latest

    View logs


    docker logs -f wildfire-env-container

    Stop container


    docker stop wildfire-env-container

    Remove container


    docker rm wildfire-env-container


    Option 2: Local Development (No Docker)



    Requirements:
    pip install fastapi uvicorn numpy matplotlib requests


    Run server:

    From OpenEnv root directory


    python -m envs.wildfire_env.server.app


    Or with environment variables:
    WILDFIRE_WIDTH=64 WILDFIRE_HUMIDITY=0.3 python -m envs.wildfire_env.server.app


    ---

    📚 API Reference



    Client Class



    from envs.wildfire_env import WildfireEnv

    Connect to existing server


    env = WildfireEnv(base_url="http://localhost:8000")

    Or create from Docker image


    env = WildfireEnv.from_docker_image("wildfire-env:latest")


    Methods



    #### reset() -> StepResult[WildfireObservation]

    Resets the environment to initial state.

    result = env.reset()
    obs = result.observation
    print(f"New episode: {obs.step == 0}")


    #### step(action: WildfireAction) -> StepResult[WildfireObservation]

    Takes an action and returns new observation.

    action = WildfireAction(action="water", x=10, y=15)
    result = env.step(action)
    print(f"Reward: {result.reward}, Done: {result.done}")


    #### state -> WildfireState

    Access current environment state.

    state = env.state
    print(f"Episode ID: {state.episode_id}")
    print(f"Total burned: {state.total_burned}")
    print(f"Total extinguished: {state.total_extinguished}")


    #### close()

    Closes the connection (for HTTP clients, this is a no-op but good practice).

    env.close()


    Data Classes



    #### WildfireAction

    @dataclass
    class WildfireAction(Action):
    action: str # "water" | "break" | "wait"
    x: Optional[int] = None # Target X coordinate (required for water/break)
    y: Optional[int] = None # Target Y coordinate (required for water/break)


    Examples:
    WildfireAction(action="water", x=10, y=15)
    WildfireAction(action="break", x=12, y=15)
    WildfireAction(action="wait") # x, y not needed


    #### WildfireObservation

    See [Observations](#-observations) section for full details.

    #### WildfireState

    @dataclass
    class WildfireState(State):
    episode_id: str
    step_count: int
    total_burned: int
    total_extinguished: int
    last_action: str
    width: int
    height: int
    wind_dir: str
    humidity: float
    remaining_water: int
    remaining_breaks: int
    grid: List[int]
    burn_timers: List[int]


    ---

    📖 Examples



    Example 1: Simple Containment Strategy



    from envs.wildfire_env import WildfireEnv, WildfireAction
    import numpy as np

    env = WildfireEnv(base_url="http://localhost:8000")
    result = env.reset()
    obs = result.observation

    grid_2d = np.array(obs.grid).reshape(obs.height, obs.width)
    total_reward = 0

    while not result.done:
    # Find burning cells
    burning_indices = np.where(grid_2d == 2)

    if len(burning_indices[0]) > 0 and obs.remaining_water > 0:
    # Water the first burning cell
    y, x = burning_indices[0][0], burning_indices[1][0]
    action = WildfireAction(action="water", x=int(x), y=int(y))
    else:
    # Wait if no water or no fires
    action = WildfireAction(action="wait")

    result = env.step(action)
    obs = result.observation
    total_reward += result.reward or 0

    # Update grid
    grid_2d = np.array(obs.grid).reshape(obs.height, obs.width)

    print(f"Step {obs.step}: Burning={obs.burning_count}, Reward={result.reward:.3f}")

    print(f"\nEpisode ended. Total reward: {total_reward:.2f}")
    print(f"Final stats: Burned={obs.burned_count}, Extinguished={env.state.total_extinguished}")
    env.close()


    Example 2: Firebreak Strategy



    from envs.wildfire_env import WildfireEnv, WildfireAction
    import numpy as np

    env = WildfireEnv(base_url="http://localhost:8000")
    result = env.reset()
    obs = result.observation

    def create_firebreak_barrier(obs, env):
    """Create firebreak ahead of fire front based on wind direction."""
    grid_2d = np.array(obs.grid).reshape(obs.height, obs.width)
    wind = obs.wind_dir

    # Find burning cells
    burning_y, burning_x = np.where(grid_2d == 2)

    if len(burning_x) == 0 or obs.remaining_breaks == 0:
    return WildfireAction(action="wait")

    # Calculate fire front position
    if wind == "E":
    target_x = int(np.max(burning_x)) + 2 # Ahead of easternmost fire
    target_y = int(np.mean(burning_y))
    elif wind == "W":
    target_x = int(np.min(burning_x)) - 2
    target_y = int(np.mean(burning_y))
    elif wind == "N":
    target_x = int(np.mean(burning_x))
    target_y = int(np.min(burning_y)) - 2
    elif wind == "S":
    target_x = int(np.mean(burning_x))
    target_y = int(np.max(burning_y)) + 2
    else:
    # Fallback: water nearest burning cell
    return WildfireAction(action="water", x=int(burning_x[0]), y=int(burning_y[0]))

    # Ensure within bounds
    target_x = max(0, min(obs.width - 1, target_x))
    target_y = max(0, min(obs.height - 1, target_y))

    return WildfireAction(action="break", x=target_x, y=target_y)

    total_reward = 0
    while not result.done:
    action = create_firebreak_barrier(obs, env)
    result = env.step(action)
    obs = result.observation
    total_reward += result.reward or 0

    if obs.step % 10 == 0:
    print(f"Step {obs.step}: Fires={obs.burning_count}, Water={obs.remaining_water}, Breaks={obs.remaining_breaks}")

    env.close()


    Example 3: Visualization with Matplotlib



    import matplotlib.pyplot as plt
    import numpy as np
    import matplotlib.colors as mcolors
    from envs.wildfire_env import WildfireEnv, WildfireAction

    env = WildfireEnv(base_url="http://localhost:8000")
    result = env.reset()
    obs = result.observation

    Setup colormap


    cmap = mcolors.ListedColormap([
    "black", # 0 = ash
    "green", # 1 = fuel
    "red", # 2 = burning
    "saddlebrown", # 3 = firebreak
    "blue" # 4 = water
    ])
    norm = mcolors.BoundaryNorm([0, 1, 2, 3, 4, 5], cmap.N)

    fig, ax = plt.subplots(figsize=(8, 8))
    plt.ion()

    for step in range(50):
    if result.done:
    break

    # Render grid
    grid_2d = np.array(obs.grid).reshape(obs.height, obs.width)
    ax.clear()
    ax.imshow(grid_2d, cmap=cmap, norm=norm, interpolation='nearest')
    ax.set_title(
    f"Step {obs.step} | Fires: {obs.burning_count} | Burned: {obs.burned_count}\n"
    f"Wind: {obs.wind_dir} | Humidity: {obs.humidity:.2f} | "
    f"Water: {obs.remaining_water} | Breaks: {obs.remaining_breaks}"
    )
    plt.pause(0.1)

    # Take action (simple: water first burning cell)
    if obs.burning_count > 0 and obs.remaining_water > 0:
    burning_indices = np.where(grid_2d == 2)
    if len(burning_indices[0]) > 0:
    y, x = burning_indices[0][0], burning_indices[1][0]
    action = WildfireAction(action="water", x=int(x), y=int(y))
    else:
    action = WildfireAction(action="wait")
    else:
    action = WildfireAction(action="wait")

    result = env.step(action)
    obs = result.observation

    plt.ioff()
    plt.show()
    env.close()


    Example 4: Training Loop for RL



    from envs.wildfire_env import WildfireEnv, WildfireAction
    import random

    env = WildfireEnv(base_url="http://localhost:8000")

    num_episodes = 10
    episode_rewards = []

    for episode in range(num_episodes):
    result = env.reset()
    obs = result.observation
    episode_reward = 0
    episode_steps = 0

    while not result.done:
    # Random policy (replace with your RL agent)
    if random.random() < 0.4 and obs.remaining_water > 0:
    action = WildfireAction(
    action="water",
    x=random.randint(0, obs.width - 1),
    y=random.randint(0, obs.height - 1)
    )
    elif random.random() < 0.3 and obs.remaining_breaks > 0:
    action = WildfireAction(
    action="break",
    x=random.randint(0, obs.width - 1),
    y=random.randint(0, obs.height - 1)
    )
    else:
    action = WildfireAction(action="wait")

    result = env.step(action)
    obs = result.observation
    episode_reward += result.reward or 0
    episode_steps += 1

    episode_rewards.append(episode_reward)
    state = env.state
    print(
    f"Episode {episode + 1}: "
    f"Reward={episode_reward:.2f}, "
    f"Steps={episode_steps}, "
    f"Burned={state.total_burned}, "
    f"Extinguished={state.total_extinguished}"
    )

    print(f"\nAverage reward: {sum(episode_rewards) / len(episode_rewards):.2f}")
    env.close()


    ---

    🌐 Web Interface



    The Wildfire Environment includes a custom web interface with visual grid display and wildfire-specific features.

    Accessing the Web Interface



    #### Using Docker

    Build base image (first time only)


    docker build -t openenv-base:latest -f src/core/containers/images/Dockerfile .

    Build wildfire environment


    docker build -t wildfire-env:latest -f src/envs/wildfire_env/server/Dockerfile .

    Run container


    docker run -p 8000:8000 -e ENABLE_WEB_INTERFACE=true wildfire-env:latest


    Then open: http://localhost:8000/web

    #### Local Testing (No Docker)

    Enable web interface with flag


    ENABLE_WEB_INTERFACE=true PYTHONPATH=src uvicorn src.envs.wildfire_env.server.app:app --reload --host 0.0.0.0 --port 8000


    Web Interface Features



    #### Left Pane: Action Interface
  • Wildfire-specific action form

  • - Action dropdown: Water (Extinguish Fire), Break (Create Firebreak), Wait (Do Nothing)
    - Coordinate inputs (X, Y) - auto-populated when clicking grid cells
    - Coordinates show/hide based on action type
  • Environment stats display

  • - Step count
    - Water remaining
    - Breaks remaining
    - Burning cells count
  • Current state display

  • - Status (Reset/Running)
    - Episode ID
    - Wind direction
    - Humidity
  • Control buttons

  • - Reset Environment
    - Get State

    #### Right Pane: Visual Grid & Logs
  • Visual 2D Grid Display 🔥

  • - 16×16 grid rendered as color-coded cells
    - Color coding:
    - 🟩 Green = Fuel (safe, value 1)
    - 🔥 Orange/Red = Burning (fire, value 2)
    - ⬛ Dark Gray = Ash (burned, value 0)
    - 🟫 Brown = Firebreak (value 3)
    - 🟦 Blue = Watered/Damp (value 4)
    - Interactive: Click cells to set coordinates for water/break actions
    - Auto-updates: Grid refreshes automatically via WebSocket
  • Legend

  • - Color-coded legend explaining all cell types
  • Action history

  • - Log of all actions with timestamps
    - Shows action, observation, reward, and done status

    #### Additional Features
  • WebSocket connection - Real-time state updates without page refresh

  • Instructions panel - Collapsible environment documentation

  • Grid status indicator - Shows grid dimensions and cell count


  • Using the Web Interface



    1. Start the server (see above)
    2. Open browser to: http://localhost:8000/web
    3. Click "Reset Environment" to initialize and display the grid
    4. Interact with the grid:
    - Click on a cell to set coordinates for water/break actions
    - Or manually enter X, Y coordinates
    5. Select action:
    - Choose water, break, or wait from the dropdown
    6. Click "Execute Action"
    7. Watch the grid update in real-time:
    - Fire spreads automatically
    - Cells change color based on state
    - Stats update automatically
    8. Monitor resources in the stats panel (water, breaks, burning count)

    ---

    🔧 Troubleshooting



    Common Issues



    #### 1. Connection Errors

    Problem: ConnectionRefusedError or Cannot connect to server

    Solutions:
  • Verify server is running: curl http://localhost:8000/health

  • Check Docker container: docker ps | grep wildfire

  • Ensure port 8000 is not in use: lsof -i :8000


  • #### 2. Index Errors

    Problem: IndexError: list index out of range

    Solution: Ensure coordinates are within bounds:

    Always check bounds before accessing


    if 0 <= x < obs.width and 0 <= y < obs.height:
    action = WildfireAction(action="water", x=x, y=y)


    #### 3. Invalid Action Warnings

    Problem: Actions returning -0.05 reward repeatedly

    Solutions:
  • Check remaining_water and remaining_breaks before using resources

  • Verify coordinates are integers and within grid bounds

  • Use action="wait" when resources are exhausted


  • #### 4. Grid Format Confusion

    Problem: How to access grid cells?

    Solution:

    Convert flat array to 2D


    grid_2d = np.array(obs.grid).reshape(obs.height, obs.width)

    Access cell at (x, y)


    cell_value = grid_2d[y][x]

    Or use flat index


    index = y * obs.width + x
    cell_value = obs.grid[index]


    #### 5. Docker Build Failures

    Problem: failed to solve: openenv-base:latest

    Solution:

    Build base image first


    docker build -t openenv-base:latest -f src/core/containers/images/Dockerfile .

    Then build wildfire image


    docker build -t wildfire-env:latest -f src/envs/wildfire_env/server/Dockerfile .


    Debugging Tips



    1. Enable verbose logging:
       docker logs -f wildfire-env-container
    ``

    2. Check environment state:
    `python
    state = env.state
    print(f"State: {state}")
    `

    3. Validate actions:
    `python
    obs = env.reset().observation
    print(f"Bounds: 0 <= x < {obs.width}, 0 <= y < {obs.height}")
    print(f"Resources: Water={obs.remaining_water}, Breaks={obs.remaining_breaks}")
    `

    4. Monitor grid changes:
    `python
    prev_grid = obs.grid.copy()
    result = env.step(action)
    new_grid = result.observation.grid
    changes = [i for i, (a, b) in enumerate(zip(prev_grid, new_grid)) if a != b]
    print(f"Changed cells: {len(changes)}")
    `

    ---

    📊 Performance Considerations



    Grid Size Impact



  • Small grids (16×16): Fast, good for quick testing

  • Medium grids (32×32): Default, balanced performance

  • Large grids (64×64+): Slower, more realistic but requires more compute


  • Resource Limits



  • Low water (4-8): Forces strategic decisions

  • High water (20+): More forgiving, easier to succeed

  • Low breaks (25): Emphasizes firebreak placement strategy

  • High breaks (100+): More freedom, less constraint


  • Episode Length



  • Short episodes (50 steps): Fast iteration, good for debugging

  • Medium episodes (128 steps): Default, balanced

  • Long episodes (200+ steps): Better for complex strategies


  • ---

    🧭 References



    Papers & Research



  • Rothermel Model: [USDA Forest Service - Surface Fire Spread Model](https://www.fs.fed.us/rm/pubs_series/rmrs/gtr/rmrs_gtr371.pdf)

  • SimFire: [MITRE Fireline Project](https://github.com/mitrefireline/simfire)

  • RL for Wildfires: [arXiv:2311.15925](https://arxiv.org/abs/2311.15925)


  • OpenEnv Framework



  • Main Repository: [OpenEnv GitHub](https://github.com/openenv)

  • Documentation: See rfcs/ directory for design documents

  • Other Environments: See src/envs/ for more environment examples


  • Related Tools



  • FastAPI: [FastAPI Documentation](https://fastapi.tiangolo.com/)

  • Reinforcement Learning: [Spinning Up in Deep RL](https://spinningup.openai.com/)

  • Docker: [Docker Documentation](https://docs.docker.com/)


---

📝 License



This environment is part of the OpenEnv project. See the main LICENSE file for details.

---

🤝 Contributing



Contributions welcome! Please see
CONTRIBUTING.md in the main OpenEnv repository.

---

🔖 Citations


bibtex
@techreport{rothermel2022surface,
title = {The Rothermel Surface Fire Spread Model and Associated Developments},
author = {Andrews, Patricia L. and Rothermel, Richard C.},
year = {2022},
institution = {USDA Forest Service},
number = {RMRS-GTR-371},
url = {https://www.fs.usda.gov/rm/pubs_series/rmrs/gtr/rmrs_gtr371.pdf}
}

@article{tapley2023reinforcement,
title = {Reinforcement Learning for Wildfire Mitigation in Simulated Disaster Environments},
author = {Tapley, A. and Dotter, M. and Doyle, M. and others},
journal = {arXiv preprint arXiv:2311.15925},
year = {2023},
url = {https://arxiv.org/abs/2311.15925}
}

@misc{mitrefireline2023simfire,
author = {{MITRE Fireline Project}},
title = {SimFire: Wildfire Simulator for Decision-Support and AI Research},
year = {2023},
howpublished = {\url{https://github.com/mitrefireline/simfire}}
}

@misc{wildfire-openenv-2025,
title = {Wildfire Environment for OpenEnv: Containment-Focused RL Simulation},
author = {OpenEnv Contributors},
year = {2025},
url = {https://github.com/openenv/openenv}
}
``

---

Happy firefighting! 🔥🚒

Take Action

Water: Extinguishes fire at target cell
Break: Creates firebreak to prevent spread
Wait: Fire continues spreading

Environment Stats

Step Count 0
Water Remaining 0
Breaks Remaining 0
Burning Cells 0

Current State

Status: Not initialized
Episode ID: -
Wind Direction: -
Humidity: -
Fire Grid Visualization

Legend

Ash (Burned)
Fuel (Safe)
Burning (Fire)
Firebreak
Watered (Damp)

Fire Grid

Waiting for grid data... (Click "Reset Environment" to initialize)

Click on a cell to set coordinates for water/break actions

Action History

No actions taken yet