day 22 part 2

...

asdf
This commit is contained in:
2024-11-01 10:27:22 +01:00
parent f599a40720
commit 886dc2e785

View File

@@ -1,4 +1,7 @@
use std::{cmp::max, collections::BinaryHeap}; use std::{
cmp::{max, min},
collections::BinaryHeap,
};
use aoc_runner_derive::{aoc, aoc_generator}; use aoc_runner_derive::{aoc, aoc_generator};
@@ -73,17 +76,11 @@ impl Wizard {
} }
} }
#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
enum Party {
Wiz,
Bo,
}
#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] #[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
struct State { struct State {
boss: Boss, boss: Boss,
wizard: Wizard, wizard: Wizard,
next: Party, hard: bool,
} }
impl Ord for State { impl Ord for State {
@@ -91,6 +88,7 @@ impl Ord for State {
other.wizard.spent.cmp(&self.wizard.spent) other.wizard.spent.cmp(&self.wizard.spent)
} }
} }
impl PartialOrd for State { impl PartialOrd for State {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
@@ -98,7 +96,7 @@ impl PartialOrd for State {
} }
impl State { impl State {
fn new(boss: Boss) -> State { fn new(boss: Boss, hard: bool) -> State {
State { State {
boss, boss,
wizard: Wizard { wizard: Wizard {
@@ -109,17 +107,29 @@ impl State {
rem_poison: 0, rem_poison: 0,
rem_recharge: 0, rem_recharge: 0,
}, },
next: Party::Wiz, hard,
} }
} }
fn play_wiz(&self) -> RoundResult { fn play_two_turns(&self) -> RoundResult {
let mut next = self.apply_effects(); let mut next = *self;
next.next = Party::Bo; if self.hard {
next.wizard.health -= 1;
if self.winner().is_some() { if next.winner() != Winner::Ongoing {
panic!("should not play if there is a winner!") return vec![];
}
} }
let next = next.apply_effects();
// if next.winner() != Winner::Ongoing {
// return vec![next];
// }
if next.wizard.cant_spell() {
// next.wizard.health = -100;
// return vec![next];
return vec![];
}
let mut nexts = vec![]; let mut nexts = vec![];
if self.wizard.can_missile() { if self.wizard.can_missile() {
@@ -155,25 +165,23 @@ impl State {
nexts.push(s); nexts.push(s);
} }
if self.wizard.can_missile() { if self.wizard.can_recharge() {
let mut s = next; let mut s = next;
s.wizard.mana -= 229; s.wizard.mana -= 229;
s.wizard.spent += 229; s.wizard.spent += 229;
s.wizard.rem_recharge = 5; s.wizard.rem_recharge = 5;
nexts.push(s); nexts.push(s);
} }
nexts
}
fn play_boss(&self) -> RoundResult { for n in &mut nexts {
let armor = self.wizard.armor(); // calc before reducing counter let armor = n.wizard.armor();
let mut next = self.apply_effects(); *n = n.apply_effects();
next.next = Party::Wiz; if n.boss.health < 1 {
if self.winner().is_some() { continue;
panic!("should not play if there is a winner!") }
n.wizard.health -= max(1, n.boss.damage - armor);
} }
next.wizard.health -= max(1, next.boss.damage - armor); nexts
vec![next]
} }
fn apply_effects(&self) -> Self { fn apply_effects(&self) -> Self {
@@ -192,59 +200,114 @@ impl State {
next next
} }
fn winner(&self) -> Option<Party> { fn winner(&self) -> Winner {
if self.wizard.cant_spell() && self.next == Party::Wiz { if self.wizard.health < 1 {
if self.boss.health < 1 { Winner::Loose
return None; } else if self.boss.health < 1 {
} Winner::Win
return Some(Party::Bo); } else {
Winner::Ongoing
} }
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
} }
} }
#[derive(PartialEq, Debug)]
enum Winner {
Win,
Loose,
Ongoing,
}
type RoundResult = Vec<State>; type RoundResult = Vec<State>;
const INTERACTIVE: bool = false; const INTERACTIVE: bool = false;
#[aoc(day22, part1)] fn play(boss: Boss, hard: bool) -> i32 {
fn part1(boss: &Boss) -> i32 { let first = State::new(boss, hard);
let first = State::new(*boss);
let mut queue = BinaryHeap::new(); let mut queue = BinaryHeap::new();
queue.push(first); queue.push(first);
let mut min_mana = i32::MAX;
while let Some(current) = queue.pop() { 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 { if INTERACTIVE {
let _ = std::io::stdin().read_line(&mut String::new()).unwrap(); let _ = std::io::stdin().read_line(&mut String::new()).unwrap();
println!("{current:?}"); println!("{current:?}");
println!("{:#?}", nexts); println!("{:?}", current.winner());
} }
let mut next = current;
if hard {
next.wizard.health -= 1;
if next.wizard.health < 1 {
continue;
}
}
let next = next.apply_effects();
let mut nexts = vec![];
if next.wizard.can_missile() {
let mut s = next;
s.wizard.mana -= 53;
s.wizard.spent += 53;
s.boss.health -= 4;
nexts.push(s);
}
if next.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 next.wizard.can_shield() {
let mut s = next;
s.wizard.mana -= 113;
s.wizard.spent += 113;
s.wizard.rem_shield = 6;
nexts.push(s);
}
if next.wizard.can_poison() {
let mut s = next;
s.wizard.mana -= 173;
s.wizard.spent += 173;
s.wizard.rem_poison = 6;
nexts.push(s);
}
if next.wizard.can_recharge() {
let mut s = next;
s.wizard.mana -= 229;
s.wizard.spent += 229;
s.wizard.rem_recharge = 5;
nexts.push(s);
}
for n in nexts { for n in nexts {
queue.push(n); let armor = n.wizard.armor();
let mut n = n.apply_effects();
if n.boss.health < 1 {
min_mana = min(min_mana, n.wizard.spent);
queue.retain(|s| s.wizard.spent > min_mana);
} else {
n.wizard.health -= max(1, n.boss.damage - armor);
if n.wizard.health > 0 && n.wizard.spent < min_mana {
queue.push(n);
}
}
} }
} }
panic!("no solution found") return min_mana;
}
#[aoc(day22, part1)]
fn part1(boss: &Boss) -> i32 {
play(*boss, false)
} }
#[aoc(day22, part2)] #[aoc(day22, part2)]
fn part2(_boss: &Boss) -> usize { fn part2(boss: &Boss) -> i32 {
0 play(*boss, true) // 1295 too high
} }