Initial atempt at day24.

This commit is contained in:
2018-12-24 08:42:13 +01:00
parent 85538d2fc6
commit f78a602d38
2 changed files with 196 additions and 4 deletions

View File

@@ -1,19 +1,193 @@
use std::cmp::Ordering;
use std::cmp::Reverse;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use itertools::Itertools;
use regex::Regex;
use common::Solution;
#[derive(Default, Debug, Eq, PartialEq, Clone)]
struct Group {
power: u32,
count: u32,
hp: u32,
initiative: i32,
damage: String,
weaknesses: Vec<String>,
immunities: Vec<String>,
faction: char,
}
impl Group {
pub fn is_alive(&self) -> bool {
self.count > 0
}
pub fn damage_to(&self, other: &Group) -> u32 {
let damage = self.effective_power();
if other.weaknesses.contains(&self.damage) {
damage * 2
} else if other.immunities.contains(&self.damage) {
0
} else {
damage
}
}
pub fn effective_power(&self) -> u32 {
self.power * self.count
}
}
impl Ord for Group {
fn cmp(&self, other: &Self) -> Ordering {
self.effective_power().cmp(&other.effective_power())
.then(other.initiative.cmp(&self.initiative))
}
}
impl PartialOrd for Group {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Default)]
pub struct Day24 {}
pub struct Day24 {
units: Vec<Group>,
}
impl Day24 {
pub fn new() -> Self {
Default::default()
}
fn read_input(&mut self, input: &mut Read) {
let matcher = Regex::new(r"(\d+) units each with (\d+) hit points (\(([^)]+)\) )?with an attack that does (\d+) (\w+) damage at initiative (\d+)").unwrap();
let weakness_matcher = Regex::new(r"(weak|immune) to ([^;)]+)").unwrap();
let reader = BufReader::new(input);
let mut fac = 'X';
for line in reader.lines() {
let line = line.unwrap();
match line.as_str() {
"Immune System:" => { fac = 'D' }
"" => {}
"Infection:" => { fac = 'I' }
line => {
let caps = matcher.captures(line).unwrap_or_else(|| {
panic!("{}", line);
});
let mut group = Group {
count: caps[1].parse().unwrap(),
hp: caps[2].parse().unwrap(),
power: caps[5].parse().unwrap(),
damage: caps[6].to_string(),
initiative: caps[7].parse().unwrap(),
faction: fac,
..Default::default()
};
if let Some(caps) = caps.get(4) {
for modified in weakness_matcher.captures_iter(caps.as_str()) {
let target = match &modified[1] {
"weak" => &mut group.weaknesses,
"immune" => &mut group.immunities,
_ => panic!(),
};
for t in modified[2].split(", ") {
target.push(t.to_string());
}
}
}
self.units.push(group)
}
}
}
}
fn simulate(&mut self) {
let mut order: Vec<usize> = (0..self.units.len()).collect();
order.sort_unstable_by_key(|&x| &self.units[x]);
order.reverse();
// select targets
let mut targets: Vec<Option<usize>> = vec![None; self.units.len()];
let mut is_targeted = vec![false; self.units.len()];
for &i in &order {
let unit = &self.units[i];
if !unit.is_alive() {
continue;
}
let damage = self.units.iter().map(|x| unit.damage_to(x)).collect_vec();
let target = (0..self.units.len())
.filter(|&x| !is_targeted[x] && self.units[x].faction != unit.faction)
.max_by(|&x, &y| {
damage[x].cmp(&damage[y])
.then(self.units[x].effective_power().cmp(&self.units[y].effective_power()))
.then(self.units[x].initiative.cmp(&self.units[y].initiative))
});
if let Some(target) = target {
println!("{} will attack {}, dealing {}", i, target, damage[target]);
targets[i] = Some(target);
is_targeted[target] = true;
}
}
order.sort_unstable_by_key(|&x| Reverse(self.units[x].initiative));
println!("{:?}", order);
for attacker in order {
if !self.units[attacker].is_alive() {
continue;
}
if let Some(id) = targets[attacker] {
let damage = self.units[attacker].damage_to(&self.units[id]);
let defender = &mut self.units[id];
let losses = damage / defender.hp;
println!("{} does {} damage to {}. Killed {}", attacker, damage, id, losses);
defender.count = defender.count.saturating_sub(losses);
}
}
}
fn both_alive(&self) -> bool {
let mut seen = None;
for unit in self.units.iter().filter(|g| g.is_alive()) {
if let Some(seen) = seen {
if seen != unit.faction {
return true;
}
continue;
}
seen = Some(unit.faction);
}
return false;
}
}
impl Solution for Day24 {
fn part1(&mut self, _input: &mut Read) -> String {
unimplemented!()
fn part1(&mut self, input: &mut Read) -> String {
self.read_input(input);
while self.both_alive() {
self.simulate();
}
let result: u32 = self.units.iter().map(|x| x.count).sum();
result.to_string()
}
fn part2(&mut self, _input: &mut Read) -> String {
@@ -22,4 +196,15 @@ impl Solution for Day24 {
}
#[cfg(test)]
mod tests {}
mod tests {
use common::Solution;
use day24::Day24;
const SAMPLE_INPUT: &[u8] = include_bytes!("samples/24.txt");
#[test]
fn sample_part1() {
let mut instance = Day24::new();
assert_eq!("5216", instance.part1(&mut SAMPLE_INPUT))
}
}

7
2018/src/samples/24.txt Normal file
View File

@@ -0,0 +1,7 @@
Immune System:
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
Infection:
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4