Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/screens/BalanceAnalyserModel.res
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type levelSummary = {
levelWidth: int,
difficultyScore: float,
guardSpawnRate: float,
meanPatrolRadius: float,
defenceDensity: float,
alertThreshold: float,
}
Expand Down Expand Up @@ -147,6 +148,7 @@ let parseLevelSummary = (obj: Dict.t<JSON.t>): levelSummary => {
levelWidth: getInt(obj, "level_width"),
difficultyScore: getFloat(obj, "difficulty_score"),
guardSpawnRate: getFloat(obj, "guard_spawn_rate"),
meanPatrolRadius: getFloat(obj, "mean_patrol_radius"),
defenceDensity: getFloat(obj, "defence_density"),
alertThreshold: getFloat(obj, "alert_threshold"),
}
Expand Down
12 changes: 10 additions & 2 deletions tools/balance-analyser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct LevelStats
level_name::String
guard_count::Int
guard_spawn_rate::Float64 # guards per 1000 pixels of level width
mean_patrol_radius::Float64 # mean patrolRadius across all guards (pixels)
alert_threshold::Float64 # normalised 0.0-1.0 (higher = harder to trigger)
difficulty_score::Float64 # composite difficulty metric
device_count::Int # total devices with custom defences
Expand Down Expand Up @@ -285,6 +286,11 @@ function level_dict_to_stats(d::Dict{String,Any})::LevelStats
# Guard spawn rate: guards per 1000px of level width
guard_spawn_rate = guard_count > 0 ? (guard_count / max_x) * 1000.0 : 0.0

# Mean patrol radius across all guards (pixels). The patrolRadius field
# carries the half-width of each guard's patrol range in LevelConfig.res.
mean_patrol_radius = guard_count > 0 ?
sum(Float64(get(g, "patrolRadius", 0.0)) for g in guards) / guard_count : 0.0

# Defence density: average number of active flags per defended device
defence_density = if device_count > 0
total_flags = sum(length(get(dd, "flags", [])) for dd in defences)
Expand Down Expand Up @@ -323,6 +329,7 @@ function level_dict_to_stats(d::Dict{String,Any})::LevelStats
get(d, "levelName", get(d, "missionId", "Unknown Level")),
guard_count,
guard_spawn_rate,
mean_patrol_radius,
alert_threshold,
difficulty_score,
device_count,
Expand Down Expand Up @@ -759,8 +766,8 @@ function generate_recommendations(stats::LevelStats, sim::SimulationResult)::Vec
push!(recs, BalanceRecommendation(
stats.level_id,
"guard_patrol_radius",
stats.guard_spawn_rate,
stats.guard_spawn_rate * 0.85,
stats.mean_patrol_radius,
stats.mean_patrol_radius * 0.85,
"$(round(guard_death_ratio*100; digits=0))% of deaths are from guards. " *
"Consider widening patrol gaps or reducing guard detection radius.",
"moderate",
Expand Down Expand Up @@ -834,6 +841,7 @@ function export_report(
"level_name" => s.level_name,
"guard_count" => s.guard_count,
"guard_spawn_rate" => round(s.guard_spawn_rate; digits=3),
"mean_patrol_radius" => round(s.mean_patrol_radius; digits=1),
"alert_threshold" => round(s.alert_threshold; digits=3),
"difficulty_score" => round(s.difficulty_score; digits=2),
"device_count" => s.device_count,
Expand Down
Loading