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: i32,
12    damage: i32,
13    armor: i32,
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 Result = (bool, i32);
29
30pub fn parse(input: &str) -> Vec<Result> {
31    let [boss_health, boss_damage, boss_armor]: [i32; 3] =
32        input.iter_signed().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 results = Vec::with_capacity(660);
71
72    for first in weapon {
73        for second in armor {
74            for &third in &combinations {
75                let Item { cost, damage, armor } = first + second + third;
76
77                let hero_turns = boss_health / (damage - boss_armor).max(1);
78                let boss_turns = 100 / (boss_damage - armor).max(1);
79                let win = hero_turns <= boss_turns;
80
81                results.push((win, cost));
82            }
83        }
84    }
85
86    results
87}
88
89pub fn part1(input: &[Result]) -> i32 {
90    *input.iter().filter_map(|(w, c)| w.then_some(c)).min().unwrap()
91}
92
93pub fn part2(input: &[Result]) -> i32 {
94    *input.iter().filter_map(|(w, c)| (!w).then_some(c)).max().unwrap()
95}