1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! # RPG Simulator 20XX
//!
//! The trick to get the outcome of each battle quickly is to divide the hero's health by the
//! boss's damage and vice-versa then find out how many turns each takes to win.
use crate::util::iter::*;
use crate::util::parse::*;
use std::ops::Add;

#[derive(Clone, Copy)]
struct Item {
    cost: i32,
    damage: i32,
    armor: i32,
}

impl Add for Item {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Item {
            cost: self.cost + rhs.cost,
            damage: self.damage + rhs.damage,
            armor: self.armor + rhs.armor,
        }
    }
}

type Result = (bool, i32);

pub fn parse(input: &str) -> Vec<Result> {
    let [boss_health, boss_damage, boss_armor]: [i32; 3] =
        input.iter_signed().chunk::<3>().next().unwrap();

    let weapon = [
        Item { cost: 8, damage: 4, armor: 0 },
        Item { cost: 10, damage: 5, armor: 0 },
        Item { cost: 25, damage: 6, armor: 0 },
        Item { cost: 40, damage: 7, armor: 0 },
        Item { cost: 74, damage: 8, armor: 0 },
    ];

    let armor = [
        Item { cost: 0, damage: 0, armor: 0 },
        Item { cost: 13, damage: 0, armor: 1 },
        Item { cost: 31, damage: 0, armor: 2 },
        Item { cost: 53, damage: 0, armor: 3 },
        Item { cost: 75, damage: 0, armor: 4 },
        Item { cost: 102, damage: 0, armor: 5 },
    ];

    let ring = [
        Item { cost: 25, damage: 1, armor: 0 },
        Item { cost: 50, damage: 2, armor: 0 },
        Item { cost: 100, damage: 3, armor: 0 },
        Item { cost: 20, damage: 0, armor: 1 },
        Item { cost: 40, damage: 0, armor: 2 },
        Item { cost: 80, damage: 0, armor: 3 },
    ];

    let mut combinations = Vec::with_capacity(22);
    combinations.push(Item { cost: 0, damage: 0, armor: 0 });

    for i in 0..6 {
        combinations.push(ring[i]);
        for j in (i + 1)..6 {
            combinations.push(ring[i] + ring[j]);
        }
    }

    let mut results = Vec::with_capacity(660);

    for first in weapon {
        for second in armor {
            for &third in &combinations {
                let Item { cost, damage, armor } = first + second + third;

                let hero_turns = boss_health / (damage - boss_armor).max(1);
                let boss_turns = 100 / (boss_damage - armor).max(1);
                let win = hero_turns <= boss_turns;

                results.push((win, cost));
            }
        }
    }

    results
}

pub fn part1(input: &[Result]) -> i32 {
    *input.iter().filter_map(|(w, c)| w.then_some(c)).min().unwrap()
}

pub fn part2(input: &[Result]) -> i32 {
    *input.iter().filter_map(|(w, c)| (!w).then_some(c)).max().unwrap()
}