Skip to content

Commit 828c78e

Browse files
committed
Precompute waterwell proximity
1 parent 4389543 commit 828c78e

1 file changed

Lines changed: 50 additions & 28 deletions

File tree

src/optimizer.rs

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,7 @@ impl SpatialGrid {
8282
/// have been REMOVED: those regions are the map's impassable mountain walls, not
8383
/// accessible ocean. Treating them as free water caused the optimizer to inflate
8484
/// scores at the western/northern map boundary and produce border-edge results.
85-
fn distance_to_nearest_water(
86-
x: f64,
87-
y: f64,
88-
opt_nodes: &[OptNode],
89-
waterwell_idx: Option<usize>,
90-
) -> f64 {
85+
fn distance_to_nearest_water(x: f64, y: f64, waterwell_nodes: &[(f64, f64)]) -> f64 {
9186
let mut min_dist_cm = f64::MAX;
9287

9388
// Major static bodies of water in Satisfactory (centres, half-widths, in cm)
@@ -122,16 +117,12 @@ fn distance_to_nearest_water(
122117
let mut min_dist_m = min_dist_cm / 100.0;
123118

124119
// Add dynamic waterwell checks
125-
if let Some(ww_idx) = waterwell_idx {
126-
for node in opt_nodes {
127-
if node.res_idx == ww_idx {
128-
let dx = (x - node.x) / 100.0;
129-
let dy = (y - node.y) / 100.0;
130-
let dist = (dx * dx + dy * dy).sqrt();
131-
if dist < min_dist_m {
132-
min_dist_m = dist;
133-
}
134-
}
120+
for &(node_x, node_y) in waterwell_nodes {
121+
let dx = (x - node_x) / 100.0;
122+
let dy = (y - node_y) / 100.0;
123+
let dist = (dx * dx + dy * dy).sqrt();
124+
if dist < min_dist_m {
125+
min_dist_m = dist;
135126
}
136127
}
137128

@@ -361,7 +352,7 @@ fn calculate_utility(
361352
weights_arr: &[f64],
362353
epsilons_arr: &[f64],
363354
res_to_idx: &HashMap<String, usize>,
364-
waterwell_idx: Option<usize>,
355+
waterwell_nodes: &[(f64, f64)],
365356
land_mask: &LandMask,
366357
) -> f64 {
367358
// Reject points outside the practical landmass polygon.
@@ -475,7 +466,7 @@ fn calculate_utility(
475466

476467
// Add virtual water yield based on proximity to mapped lakes/ponds or waterwells.
477468
if let Some(&water_idx) = res_to_idx.get("water") {
478-
let water_dist = distance_to_nearest_water(x, y, opt_nodes, waterwell_idx);
469+
let water_dist = distance_to_nearest_water(x, y, waterwell_nodes);
479470
let water_decay = match config.decay_func {
480471
crate::models::DistanceDecay::Gaussian => {
481472
let two_sigma_sq = 2.0 * config.sigma * config.sigma;
@@ -627,7 +618,7 @@ fn run_hill_climbing(
627618
weights_arr: &[f64],
628619
epsilons_arr: &[f64],
629620
res_to_idx: &HashMap<String, usize>,
630-
waterwell_idx: Option<usize>,
621+
waterwell_nodes: &[(f64, f64)],
631622
land_mask: &LandMask,
632623
) -> OptimizationResult {
633624
let mut curr_x = start_x;
@@ -646,7 +637,7 @@ fn run_hill_climbing(
646637
weights_arr,
647638
epsilons_arr,
648639
res_to_idx,
649-
waterwell_idx,
640+
waterwell_nodes,
650641
land_mask,
651642
);
652643

@@ -684,7 +675,7 @@ fn run_hill_climbing(
684675
weights_arr,
685676
epsilons_arr,
686677
res_to_idx,
687-
waterwell_idx,
678+
waterwell_nodes,
688679
land_mask,
689680
);
690681
if score > best_neighbor_score {
@@ -844,7 +835,7 @@ struct SearchContext {
844835
weights_arr: Vec<f64>,
845836
epsilons_arr: Vec<f64>,
846837
res_to_idx: HashMap<String, usize>,
847-
waterwell_idx: Option<usize>,
838+
waterwell_nodes: Vec<(f64, f64)>,
848839
land_mask: LandMask,
849840
}
850841

@@ -934,6 +925,15 @@ fn prepare_context(nodes: &[ResourceNode], config: &OptimizerConfig) -> SearchCo
934925

935926
let spatial_grid = SpatialGrid::new(&opt_nodes, 100000.0);
936927
let waterwell_idx = res_to_idx.get("waterwell").copied();
928+
let waterwell_nodes = waterwell_idx
929+
.map(|idx| {
930+
opt_nodes
931+
.iter()
932+
.filter(|node| node.res_idx == idx)
933+
.map(|node| (node.x, node.y))
934+
.collect()
935+
})
936+
.unwrap_or_default();
937937
let land_mask = LandMask::from_nodes(&opt_nodes);
938938

939939
SearchContext {
@@ -943,7 +943,7 @@ fn prepare_context(nodes: &[ResourceNode], config: &OptimizerConfig) -> SearchCo
943943
weights_arr,
944944
epsilons_arr,
945945
res_to_idx,
946-
waterwell_idx,
946+
waterwell_nodes,
947947
land_mask,
948948
}
949949
}
@@ -981,7 +981,7 @@ fn grid_search_refine(
981981
&ctx.weights_arr,
982982
&ctx.epsilons_arr,
983983
&ctx.res_to_idx,
984-
ctx.waterwell_idx,
984+
&ctx.waterwell_nodes,
985985
&ctx.land_mask,
986986
)
987987
})
@@ -1068,7 +1068,7 @@ fn grid_search_refine(
10681068
&ctx.weights_arr,
10691069
&ctx.epsilons_arr,
10701070
&ctx.res_to_idx,
1071-
ctx.waterwell_idx,
1071+
&ctx.waterwell_nodes,
10721072
&ctx.land_mask,
10731073
)
10741074
})
@@ -1187,7 +1187,7 @@ fn optimize_fast(ctx: &SearchContext, config: &OptimizerConfig) -> Vec<Optimizat
11871187
&ctx.weights_arr,
11881188
&ctx.epsilons_arr,
11891189
&ctx.res_to_idx,
1190-
ctx.waterwell_idx,
1190+
&ctx.waterwell_nodes,
11911191
&ctx.land_mask,
11921192
)
11931193
})
@@ -1211,6 +1211,28 @@ mod tests {
12111211
use super::*;
12121212
use crate::models::{GamePhase, OptimizerConfig};
12131213

1214+
#[test]
1215+
fn test_water_distance_inside_static_water_body() {
1216+
let distance = distance_to_nearest_water(140000.0, 230000.0, &[]);
1217+
1218+
assert!(distance.abs() < f64::EPSILON);
1219+
}
1220+
1221+
#[test]
1222+
fn test_water_distance_prefers_nearby_waterwell() {
1223+
let distance = distance_to_nearest_water(0.0, 0.0, &[(300.0, 400.0)]);
1224+
1225+
assert!((distance - 5.0).abs() < f64::EPSILON);
1226+
}
1227+
1228+
#[test]
1229+
fn test_water_distance_without_waterwells_uses_static_water() {
1230+
let distance = distance_to_nearest_water(0.0, 0.0, &[]);
1231+
let expected_distance = (30_000.0_f64.powi(2) + 5_000.0_f64.powi(2)).sqrt() / 100.0;
1232+
1233+
assert!((distance - expected_distance).abs() < f64::EPSILON);
1234+
}
1235+
12141236
#[test]
12151237
fn test_ignore_spawns() {
12161238
let nodes = crate::data_loader::load_default_nodes();
@@ -1237,7 +1259,7 @@ mod tests {
12371259
&ctx.weights_arr,
12381260
&ctx.epsilons_arr,
12391261
&ctx.res_to_idx,
1240-
ctx.waterwell_idx,
1262+
&ctx.waterwell_nodes,
12411263
&ctx.land_mask,
12421264
);
12431265
let ignored_score = calculate_utility(
@@ -1250,7 +1272,7 @@ mod tests {
12501272
&ctx.weights_arr,
12511273
&ctx.epsilons_arr,
12521274
&ctx.res_to_idx,
1253-
ctx.waterwell_idx,
1275+
&ctx.waterwell_nodes,
12541276
&ctx.land_mask,
12551277
);
12561278

0 commit comments

Comments
 (0)