aoc/year2015/
day21.rs

1//! # RPG Simulator 20XX
2//!
3//! The trick to get the outcome of each battle quickly is to divide the hero's health by the
4//! boss's damage and vice-versa then find out how many turns each takes to win.
5use crate::util::iter::*;
6use crate::util::parse::*;
7use std::ops::Add;
8
9#[derive(Clone, Copy)]
10struct Item {
11    cost: u32,
12    damage: u32,
13    armor: u32,
14}
15
16impl Add for Item {
17    type Output = Self;
18
19    fn add(self, rhs: Self) -> Self {
20        Item {
21            cost: self.cost + rhs.cost,
22            damage: self.damage + rhs.damage,
23            armor: self.armor + rhs.armor,
24        }
25    }
26}
27
28type Input = (u32, u32);
29
30pub fn parse(input: &str) -> Input {
31    let [boss_health, boss_damage, boss_armor]: [u32; 3] =
32        input.iter_unsigned().chunk::<3>().next().unwrap();
33
34    let weapon = [
35        Item { cost: 8, damage: 4, armor: 0 },
36        Item { cost: 10, damage: 5, armor: 0 },
37        Item { cost: 25, damage: 6, armor: 0 },
38        Item { cost: 40, damage: 7, armor: 0 },
39        Item { cost: 74, damage: 8, armor: 0 },
40    ];
41
42    let armor = [
43        Item { cost: 0, damage: 0, armor: 0 },
44        Item { cost: 13, damage: 0, armor: 1 },
45        Item { cost: 31, damage: 0, armor: 2 },
46        Item { cost: 53, damage: 0, armor: 3 },
47        Item { cost: 75, damage: 0, armor: 4 },
48        Item { cost: 102, damage: 0, armor: 5 },
49    ];
50
51    let ring = [
52        Item { cost: 25, damage: 1, armor: 0 },
53        Item { cost: 50, damage: 2, armor: 0 },
54        Item { cost: 100, damage: 3, armor: 0 },
55        Item { cost: 20, damage: 0, armor: 1 },
56        Item { cost: 40, damage: 0, armor: 2 },
57        Item { cost: 80, damage: 0, armor: 3 },
58    ];
59
60    let mut combinations = Vec::with_capacity(22);
61    combinations.push(Item { cost: 0, damage: 0, armor: 0 });
62
63    for i in 0..6 {
64        combinations.push(ring[i]);
65        for j in (i + 1)..6 {
66            combinations.push(ring[i] + ring[j]);
67        }
68    }
69
70    let mut part_one = u32::MAX;
71    let mut part_two = u32::MIN;
72
73    for first in weapon {
74        for second in armor {
75            for &third in &combinations {
76                let Item { cost, damage, armor } = first + second + third;
77
78                let hero_hit = damage.saturating_sub(boss_armor).max(1);
79                let hero_turns = boss_health.div_ceil(hero_hit);
80                let boss_hit = boss_damage.saturating_sub(armor).max(1);
81                let boss_turns = 100_u32.div_ceil(boss_hit);
82                let win = hero_turns <= boss_turns;
83
84                if win {
85                    part_one = part_one.min(cost);
86                } else {
87                    part_two = part_two.max(cost);
88                }
89            }
90        }
91    }
92
93    (part_one, part_two)
94}
95
96pub fn part1(input: &Input) -> u32 {
97    input.0
98}
99
100pub fn part2(input: &Input) -> u32 {
101    input.1
102}