// Core types for game-agnostic balance optimization
/// Simulation result: flexible key-value metrics
pub struct Metrics(Map[String, Double])
/// Get a metric value, defaulting to 0.0 if not found
pub fn Metrics::get(self : Metrics, key : String) -> Double {
match self.0.get(key) {
Some(v) => v
None => 0.0
}
}
/// Set a metric value
pub fn Metrics::set(self : Metrics, key : String, value : Double) -> Unit {
self.0.set(key, value)
}
/// Create empty metrics
pub fn Metrics::new() -> Metrics {
Metrics(Map::new())
}
/// Iterate over all metric entries
pub fn Metrics::each(self : Metrics, f : (String, Double) -> Unit) -> Unit {
self.0.each(f)
}
/// Average multiple metrics maps (per-key averaging)
pub fn average_metrics(all : Array[Metrics]) -> Metrics {
if all.length() == 0 {
return Metrics::new()
}
let result : Map[String, Double] = Map::new()
let n = all.length().to_double()
for m in all {
m.each(fn(k, v) {
match result.get(k) {
Some(prev) => result.set(k, prev + v)
None => result.set(k, v)
}
})
}
let keys : Array[String] = []
result.each(fn(k, _) { keys.push(k) })
for k in keys {
match result.get(k) {
Some(v) => result.set(k, v / n)
None => ()
}
}
Metrics(result)
}
/// A single optimization target: metric name, target value, weight
pub(all) struct BalanceTarget {
metric : String
target : Double
weight : Double
}
/// Parameter specification: name, initial value, min, max
pub(all) struct ParamSpec {
name : String
initial : Double
min : Double
max : Double
}
/// Clamp params array according to specs
pub fn clamp_params(
params : Array[Double],
specs : Array[ParamSpec],
) -> Array[Double] {
let result = params.copy()
for i = 0; i < specs.length() && i < result.length(); i = i + 1 {
let s = specs[i]
if result[i] < s.min {
result[i] = s.min
} else if result[i] > s.max {
result[i] = s.max
}
}
result
}
/// Extract initial values from param specs
pub fn initial_params(specs : Array[ParamSpec]) -> Array[Double] {
specs.map(fn(s) { s.initial })
}
/// Extract param names from specs
pub fn param_names(specs : Array[ParamSpec]) -> Array[String] {
specs.map(fn(s) { s.name })
}
/// Result of a tuning run
pub(all) struct TuneResult {
params : Array[Double]
loss : Double
generation : Int
metrics : Metrics
}