diff --git a/src/day22.rs b/src/day22.rs new file mode 100644 index 0000000..cddc4f2 --- /dev/null +++ b/src/day22.rs @@ -0,0 +1,250 @@ +use std::{cmp::max, collections::BinaryHeap}; + +use aoc_runner_derive::{aoc, aoc_generator}; + +#[aoc_generator(day22)] +fn parse(input: &str) -> Boss { + let ints: Vec = input + .lines() + .map(|line| line.split_once(": ").unwrap().1) + .map(|it| it.parse().unwrap()) + .collect(); + Boss { + health: ints[0], + damage: ints[1], + } +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +struct Boss { + health: i32, + damage: i32, +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +struct Wizard { + health: i32, + mana: i32, + spent: i32, + rem_shield: i32, + rem_poison: i32, + rem_recharge: i32, +} + +impl Wizard { + fn armor(&self) -> i32 { + if self.rem_shield > 0 { + 7 + } else { + 0 + } + } + + fn cant_spell(&self) -> bool { + if !self.can_missile() + && !self.can_drain() + && !self.can_shield() + && !self.can_poison() + && !self.can_recharge() + { + return true; + } + false + } + + fn can_missile(&self) -> bool { + self.mana >= 53 + } + + fn can_drain(&self) -> bool { + self.mana >= 73 + } + + fn can_shield(&self) -> bool { + self.mana >= 113 && self.rem_shield < 1 + } + + fn can_poison(&self) -> bool { + self.mana >= 173 && self.rem_poison < 1 + } + + fn can_recharge(&self) -> bool { + self.mana >= 229 && self.rem_recharge < 1 + } +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +enum Party { + Wiz, + Bo, +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +struct State { + boss: Boss, + wizard: Wizard, + next: Party, +} + +impl Ord for State { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + other.wizard.spent.cmp(&self.wizard.spent) + } +} +impl PartialOrd for State { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl State { + fn new(boss: Boss) -> State { + State { + boss, + wizard: Wizard { + health: 50, + mana: 500, + spent: 0, + rem_shield: 0, + rem_poison: 0, + rem_recharge: 0, + }, + next: Party::Wiz, + } + } + + fn play_wiz(&self) -> RoundResult { + let mut next = self.apply_effects(); + next.next = Party::Bo; + + if self.winner().is_some() { + panic!("should not play if there is a winner!") + } + let mut nexts = vec![]; + + if self.wizard.can_missile() { + let mut s = next; + s.wizard.mana -= 53; + s.wizard.spent += 53; + s.boss.health -= 4; + nexts.push(s); + } + + if self.wizard.can_drain() { + let mut s = next; + s.wizard.mana -= 73; + s.wizard.spent += 73; + s.boss.health -= 2; + s.wizard.health += 2; + nexts.push(s); + } + + if self.wizard.can_shield() { + let mut s = next; + s.wizard.mana -= 113; + s.wizard.spent += 113; + s.wizard.rem_shield = 6; + nexts.push(s); + } + + if self.wizard.can_poison() { + let mut s = next; + s.wizard.mana -= 173; + s.wizard.spent += 173; + s.wizard.rem_poison = 6; + nexts.push(s); + } + + if self.wizard.can_missile() { + let mut s = next; + s.wizard.mana -= 229; + s.wizard.spent += 229; + s.wizard.rem_recharge = 5; + nexts.push(s); + } + nexts + } + + fn play_boss(&self) -> RoundResult { + let armor = self.wizard.armor(); // calc before reducing counter + let mut next = self.apply_effects(); + next.next = Party::Wiz; + if self.winner().is_some() { + panic!("should not play if there is a winner!") + } + next.wizard.health -= max(1, next.boss.damage - armor); + vec![next] + } + + fn apply_effects(&self) -> Self { + let mut next = *self; + if next.wizard.rem_shield > 0 { + next.wizard.rem_shield -= 1; + } + if next.wizard.rem_poison > 0 { + next.wizard.rem_poison -= 1; + next.boss.health -= 3; + } + if next.wizard.rem_recharge > 0 { + next.wizard.rem_recharge -= 1; + next.wizard.mana += 101; + } + next + } + + fn winner(&self) -> Option { + if self.wizard.cant_spell() && self.next == Party::Wiz { + if self.boss.health < 1 { + return None; + } + return Some(Party::Bo); + } + if self.wizard.health > 0 && self.boss.health < 1 { + return Some(Party::Wiz); + } + if self.wizard.health < 1 && self.boss.health > 0 { + return Some(Party::Bo); + } + if self.wizard.health < 1 && self.boss.health < 1 { + return None; + } + None + } +} + +type RoundResult = Vec; +const INTERACTIVE: bool = false; + +#[aoc(day22, part1)] +fn part1(boss: &Boss) -> i32 { + let first = State::new(*boss); + let mut queue = BinaryHeap::new(); + queue.push(first); + + while let Some(current) = queue.pop() { + if let Some(winner) = current.winner() { + if winner == Party::Wiz { + return current.wizard.spent; + } + continue; + } + let nexts = match current.next { + Party::Wiz => current.play_wiz(), + Party::Bo => current.play_boss(), + }; + if INTERACTIVE { + let _ = std::io::stdin().read_line(&mut String::new()).unwrap(); + println!("{current:?}"); + println!("{:#?}", nexts); + } + for n in nexts { + queue.push(n); + } + } + panic!("no solution found") +} + +#[aoc(day22, part2)] +fn part2(_boss: &Boss) -> usize { + 0 +} diff --git a/src/day23.rs b/src/day23.rs new file mode 100644 index 0000000..11fa780 --- /dev/null +++ b/src/day23.rs @@ -0,0 +1,18 @@ +use aoc_runner_derive::{aoc, aoc_generator}; + +type Input = (); + +#[aoc_generator(day22)] +fn parse(input: &str) -> Input { + todo!() +} + +#[aoc(day22, part1)] +fn part1(_: &Input) -> usize { + 0 +} + +#[aoc(day22, part2)] +fn part2(_: &Input) -> usize { + 0 +} diff --git a/src/day24.rs b/src/day24.rs new file mode 100644 index 0000000..11fa780 --- /dev/null +++ b/src/day24.rs @@ -0,0 +1,18 @@ +use aoc_runner_derive::{aoc, aoc_generator}; + +type Input = (); + +#[aoc_generator(day22)] +fn parse(input: &str) -> Input { + todo!() +} + +#[aoc(day22, part1)] +fn part1(_: &Input) -> usize { + 0 +} + +#[aoc(day22, part2)] +fn part2(_: &Input) -> usize { + 0 +} diff --git a/src/day25.rs b/src/day25.rs new file mode 100644 index 0000000..11fa780 --- /dev/null +++ b/src/day25.rs @@ -0,0 +1,18 @@ +use aoc_runner_derive::{aoc, aoc_generator}; + +type Input = (); + +#[aoc_generator(day22)] +fn parse(input: &str) -> Input { + todo!() +} + +#[aoc(day22, part1)] +fn part1(_: &Input) -> usize { + 0 +} + +#[aoc(day22, part2)] +fn part2(_: &Input) -> usize { + 0 +} diff --git a/src/lib.rs b/src/lib.rs index bd1adc8..11ebd92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,5 +14,6 @@ mod day18; mod day19; mod day20; mod day21; +mod day22; aoc_lib! { year = 2015 }