Target SEAD strikes at observed radar contacts

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
2026-11-02 23:58:35 +03:00
parent e38ca0d9ab
commit ce76f20074
2 changed files with 29 additions and 6 deletions
+1 -1
View File
@@ -175,7 +175,7 @@ impl Sim {
continue;
}
// Only those not yet sent at a unit.
if self.sams.iter().any(|s| s.pos.distance(d.target - d.err) < 6.0) {
if known.iter().any(|(_, pos)| pos.distance(d.target - d.err) < 6.0) {
continue;
}
let to = if !ew.is_empty() { pick(n, &ew) } else { pick(n, &known) };
+28 -5
View File
@@ -539,12 +539,14 @@ impl Sim {
}
}
/// The strikes a smart raid sends at the radars (they emit, so the enemy knows them whether or not they
/// fired): for the drones of `kinds` in order, the radar's place and id for those that go for one. Each radar
/// The strikes a smart raid sends at observed radars: for drones of `kinds` in order,
/// the last observed place and id for those that go for one. Each radar
/// gets two or three, the fast strikers first, and all together no more than about a quarter of the raid.
pub(super) fn radar_strikes(&self, kinds: &[(DroneKind, u32)]) -> Vec<Option<(DVec3, u32)>> {
let mut radars: Vec<&Sam> = self.sams.iter().filter(|s| s.alive() && s.kind == SamKind::Radar).collect();
radars.sort_by_key(|s| s.id);
let mut radars: Vec<(u32, DVec3)> = self.known_units().into_iter()
.filter(|(id, _)| self.sams.iter().any(|s| s.id == *id && s.alive() && s.kind == SamKind::Radar))
.collect();
radars.sort_by_key(|(id, _)| *id);
let flat: Vec<DroneKind> = kinds.iter().flat_map(|&(k, n)| std::iter::repeat_n(k, n as usize)).collect();
let mut jobs = vec![None; flat.len()];
if radars.is_empty() {
@@ -559,7 +561,7 @@ impl Sim {
let cap = (flat.len() / 4).max(1);
for (n, &(_, i)) in ranked.iter().take((radars.len() * want).min(cap)).enumerate() {
let radar = radars[n % radars.len()];
jobs[i] = Some((radar.pos, radar.id));
jobs[i] = Some((radar.1, radar.0));
}
jobs
}
@@ -969,6 +971,26 @@ mod tests {
sim.drones.iter().chain(sim.queue.iter().map(|q| &q.1)).filter(|d| d.alive() && (d.target - d.err).distance(p) < 6.0).map(|d| d.kind).collect()
}
#[test]
fn sead_uses_only_unexpired_observations_at_the_last_known_position() {
let mut sim = Sim::new(121);
let old = DVec3::new(100.0, 70.0, 200.0);
let mut radar = Sam::new(1, SamKind::Radar, old, 0.0);
radar.radar_on = false;
sim.sams.push(radar);
let kinds = [(DroneKind::Kh22, 8)];
assert!(sim.radar_strikes(&kinds).iter().all(Option::is_none));
sim.tick = 20;
sim.intel.revealed.push((1, old, sim.tick));
sim.sams[0].pos = DVec3::new(900.0, 70.0, 900.0);
sim.tick = 40;
let jobs = sim.radar_strikes(&kinds);
assert_eq!(jobs.iter().flatten().count(), 2);
assert!(jobs.iter().flatten().all(|job| *job == (old, 1)));
sim.tick = 6020;
assert!(sim.radar_strikes(&kinds).iter().all(Option::is_none));
}
#[test]
fn a_smart_raid_goes_for_every_radar_and_the_enemys_adapting_does_not_turn_it_away() {
let mut sim = Sim::new(21);
@@ -978,6 +1000,7 @@ mod tests {
let p = DVec3::new(at.0, 70.0, at.1);
sim.sams.push(Sam::new(id, SamKind::Radar, p, i as f64));
radars.push((id, p));
sim.intel.revealed.push((id, p, sim.tick));
}
let ew = sim.id();
sim.sams.push(Sam::new(ew, SamKind::Ew, DVec3::new(0.0, 70.0, 0.0), 0.0));