sbs

    Deterministic indoor evacuation plan validation and drill simulation engine

    evacuation
    simulation
    graph
    safety
    svg
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    35

    No dependencies

    #MoonEvac(疏途)

    CI License MoonBit

    MoonEvac 是一个以 MoonBit 实现的确定性室内疏散方案验证与演练仿真引擎。它把房间、走廊、楼梯、出口、人员和危险源建模为带容量的有向图,检查出口可达性和单点故障,并在通道容量、人员差异、危险传播及临时封路条件下执行可复现仿真。

    MoonEvac 用于教学、演练方案预检和算法研究,不是经过认证的消防工程软件,也不能代替现场应急指挥或专业安全评估。

    #功能

    • 建筑拓扑:房间、走廊、楼梯、集合点、出口和有向/双向通道;
    • 方案验证:重复标识、悬空引用、非法容量、不可达出口和出口冗余;
    • 动态寻路:综合距离、风险、拥堵、楼梯和无障碍约束;
    • 危险传播:危险源增长、门/走廊/楼梯差异化扩散、被动衰减;
    • 场景事件:点燃危险源、关闭通道、重新开放通道和清除风险;
    • 容量仿真:响应延迟、通道吞吐、队列、群组拆分和动态重路由;
    • 韧性分析:出口使用平衡、关键节点、通道压力和场景排名;
    • 报告输出:Markdown、JSON、CSV、建筑 SVG 和时间线 SVG;
    • 内置示例:两出口教室、两层校园、实验室火灾和主出口关闭;
    • 39 项自动测试以及 GitHub Actions 持续集成。

    #快速开始

    要求安装 2026 年 8 月或更新版本的 MoonBit 工具链。

    git clone https://github.com/17731189258/sbs.git cd sbs moon check moon test moon run cmd/main

    moon run cmd/main 会运行内置的两层校园火灾场景,并在终端输出 Markdown 报告。报告包含总人数、已疏散人数、滞留人数、最大风险、各群组离场时间和仿真时间线。

    #作为 MoonBit 包使用

    发布至 Mooncakes 后,可添加依赖:

    moon add 17731189258/sbs

    在 moon.pkg 中导入:

    import {
    "17731189258/sbs" @evac,
    }

    运行一个确定性仿真:

    fn main {
    let plan = @evac.school_fire_scenario()
    let findings = @evac.validate_plan(plan)
    if @evac.validation_ok(findings) {
    let result = @evac.run_simulation(
    plan,
    @evac.SimulationConfig::default(),
    )
    println(@evac.simulation_markdown(result))
    } else {
    println(@evac.validation_markdown(plan, findings))
    }
    }

    #建模接口

    最小方案由 Place、Passage 和 PopulationGroup 组成:

    let plan = @evac.EvacuationPlan::new("Training room")
    plan.places.push(@evac.Place::new(
    "room", "Training Room", @evac.Room, 1, 80.0, 80.0, 40,
    ))
    plan.places.push(@evac.Place::new(
    "exit", "Main Exit", @evac.Exit, 1, 240.0, 80.0, 200,
    ))
    plan.passages.push(@evac.Passage::new(
    "door", "room", "exit", @evac.Door, 12.0, 1.6,
    ))
    plan.populations.push(@evac.PopulationGroup::new(
    "visitors", "room", 30, @evac.Standard,
    ))

    事件使用整数时间步:

    plan.events.push(@evac.Ignite("room", 5, 0.55))
    plan.events.push(@evac.ClosePassage("door", 20))
    plan.events.push(@evac.OpenPassage("door", 60))

    #算法说明

    1. 验证器首先检查模型内部一致性和到出口的图可达性;
    2. 路由器使用确定性 Dijkstra 搜索,边权包含距离、风险、拥堵和无障碍代价;
    3. 危险模型按时间步沿启用通道传播,并对不同通道类型应用不同传递系数;
    4. 疏散引擎将人口表示为可拆分 cohort,按通道吞吐量逐步移动;
    5. 通道关闭、危险阈值或定期重算会触发动态寻路;
    6. 所有遍历和并列处理采用稳定输入顺序,同一输入得到相同输出。

    详细设计参见 docs/architecture.md。

    #项目结构

    model.mbt 领域模型和公共构造函数 validation.mbt 数据及图连通性验证 routing.mbt 风险感知寻路和瓶颈分析 hazard.mbt 危险传播与场景事件 simulation.mbt 容量受限疏散状态机 analytics.mbt 韧性指标和场景比较 report.mbt Markdown/JSON/CSV/SVG 输出 examples.mbt 可运行的内置建筑与场景 core_test.mbt 黑盒行为测试 cmd/main/ 命令行示例

    #测试与质量检查

    moon fmt --check moon check --deny-warn moon test moon run cmd/main

    测试覆盖模型验证、最短路、无障碍路径、危险事件、传播预览、通道状态、完整疏散、无效方案拒绝、JSON/XML 转义、SVG 输出、统计量和场景排名。

    #限制与功能边界

    • 当前使用图级而不是连续空间或 CFD 烟气模型;
    • 人群行为采用确定性 cohort 模型,不模拟恐慌或个体心理;
    • 几何坐标仅用于报告布局,不进行碰撞检测;
    • 所有参数都需要根据实际场所和专业指导进行校准;
    • 示例中的结果不能作为法规合规结论。

    #开源与来源

    项目为原创实现,未移植第三方项目代码。图搜索、离散时间仿真和描述统计采用公开的通用算法思想,由本项目独立实现。运行时只依赖 MoonBit 核心库。项目采用 Apache-2.0 许可证。

    #维护计划

    • 增加稳定的文本/JSON 方案解析器;
    • 增加多场景批处理和属性测试;
    • 引入更细粒度的通道旅行时间;
    • 提供浏览器端 WebAssembly 交互可视化;
    • 根据公开疏散数据增加参数校准示例。

    Cohort

    A movable cohort. Cohorts split when passage capacity is insufficient.

    Diagnostic

    pub struct Diagnostic {
    code : String
    severity : Severity
    subject : String
    message : String
    hint : String
    } derive(Eq,
    Debug
    )

    A diagnostic produced while validating a plan.

    DistributionStats

    pub struct DistributionStats {
    count : Int
    minimum : Double
    maximum : Double
    mean : Double
    median : Double
    percentile_90 : Double
    } derive(Eq,
    Debug
    )

    Descriptive statistics used by reports and scenario comparisons.

    EdgeCost

    pub struct EdgeCost {
    passage_index : Int
    from_index : Int
    to_index : Int
    cost : Double
    distance_m : Double
    risk : Double
    congestion : Double
    } derive(Eq,
    Debug
    )

    One passage with a computed dynamic cost.

    EvacuationPlan

    pub struct EvacuationPlan {
    name : String
    timestep_seconds : Double
    places : Array[Place]
    passages : Array[Passage]
    populations : Array[PopulationGroup]
    events : Array[ScenarioEvent]
    } derive(Eq,
    Debug
    )

    Complete immutable input to validation and simulation.

    EvacuationPlan::exit_indices

    fn EvacuationPlan::exit_indices(self : EvacuationPlan) -> Array[Int]

    Return all exit indices.

    EvacuationPlan::new

    fn EvacuationPlan::new(name : String) -> EvacuationPlan

    Construct an empty plan.

    EvacuationPlan::passage_index

    fn EvacuationPlan::passage_index(self : EvacuationPlan, id : String) -> Int

    Find a passage index using a deterministic linear scan.

    EvacuationPlan::place_index

    fn EvacuationPlan::place_index(self : EvacuationPlan, id : String) -> Int

    Find a place index using a deterministic linear scan.

    EvacuationPlan::place_kind_count

    fn EvacuationPlan::place_kind_count(self : EvacuationPlan, kind : PlaceKind) -> Int

    Number of locations of a given type.

    EvacuationPlan::population_count

    fn EvacuationPlan::population_count(self : EvacuationPlan) -> Int

    Total number of people in the plan.

    ExitUsage

    pub struct ExitUsage {
    exit_id : String
    assigned_groups : Int
    assigned_people : Int
    share : Double
    } derive(Eq,
    Debug
    )

    Static planned use of one exit.

    GroupOutcome

    pub struct GroupOutcome {
    group_id : String
    initial_count : Int
    evacuated_count : Int
    remaining_count : Int
    first_exit_step : Int
    last_exit_step : Int
    accumulated_exposure : Double
    reroutes : Int
    } derive(Eq,
    Debug
    )

    Final group-level metrics.

    HazardConfig

    pub struct HazardConfig {
    spread_rate : Double
    source_growth : Double
    passive_decay : Double
    door_transfer : Double
    hallway_transfer : Double
    stair_transfer : Double
    ramp_transfer : Double
    outdoor_transfer : Double
    floor_penalty : Double
    block_threshold : Double
    } derive(Eq,
    Debug
    )

    Tunable parameters for the graph-based hazard model.

    HazardConfig::aggressive

    fn HazardConfig::aggressive() -> HazardConfig

    A fast spread stress-test profile.

    HazardConfig::default

    fn HazardConfig::default() -> HazardConfig

    Conservative defaults for a teaching simulation.

    HazardConfig::slow

    A slower spread profile useful for drills.

    LiveEdge

    type LiveEdge derive(Eq,
    Debug
    )

    A state-aware edge used internally by the simulation router.

    Mobility

    pub(all) enum Mobility {
    Fast
    Standard
    Slow
    Assisted
    } derive(Eq,
    Debug
    )

    Mobility profile of an evacuee group.

    Mobility::code

    fn Mobility::code(self : Mobility) -> String

    Stable textual name used in reports.

    Mobility::requires_accessible

    fn Mobility::requires_accessible(self : Mobility) -> Bool

    Whether a mobility profile requires accessible passages.

    Mobility::speed_factor

    fn Mobility::speed_factor(self : Mobility) -> Double

    Return the speed multiplier of a mobility profile.

    Passage

    pub struct Passage {
    id : String
    from_id : String
    to_id : String
    kind : PassageKind
    length_m : Double
    width_m : Double
    flow_per_second : Double
    bidirectional : Bool
    accessible : Bool
    enabled : Bool
    } derive(Eq,
    Debug
    )

    A directed graph edge used by routing and simulation.

    Passage::new

    fn Passage::new(id : String, from_id : String, to_id : String, kind : PassageKind, length_m : Double, width_m : Double) -> Passage

    Construct a directed passage with practical defaults.

    PassageKind

    pub(all) enum PassageKind {
    Door
    Hallway
    Stairway
    Ramp
    Outdoor
    } derive(Eq,
    Debug
    )

    A directed connection category.

    PassageKind::code

    fn PassageKind::code(self : PassageKind) -> String

    Stable textual name used in machine-readable reports.

    PassagePressure

    pub struct PassagePressure {
    passage_id : String
    assigned_people : Int
    estimated_seconds : Double
    utilization : Double
    critical : Bool
    } derive(Eq,
    Debug
    )

    Capacity and route pressure summary for one passage.

    Place

    pub struct Place {
    id : String
    label : String
    kind : PlaceKind
    floor : Int
    x : Double
    y : Double
    capacity : Int
    initial_risk : Double
    } derive(Eq,
    Debug
    )

    A location is a graph vertex with geometry and holding capacity.

    Place::new

    fn Place::new(id : String, label : String, kind : PlaceKind, floor : Int, x : Double, y : Double, capacity : Int) -> Place

    Construct a place with explicit properties.

    PlaceKind

    pub(all) enum PlaceKind {
    Room
    Corridor
    Stair
    Assembly
    Exit
    } derive(Eq,
    Debug
    )

    A semantic category for a location in an evacuation map.

    PlaceKind::code

    fn PlaceKind::code(self : PlaceKind) -> String

    Stable textual name used in machine-readable reports.

    Point

    pub struct Point {
    x : Double
    y : Double
    } derive(Eq,
    Debug
    )

    A compact point used by SVG rendering.

    PopulationGroup

    pub struct PopulationGroup {
    id : String
    place_id : String
    count : Int
    mobility : Mobility
    response_delay : Int
    familiarity : Double
    } derive(Eq,
    Debug
    )

    Population initially located at one place.

    PopulationGroup::new

    fn PopulationGroup::new(id : String, place_id : String, count : Int, mobility : Mobility) -> PopulationGroup

    Construct a population group.

    ResilienceFinding

    pub struct ResilienceFinding {
    code : String
    severity : Severity
    subject : String
    value : Double
    message : String
    } derive(Eq,
    Debug
    )

    Resilience observation derived from topology and planned routes.

    RiskBand

    pub(all) enum RiskBand {
    Clear
    Caution
    Dangerous
    Critical
    } derive(Eq,
    Debug
    )

    Risk classification for reporting and routing policy.

    RiskBand::code

    fn RiskBand::code(self : RiskBand) -> String

    Stable code for a risk band.

    RiskBand::color

    fn RiskBand::color(self : RiskBand) -> String

    Display color used by the SVG renderer.

    RiskObservation

    pub struct RiskObservation {
    step : Int
    clear_places : Int
    caution_places : Int
    dangerous_places : Int
    critical_places : Int
    maximum_risk : Double
    mean_risk : Double
    } derive(Eq,
    Debug
    )

    Aggregate risk observation for one time step.

    Route

    pub struct Route {
    place_indices : Array[Int]
    passage_indices : Array[Int]
    total_cost : Double
    distance_m : Double
    maximum_risk : Double
    reachable : Bool
    } derive(Eq,
    Debug
    )

    A selected route through the evacuation graph.

    Route::exit_id

    fn Route::exit_id(self : Route, plan : EvacuationPlan) -> String

    Return the destination exit id of a route.

    Route::free_flow_seconds

    fn Route::free_flow_seconds(self : Route, mobility : Mobility) -> Double

    Estimate travel time before queues and response delay.

    Route::no_route

    fn Route::no_route() -> Route

    Create an explicit unreachable route value.

    Route::place_ids

    fn Route::place_ids(self : Route, plan : EvacuationPlan) -> Array[String]

    Produce the route as place identifiers.

    Route::summary

    fn Route::summary(self : Route, plan : EvacuationPlan) -> String

    Describe a route in a compact stable form.

    RouteOptions

    pub struct RouteOptions {
    distance_weight : Double
    risk_weight : Double
    congestion_weight : Double
    stair_penalty : Double
    require_accessible : Bool
    maximum_risk : Double
    } derive(Eq,
    Debug
    )

    Parameters controlling deterministic route selection.

    RouteOptions::accessible

    fn RouteOptions::accessible() -> RouteOptions

    Create routing options for an assisted mobility group.

    RouteOptions::balanced

    fn RouteOptions::balanced() -> RouteOptions

    Create balanced routing options.

    RouteOptions::shortest

    fn RouteOptions::shortest() -> RouteOptions

    Create options that strongly prefer the shortest geometric route.

    ScenarioDelta

    pub struct ScenarioDelta {
    baseline_name : String
    candidate_name : String
    evacuation_step_delta : Int
    remaining_delta : Int
    peak_stalled_delta : Int
    peak_risk_delta : Double
    score_delta : Double
    improved : Bool
    } derive(Eq,
    Debug
    )

    Pairwise comparison of two simulation results.

    ScenarioEvent

    pub(all) enum ScenarioEvent {
    ClosePassage(String, Int)
    OpenPassage(String, Int)
    Ignite(String, Int, Double)
    ClearRisk(String, Int)
    } derive(Eq,
    Debug
    )

    A time-based change applied to the simulation.

    ScenarioRanking

    pub struct ScenarioRanking {
    name : String
    score : Double
    completed : Bool
    rank : Int
    } derive(Eq,
    Debug
    )

    Ranked scenario entry.

    ScenarioState

    pub struct ScenarioState {
    step : Int
    risks : Array[Double]
    active_sources : Array[Double]
    passage_enabled : Array[Bool]
    event_log : Array[String]
    } derive(Eq,
    Debug
    )

    Mutable scenario state at a specific simulation step.

    ScenarioState::copy

    Copy scenario state so scenario branches are independent.

    ScenarioState::from_plan

    fn ScenarioState::from_plan(plan : EvacuationPlan) -> ScenarioState

    Initialize scenario state from plan defaults.

    Severity

    pub(all) enum Severity {
    Info
    Warning
    Error
    } derive(Eq,
    Debug
    )

    Severity used by validation diagnostics.

    Severity::code

    fn Severity::code(self : Severity) -> String

    Stable textual name used in reports.

    SimulationConfig

    pub struct SimulationConfig {
    maximum_steps : Int
    reroute_interval : Int
    snapshot_interval : Int
    maximum_route_risk : Double
    stop_when_evacuated : Bool
    hazard : HazardConfig
    } derive(Eq,
    Debug
    )

    Settings controlling a complete evacuation run.

    SimulationConfig::default

    Practical deterministic defaults.

    SimulationConfig::short_run

    Short configuration useful for tests.

    SimulationResult

    pub struct SimulationResult {
    plan_name : String
    completed : Bool
    steps : Int
    total_people : Int
    evacuated_people : Int
    remaining_people : Int
    peak_stalled_people : Int
    peak_risk : Double
    mean_evacuation_step : Double
    snapshots : Array[SimulationSnapshot]
    group_outcomes : Array[GroupOutcome]
    passage_peak_queues : Array[Int]
    event_log : Array[String]
    } derive(Eq,
    Debug
    )

    Complete deterministic result.

    SimulationSnapshot

    pub struct SimulationSnapshot {
    step : Int
    remaining : Int
    evacuated : Int
    stalled : Int
    maximum_risk : Double
    mean_risk : Double
    passage_queues : Array[Int]
    place_occupancy : Array[Int]
    } derive(Eq,
    Debug
    )

    Aggregate state captured for reports and regression tests.

    accumulated_exposure

    fn accumulated_exposure(samples : Array[Double], timestep : Double) -> Double

    Integrate risk exposure over a series of risk samples.

    advance_hazard

    fn advance_hazard(plan : EvacuationPlan, state : ScenarioState, config : HazardConfig) -> Unit

    Advance graph risk by one deterministic step.

    analyze_exit_usage

    fn analyze_exit_usage(plan : EvacuationPlan) -> Array[ExitUsage]

    Compute planned exit usage before dynamic events occur.

    analyze_passage_pressure

    fn analyze_passage_pressure(plan : EvacuationPlan, routes : Array[Route], critical_seconds : Double) -> Array[PassagePressure]

    Estimate pressure on each passage from planned routes.

    apply_due_events

    fn apply_due_events(plan : EvacuationPlan, state : ScenarioState) -> Unit

    Apply all events due at the state's current step.

    compact_demo_plan

    fn compact_demo_plan() -> EvacuationPlan

    A compact two-exit classroom useful for unit tests and tutorials.

    compare_results

    fn compare_results(left_name : String, left : SimulationResult, right_name : String, right : SimulationResult) -> String

    Return the name of the better scenario, or "tie".

    critical_places

    fn critical_places(plan : EvacuationPlan) -> Array[String]

    Find places whose loss disconnects at least one occupied origin from all exits.

    diagnostic

    fn diagnostic(code : String, severity : Severity, subject : String, message : String, hint : String) -> Diagnostic

    Create a diagnostic value.

    diagnostic_count

    fn diagnostic_count(diagnostics : Array[Diagnostic], severity : Severity) -> Int

    Count diagnostics of one severity.

    distribution_stats

    fn distribution_stats(values : Array[Double]) -> DistributionStats

    Compute descriptive statistics for an array.

    escape_json

    fn escape_json(text : String) -> String

    Escape arbitrary text for JSON string values.

    escape_xml

    fn escape_xml(text : String) -> String

    Escape arbitrary text for XML and SVG text nodes.

    evacuation_progress_at

    fn evacuation_progress_at(result : SimulationResult, step : Int) -> Double

    Estimate evacuation progress at an arbitrary step using the last observation.

    evacuation_threshold_step

    fn evacuation_threshold_step(result : SimulationResult, fraction : Double) -> Int

    Find the first snapshot meeting an evacuation percentage.

    exit_use_balance

    fn exit_use_balance(usages : Array[ExitUsage]) -> Double

    Shannon entropy of exit use, normalized to 0..1.

    hazard_preview

    fn hazard_preview(plan : EvacuationPlan, initial : ScenarioState, config : HazardConfig, steps : Int) -> Array[RiskObservation]

    Run a hazard-only preview and collect observations.

    integer_distribution_stats

    fn integer_distribution_stats(values : Array[Int]) -> DistributionStats

    Convert integer observations to descriptive statistics.

    invalid_demo_plan

    fn invalid_demo_plan() -> EvacuationPlan

    Create a deliberately invalid example for validation demonstrations.

    normalized_passage_congestion

    fn normalized_passage_congestion(plan : EvacuationPlan, queued : Array[Int]) -> Array[Double]

    Produce congestion input compatible with the routing engine.

    observe_risk

    fn observe_risk(state : ScenarioState) -> RiskObservation

    Observe current risk distribution.

    outgoing_edges

    fn outgoing_edges(plan : EvacuationPlan, place_index : Int, risks : Array[Double], congestion : Array[Double], options : RouteOptions) -> Array[EdgeCost]

    Return every outgoing edge from a place under current conditions.

    percentile

    fn percentile(values : Array[Double], probability : Double) -> Double

    Calculate a linearly interpolated percentile from numeric values.

    place_degrees

    fn place_degrees(plan : EvacuationPlan) -> Array[Int]

    Compute simple undirected degree for every place.

    plan_svg

    fn plan_svg(plan : EvacuationPlan, risks : Array[Double]) -> String

    Render the plan and an optional risk state as a standalone SVG document.

    planned_group_routes

    fn planned_group_routes(plan : EvacuationPlan, risks : Array[Double], congestion : Array[Double]) -> Array[Route]

    Assign every population group to its preferred route.

    planned_passage_loads

    fn planned_passage_loads(plan : EvacuationPlan, routes : Array[Route]) -> Array[Int]

    Count planned people using each passage.

    rank_scenarios

    fn rank_scenarios(names : Array[String], results : Array[SimulationResult]) -> Array[ScenarioRanking]

    Rank named scenario results using the stable result score.

    resilience_findings

    fn resilience_findings(plan : EvacuationPlan) -> Array[ResilienceFinding]

    Perform static resilience checks that complement validation.

    resilience_markdown

    fn resilience_markdown(plan : EvacuationPlan) -> String

    Create a concise Markdown resilience section.

    result_score

    fn result_score(result : SimulationResult) -> Double

    Compare two results with a stable lower-is-better score.

    risk_band

    fn risk_band(value : Double) -> RiskBand

    Convert numeric risk to a stable reporting band.

    risky_place_ids

    fn risky_place_ids(plan : EvacuationPlan, state : ScenarioState, threshold : Double) -> Array[String]

    Return ids currently above a chosen risk threshold.

    route_to_nearest_exit

    fn route_to_nearest_exit(plan : EvacuationPlan, start : Int, risks : Array[Double], congestion : Array[Double], options : RouteOptions) -> Route

    Find the lowest-cost reachable exit without mutating the plan.

    route_to_place

    fn route_to_place(plan : EvacuationPlan, start : Int, target : Int, risks : Array[Double], congestion : Array[Double], options : RouteOptions) -> Route

    Find the lowest-cost route from a place to a particular target.

    run_simulation

    fn run_simulation(plan : EvacuationPlan, config : SimulationConfig) -> SimulationResult

    Run a complete deterministic evacuation simulation.

    scenario_delta

    fn scenario_delta(baseline_name : String, baseline : SimulationResult, candidate_name : String, candidate : SimulationResult) -> ScenarioDelta

    Compare a candidate result against a named baseline.

    school_demo_plan

    fn school_demo_plan() -> EvacuationPlan

    Build a realistic two-floor school wing with redundant exits.

    school_exit_closure_scenario

    fn school_exit_closure_scenario() -> EvacuationPlan

    Add an early main-exit closure without a hazard source.

    school_fire_scenario

    fn school_fire_scenario() -> EvacuationPlan

    Add a delayed laboratory fire to the school example.

    simulation_groups_csv

    fn simulation_groups_csv(result : SimulationResult) -> String

    Render group outcomes as CSV.

    simulation_json

    fn simulation_json(result : SimulationResult) -> String

    Render a simulation result as stable JSON.

    simulation_markdown

    fn simulation_markdown(result : SimulationResult) -> String

    Render a full simulation result as Markdown.

    simulation_timeline_csv

    fn simulation_timeline_csv(result : SimulationResult) -> String

    Render timeline data as CSV for spreadsheet analysis.

    snapshot_people_in_risky_places

    fn snapshot_people_in_risky_places(snapshot : SimulationSnapshot, risks : Array[Double], threshold : Double) -> Int

    Count total people at risk at or above a threshold in one snapshot.

    timeline_svg

    fn timeline_svg(result : SimulationResult) -> String

    Render remaining and evacuated population as an SVG timeline.

    total_queue_timeline

    fn total_queue_timeline(result : SimulationResult) -> Array[Int]

    Collect snapshot queue totals.

    update_hazard_blocks

    fn update_hazard_blocks(plan : EvacuationPlan, state : ScenarioState, config : HazardConfig) -> Unit

    Disable passages touching places above the configured block threshold.

    validate_plan

    fn validate_plan(plan : EvacuationPlan) -> Array[Diagnostic]

    Validate a complete plan and return every deterministic diagnostic.

    validation_json

    fn validation_json(plan : EvacuationPlan, diagnostics : Array[Diagnostic]) -> String

    Render diagnostics as stable JSON without external dependencies.

    validation_markdown

    fn validation_markdown(plan : EvacuationPlan, diagnostics : Array[Diagnostic]) -> String

    Render validation diagnostics as a compact Markdown document.

    validation_ok

    fn validation_ok(diagnostics : Array[Diagnostic]) -> Bool

    Whether validation found no errors.

    worst_queue_timeline

    fn worst_queue_timeline(result : SimulationResult) -> Array[Int]

    Collect maximum passage queue at every snapshot.