pub fn process_part1(input: &str) -> u32 { let mut boss = Character::default(); input.lines().for_each(|line| { let (attribute, value) = line.split_once(": ").unwrap(); if attribute == "Hit Points" { boss.hp = value.parse::().unwrap(); } else if attribute == "Damage" { boss.damage = value.parse::().unwrap(); } }); let player = Character { hp: 50, mana: 500, ..Default::default() }; let mut used_mana = Vec::new(); for spell in Spell::get_all() { used_mana.push(round(player.clone(), boss.clone(), spell, 0)); } let mut smallest = u32::MAX; for state in used_mana { if state.get_value() < smallest { smallest = state.get_value(); } } smallest } pub fn process_part2(input: &str) -> u32 { 0 } fn round(mut player: Character, mut boss: Character, spell: Spell, spent_so_far: u32) -> State { // Player turn for effect in player.status_effects.iter_mut() { if effect.name == SpellID::Recharge { player.mana += effect.mana; } effect.duration -= 1; if effect.duration > 0 && effect.name == SpellID::Shield { player.armor = effect.armor; } else if effect.name == SpellID::Shield { player.armor = 0; } } for effect in boss.status_effects.iter_mut() { boss.hp = boss.hp.saturating_sub(effect.damage); effect.duration -= 1; } boss.status_effects = boss .status_effects .into_iter() .filter(|&effect| effect.duration > 0) .collect::>(); player.status_effects = player .status_effects .into_iter() .filter(|&effect| effect.duration > 0) .collect::>(); let used_mana = spell.cost + spent_so_far; let mut dmg = 0; match spell.name { SpellID::MagicMissile => { player.mana = player.mana.saturating_sub(spell.cost); dmg = spell.damage; } SpellID::Drain => { player.mana = player.mana.saturating_sub(spell.cost); dmg = spell.damage; player.hp += spell.hp; } SpellID::Shield => { player.mana = player.mana.saturating_sub(spell.cost); player.status_effects.push(spell); player.armor = spell.armor; } SpellID::Poison => { player.mana = player.mana.saturating_sub(spell.cost); boss.status_effects.push(spell); } SpellID::Recharge => { player.mana = player.mana.saturating_sub(spell.cost); player.status_effects.push(spell); } } boss.hp = boss.hp.saturating_sub(dmg); if boss.hp == 0 { return State::Win(used_mana); } if player.mana == 0 { return State::Loss(0); } // Boss turn for effect in player.status_effects.iter_mut() { if effect.name == SpellID::Recharge { player.mana += effect.mana; } effect.duration -= 1; if effect.duration > 0 && effect.name == SpellID::Shield { player.armor = effect.armor; } else if effect.name == SpellID::Shield { player.armor = 0; } } player.status_effects = player .status_effects .into_iter() .filter(|&effect| effect.duration > 0) .collect::>(); for effect in boss.status_effects.iter_mut() { boss.hp = boss.hp.saturating_sub(effect.damage); effect.duration -= 1; } if boss.hp == 0 { return State::Win(used_mana); } boss.status_effects = boss .status_effects .into_iter() .filter(|&effect| effect.duration > 0) .collect::>(); let dmg = boss.hit_damage(&player); player.hp = player.hp.saturating_sub(dmg); if player.hp == 0 { return State::Loss(0); } // next round let mut smallest_used_mana = State::Playing(u32::MAX); for spell in Spell::get_all() { let next_state = round(player.clone(), boss.clone(), spell, used_mana); match next_state { State::Playing(mana) => { if mana < smallest_used_mana.get_value() { smallest_used_mana = State::Playing(mana); } } State::Win(mana) => { if mana < smallest_used_mana.get_value() { smallest_used_mana = State::Win(mana); } } State::Loss(_) => {} }; } smallest_used_mana } #[derive(Debug)] enum State { Playing(u32), Win(u32), Loss(u32), } impl State { fn get_value(&self) -> u32 { match self { State::Playing(value) => *value, State::Win(value) => *value, State::Loss(value) => *value, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum SpellID { MagicMissile, Drain, Shield, Poison, Recharge, } #[derive(Debug, Clone, Copy)] struct Spell { name: SpellID, hp: u32, damage: u32, mana: u32, armor: u32, duration: u32, cost: u32, } impl Spell { fn get_all() -> Vec { vec![ Spell { name: SpellID::MagicMissile, hp: 0, damage: 4, mana: 0, armor: 0, duration: 0, cost: 53, }, Spell { name: SpellID::Drain, hp: 2, damage: 2, mana: 0, armor: 0, duration: 0, cost: 73, }, Spell { name: SpellID::Shield, hp: 0, damage: 0, mana: 0, armor: 7, duration: 6, cost: 113, }, Spell { name: SpellID::Poison, hp: 0, damage: 3, mana: 0, armor: 0, duration: 6, cost: 173, }, Spell { name: SpellID::Recharge, hp: 0, damage: 0, mana: 101, armor: 0, duration: 5, cost: 229, }, ] } } #[derive(Debug, Default, Clone)] struct Character { hp: u32, damage: u32, armor: u32, mana: u32, status_effects: Vec, } impl Character { fn hit_damage(&self, other: &Self) -> u32 { let damage = self.damage.saturating_sub(other.armor); if damage > 0 { damage } else { 1 } } } #[cfg(test)] mod tests { use super::*; #[test] fn part1_1() { let boss = Character { hp: 13, damage: 8, ..Default::default() }; let player = Character { hp: 10, mana: 250, ..Default::default() }; let mut used_mana = Vec::new(); for spell in Spell::get_all() { used_mana.push(round(player.clone(), boss.clone(), spell, 0)); } let mut result = u32::MAX; for state in used_mana { if state.get_value() < result { result = state.get_value(); } } assert_eq!(result, 226); } #[test] fn part1_2() { let boss = Character { hp: 14, damage: 8, ..Default::default() }; let player = Character { hp: 10, mana: 250, ..Default::default() }; let mut used_mana = Vec::new(); for spell in Spell::get_all() { used_mana.push(round(player.clone(), boss.clone(), spell, 0)); } let mut result = u32::MAX; for state in used_mana { if state.get_value() < result { result = state.get_value(); } } assert_eq!(result, 641); } #[test] fn part2() { let result = process_part2(""); assert_eq!(result, 0); } }