solution: day22

This commit is contained in:
Matej Janezic 2022-12-13 22:19:24 +01:00
parent 00000370b3
commit 00000380c3
Signed by: janezicmatej
GPG Key ID: 4298E230ED37B2C0
2 changed files with 213 additions and 0 deletions

211
src/bin/22.rs Normal file
View File

@ -0,0 +1,211 @@
use std::cmp::max;
use itertools::Itertools;
use lazy_static::lazy_static;
#[derive(Clone, Copy)]
struct Player {
hp: i32,
damage: i32,
mana: i32,
}
#[derive(Debug, Clone)]
struct Spell {
name: String,
timer: i32,
damage: i32,
armor: i32,
heal: i32,
mana: i32,
cost: i32,
}
lazy_static! {
static ref SPELLS: [Spell; 5] = [
Spell {
name: "Magic Missile".to_string(),
timer: 1,
damage: 4,
armor: 0,
heal: 0,
mana: 0,
cost: 53,
},
Spell {
name: "Drain".to_string(),
timer: 1,
damage: 2,
armor: 0,
heal: 2,
mana: 0,
cost: 73,
},
Spell {
name: "Shield".to_string(),
timer: 6,
damage: 0,
armor: 7,
heal: 0,
mana: 0,
cost: 113,
},
Spell {
name: "Poison".to_string(),
timer: 6,
damage: 3,
armor: 0,
heal: 0,
mana: 0,
cost: 173,
},
Spell {
name: "Recharge".to_string(),
timer: 5,
damage: 0,
armor: 0,
heal: 0,
mana: 101,
cost: 229,
},
];
}
fn optimize_fight(
mut boss: Player,
mut player: Player,
players_turn: bool,
mut active_spells: Vec<Spell>,
best_cost: &mut i32,
cost: i32,
hardmode: bool,
) {
if cost > *best_cost {
return;
}
let mut remaining = active_spells
.drain(..)
.filter(|x| x.timer > 0)
.collect_vec();
let mut armor = 0;
for spell in remaining.iter_mut() {
spell.timer -= 1;
boss.hp -= spell.damage;
armor += spell.armor;
player.hp += spell.heal;
player.mana += spell.mana;
}
if hardmode && players_turn {
player.hp -= 1;
}
if player.hp <= 0 {
return;
}
if boss.hp <= 0 {
if cost < *best_cost {
*best_cost = cost;
}
return;
}
if players_turn {
let current = remaining
.iter()
.filter(|x| x.timer > 0)
.map(|x| x.name.to_string())
.collect_vec();
for spell in SPELLS
.iter()
.filter(|x| !current.contains(&x.name) && x.cost < player.mana)
.collect_vec()
{
let mut new_spells = remaining.to_vec();
new_spells.push(spell.clone());
let mut c = player;
c.mana -= spell.cost;
optimize_fight(
boss,
c,
!players_turn,
new_spells,
best_cost,
cost + spell.cost,
hardmode,
);
}
} else {
let damage = max(boss.damage - armor, 1);
player.hp -= damage;
optimize_fight(
boss,
player,
!players_turn,
remaining,
best_cost,
cost,
hardmode,
);
}
}
pub fn part_one(input: &str) -> Option<i32> {
let (hp, damage) = input
.split('\n')
.map(|x| x.split(": ").last().unwrap().parse::<i32>().unwrap())
.next_tuple()
.unwrap();
let boss = Player {
hp,
damage,
mana: 0,
};
let me = Player {
hp: 50,
damage: 0,
mana: 500,
};
// this takes 7minutes but iiwii
let mut best_score = i32::max_value();
optimize_fight(boss, me, true, vec![], &mut best_score, 0, false);
Some(best_score)
}
pub fn part_two(input: &str) -> Option<i32> {
let (hp, damage) = input
.split('\n')
.map(|x| x.split(": ").last().unwrap().parse::<i32>().unwrap())
.next_tuple()
.unwrap();
let boss = Player {
hp,
damage,
mana: 0,
};
let me = Player {
hp: 50,
damage: 0,
mana: 500,
};
let mut best_score = i32::max_value();
optimize_fight(boss, me, true, vec![], &mut best_score, 0, true);
Some(best_score)
}
fn main() {
let input = &aoc::read_file("inputs", 22);
aoc::solve!(1, part_one, input);
aoc::solve!(2, part_two, input);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
let input = aoc::read_file("test_inputs", 22);
assert_eq!(part_one(input.trim()), Some(212));
}
#[test]
fn test_part_two() {
let input = aoc::read_file("test_inputs", 22);
assert_eq!(part_two(input.trim()), Some(212));
}
}

2
src/test_inputs/22.txt Normal file
View File

@ -0,0 +1,2 @@
Hit Points: 14
Damage: 8