solution: day22
This commit is contained in:
parent
00000370b3
commit
00000380c3
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
Hit Points: 14
|
||||||
|
Damage: 8
|
Loading…
Reference in New Issue