From 00000370b3de564aad8bf2525192935a8ab76e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Jane=C5=BEi=C4=8D?= Date: Mon, 12 Dec 2022 21:36:58 +0100 Subject: [PATCH] solution: day21 --- src/bin/19.rs | 4 +- src/bin/21.rs | 259 +++++++++++++++++++++++++++++++++++++++++ src/test_inputs/20.txt | 0 3 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 src/bin/21.rs delete mode 100644 src/test_inputs/20.txt diff --git a/src/bin/19.rs b/src/bin/19.rs index f313a27..ce09c2e 100644 --- a/src/bin/19.rs +++ b/src/bin/19.rs @@ -62,11 +62,11 @@ mod tests { #[test] fn test_part_one() { let input = aoc::read_file("test_inputs", 19); - assert_eq!(part_one(&input.trim()), Some(4)); + assert_eq!(part_one(input.trim()), Some(4)); } #[test] fn test_part_two() { let input = aoc::read_file("test_inputs", 19); - assert_eq!(part_two(&input.trim()), Some(3)); + assert_eq!(part_two(input.trim()), Some(3)); } } diff --git a/src/bin/21.rs b/src/bin/21.rs new file mode 100644 index 000000000..bb7b3c9 --- /dev/null +++ b/src/bin/21.rs @@ -0,0 +1,259 @@ +use itertools::Itertools; +use ItemType::*; + +static STORE: [Item; 16] = [ + // weapons + Item { + item_type: Weapon, + cost: 8, + damage: 4, + armor: 0, + }, + Item { + item_type: Weapon, + cost: 10, + damage: 5, + armor: 0, + }, + Item { + item_type: Weapon, + cost: 25, + damage: 6, + armor: 0, + }, + Item { + item_type: Weapon, + cost: 40, + damage: 7, + armor: 0, + }, + Item { + item_type: Weapon, + cost: 74, + damage: 8, + armor: 0, + }, + // armor + Item { + item_type: Armor, + cost: 13, + damage: 0, + armor: 1, + }, + Item { + item_type: Armor, + cost: 31, + damage: 0, + armor: 2, + }, + Item { + item_type: Armor, + cost: 53, + damage: 0, + armor: 3, + }, + Item { + item_type: Armor, + cost: 75, + damage: 0, + armor: 4, + }, + Item { + item_type: Armor, + cost: 102, + damage: 0, + armor: 5, + }, + // rings + Item { + item_type: Ring, + cost: 25, + damage: 1, + armor: 0, + }, + Item { + item_type: Ring, + cost: 50, + damage: 2, + armor: 0, + }, + Item { + item_type: Ring, + cost: 100, + damage: 3, + armor: 0, + }, + Item { + item_type: Ring, + cost: 20, + damage: 0, + armor: 1, + }, + Item { + item_type: Ring, + cost: 40, + damage: 0, + armor: 2, + }, + Item { + item_type: Ring, + cost: 80, + damage: 0, + armor: 3, + }, +]; + +#[derive(Clone, Copy)] +struct Player { + hp: i32, + damage: i32, + armor: i32, +} + +impl Player { + fn alive(&self) -> bool { + self.hp > 0 + } +} + +#[derive(Debug)] +enum ItemType { + Weapon, + Armor, + Ring, +} + +#[derive(Debug)] +struct Item { + item_type: ItemType, + cost: i32, + damage: i32, + armor: i32, +} + +impl Item { + fn valid_selection(selection: &[&Item]) -> bool { + if selection + .iter() + .filter(|x| matches!(x.item_type, Weapon)) + .count() + != 1 + { + return false; + } + + if selection + .iter() + .filter(|x| matches!(x.item_type, Armor)) + .count() + > 1 + { + return false; + } + + if selection + .iter() + .filter(|x| matches!(x.item_type, Ring)) + .count() + > 2 + { + return false; + } + + true + } +} + +fn simulate(me: Player, items: &[&Item], mut boss: Player) -> bool { + let mut equiped_me = Player { + hp: me.hp, + damage: me.damage + items.iter().map(|x| x.damage).sum::(), + armor: me.armor + items.iter().map(|x| x.armor).sum::(), + }; + + while equiped_me.alive() && boss.alive() { + let mut my_damage = equiped_me.damage - boss.armor; + if my_damage < 1 { + my_damage = 1; + } + + let mut boss_damage = boss.damage - equiped_me.armor; + if boss_damage < 1 { + boss_damage = 1; + } + + equiped_me.hp -= boss_damage; + boss.hp -= my_damage; + } + + !boss.alive() +} +pub fn part_one(input: &str) -> Option { + let (hp, damage, armor) = input + .split('\n') + .map(|x| x.split(": ").last().unwrap().parse::().unwrap()) + .next_tuple() + .unwrap(); + let boss = Player { hp, damage, armor }; + let me = Player { + hp: 100, + damage: 0, + armor: 0, + }; + + (0..STORE.len()) + .map(|x| { + STORE + .iter() + .combinations(x) + .filter(|y| Item::valid_selection(y)) + .map(|y| { + ( + y.iter().map(|z| z.cost).sum::(), + simulate(me, &y, boss), + ) + }) + .filter(|(_, y)| *y) + .map(|(y, _)| y) + .collect_vec() + }) + .filter_map(|x| x.iter().min().copied()) + .min() +} +pub fn part_two(input: &str) -> Option { + let (hp, damage, armor) = input + .split('\n') + .map(|x| x.split(": ").last().unwrap().parse::().unwrap()) + .next_tuple() + .unwrap(); + let boss = Player { hp, damage, armor }; + let me = Player { + hp: 100, + damage: 0, + armor: 0, + }; + + (0..STORE.len()) + .map(|x| { + STORE + .iter() + .combinations(x) + .filter(|y| Item::valid_selection(y)) + .map(|y| { + ( + y.iter().map(|z| z.cost).sum::(), + !simulate(me, &y, boss), + ) + }) + .filter(|(_, y)| *y) + .map(|(y, _)| y) + .collect_vec() + }) + .filter_map(|x| x.iter().max().copied()) + .max() +} +fn main() { + let input = &aoc::read_file("inputs", 21); + aoc::solve!(1, part_one, input); + aoc::solve!(2, part_two, input); +} diff --git a/src/test_inputs/20.txt b/src/test_inputs/20.txt deleted file mode 100644 index e69de29..000000000