Skip to content

Commit 67c7736

Browse files
committed
Abandon setups that are unlikely to win
2018 day 24 is somewhat input sensitive; on my machine, my input file completed in 2.5ms at a boost of 42, but another one scraped from the megathread took 5.0ms and had to reach a boost of 90. With some tracing, I observed that the higher the boost got, the closer the ratio between baseline infect_hp/immune_eff vs. immune_hp/infect_eff. It is still possible to win even when the ratios are not equal (thanks to the group attributes tweaking actual damage), but appears that any difference larger than double is going to favor the infection - there simply won't be enough hit points to survive long enough for immune to win. Capture this as a heuristic to bypass low-value boosts, so that the time is spent on battles more likely to matter. For the files I tested, this cuts runtime on my machine to 2.0ms, regardless of whether the pre-patch file was 2.5ms or 5.0ms.
1 parent 114bd19 commit 67c7736

1 file changed

Lines changed: 20 additions & 0 deletions

File tree

src/year2018/day24.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
//! become too weak to destroy any further units. As each fight is independent, we find the
77
//! minimum boost value with a multithreaded parallel search.
88
//!
9+
//! Additionally, it is possible to speed up part 2 with a heuristic comparing the ratio of
10+
//! infection hit points to immune effective power, vs. the ratio of immune hit points to
11+
//! infection effective power. A larger ratio requires more turns to defeat the other side. It is
12+
//! possible to win with the larger ratio, but larger boosts bring the ratios closer together;
13+
//! so this code skips boosts where the immune system has more than twice the nominal work to
14+
//! do to wipe out the infection hit points.
15+
//!
916
//! [`Day 15`]: crate::year2018::day15
1017
use crate::util::hash::*;
1118
use crate::util::parse::*;
@@ -21,6 +28,7 @@ enum Kind {
2128
Immune,
2229
Infection,
2330
Draw,
31+
Unbalanced,
2432
}
2533

2634
#[derive(Clone, Copy)]
@@ -118,6 +126,18 @@ fn fight(input: &Input, boost: i32) -> (Kind, i32) {
118126
// Boost reindeer's immune system.
119127
immune.iter_mut().for_each(|group| group.damage += boost);
120128

129+
// Determine if the fight is even worth running.
130+
let infect_hp: usize =
131+
infection.iter().map(|group| (group.units * group.hit_points) as usize).sum();
132+
let immune_hp: usize =
133+
immune.iter().map(|group| (group.units * group.hit_points) as usize).sum();
134+
let infect_eff: usize =
135+
infection.iter().map(|group| (group.units * group.damage) as usize).sum();
136+
let immune_eff: usize = immune.iter().map(|group| (group.units * group.damage) as usize).sum();
137+
if boost != 0 && infect_hp / immune_eff > 2 * immune_hp / infect_eff {
138+
return (Kind::Unbalanced, 0);
139+
}
140+
121141
for turn in 1.. {
122142
// Target selection phase.
123143
let mut target_selection = |attacker: &[Group], defender: &mut [Group], kind: Kind| {

0 commit comments

Comments
 (0)