From eba60c4c97116b96b683a55d61d184b4e5447b41 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 30 Oct 2024 23:29:32 +0100 Subject: [PATCH] day 21 --- src/day21.rs | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 162 insertions(+) create mode 100644 src/day21.rs diff --git a/src/day21.rs b/src/day21.rs new file mode 100644 index 0000000..70ec190 --- /dev/null +++ b/src/day21.rs @@ -0,0 +1,161 @@ +use std::cmp::max; + +use aoc_runner_derive::{aoc, aoc_generator}; + +#[aoc_generator(day21)] +fn parse(input: &str) -> Fighter { + let ints: Vec = input + .lines() + .map(|line| line.split_once(": ").unwrap().1) + .map(|it| it.parse().unwrap()) + .collect(); + Fighter { + health: ints[0], + damage: ints[1], + armor: ints[2], + } +} + +#[derive(Clone, Copy, Debug)] +struct Fighter { + health: i32, + damage: i32, + armor: i32, +} + +#[derive(Clone, Copy)] +struct Item { + cost: i32, + damage: i32, + armor: i32, +} + +impl Item { + fn new(cost: i32, damage: i32, armor: i32) -> Self { + Self { + cost, + damage, + armor, + } + } +} +#[aoc(day21, part1)] +fn part1(boss: &Fighter) -> usize { + combinations() + .into_iter() + .filter(|(fighter, _)| fight(*fighter, *boss)) + .map(|(_, cost)| cost) + // .count() + .min() + .unwrap() as usize +} + +/// returns `true` if `fighter` wins the fight +fn fight(mut fighter: Fighter, mut boss: Fighter) -> bool { + loop { + boss.health -= max(1, fighter.damage - boss.armor); + if boss.health < 1 { + return true; + } + fighter.health -= max(1, boss.damage - fighter.armor); + if fighter.health < 1 { + return false; + } + } +} + +fn combinations() -> Vec<(Fighter, i32)> { + let (weapons, armors, rings) = items(); + let mut all_rings = vec![Item::new(0, 0, 0)]; + for r1 in 0..rings.len() { + let ring1 = rings[r1]; + all_rings.push(ring1); + for ring2 in rings.iter().skip(r1 + 1) { + all_rings.push(Item::new( + ring1.cost + ring2.cost, + ring1.damage + ring2.damage, + ring1.armor + ring2.armor, + )); + } + } + let mut result = vec![]; + + for weapon in weapons { + for &armor in &armors { + for &ring in &all_rings { + let cost = weapon.cost + armor.cost + ring.cost; + let health = 100; + let damage = weapon.damage + ring.damage; + let armor = armor.armor + ring.armor; + result.push(( + Fighter { + health, + damage, + armor, + }, + cost, + )); + } + } + } + + result +} + +#[aoc(day21, part2)] +fn part2(boss: &Fighter) -> usize { + combinations() + .into_iter() + .filter(|(fighter, _)| !fight(*fighter, *boss)) + .map(|(_, cost)| cost) + .max() + .unwrap() as usize +} + +fn items() -> (Vec, Vec, Vec) { + let weapons = vec![ + Item::new(8, 4, 0), + Item::new(10, 5, 0), + Item::new(25, 6, 0), + Item::new(40, 7, 0), + Item::new(74, 8, 0), + ]; + let armors = vec![ + Item::new(0, 0, 0), // because we need the option to have no armor + Item::new(13, 0, 1), + Item::new(31, 0, 2), + Item::new(53, 0, 3), + Item::new(75, 0, 4), + Item::new(102, 0, 5), + ]; + let rings = vec![ + Item::new(25, 1, 0), + Item::new(50, 2, 0), + Item::new(100, 3, 0), + Item::new(20, 0, 1), + Item::new(40, 0, 2), + Item::new(80, 0, 3), + ]; + (weapons, armors, rings) +} + +#[cfg(test)] +mod test { + use crate::day21::{fight, Fighter}; + + #[test] + fn test_case() { + assert!(fight( + Fighter { + health: 8, + damage: 5, + armor: 5, + }, + Fighter { + health: 12, + damage: 7, + armor: 2, + } + )) + } +} diff --git a/src/lib.rs b/src/lib.rs index d7d8d10..bd1adc8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,5 +13,6 @@ mod day17; mod day18; mod day19; mod day20; +mod day21; aoc_lib! { year = 2015 }