summaryrefslogtreecommitdiff
path: root/src/bin/day_1.rs
blob: 7c8f104dcbb9ba1c3efa3378ed4c89b3133808ec (plain)
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
96
97
use std::io;
use std::io::prelude::*;
use std::iter;
use std::iter::Sum;
use std::num::ParseIntError;
use std::ops::Add;
use std::str::FromStr;

fn main() {
    let stdin = io::stdin();

    let input = stdin
        .lock()
        .lines()
        .map(|l| l.unwrap().parse::<Module>().unwrap());

    dbg!(fuel_required(input));
}

// TODO: If this were a nice CLI program, it would probably have a switch to choose between these two rather than doing both?
fn fuel_required(it: impl Iterator<Item = Module>) -> (Fuel, Fuel) {
    it.map(|m| {
        (
            m.fuel_excluding_fuel_weight(),
            m.fuel_including_fuel_weight(),
        )
    })
    .fold((Fuel(0), Fuel(0)), |(sum_x, sum_y), (x, y)| {
        (sum_x + x, sum_y + y)
    })
}

struct Module {
    weight: Weight,
}

impl FromStr for Module {
    type Err = ParseIntError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Module {
            weight: Weight(s.parse()?),
        })
    }
}

impl Module {
    fn fuel_excluding_fuel_weight(&self) -> Fuel {
        self.weight.required_fuel()
    }

    fn fuel_including_fuel_weight(&self) -> Fuel {
        iter::successors(Some(self.weight.required_fuel()), |fuel| {
            if fuel.weight().is_zero() {
                None
            } else {
                Some(fuel.weight().required_fuel())
            }
        })
        .sum()
    }
}

struct Weight(u32);

impl Weight {
    fn is_zero(&self) -> bool {
        self.0 == 0
    }

    fn required_fuel(&self) -> Fuel {
        Fuel((self.0 / 3).saturating_sub(2))
    }
}

#[derive(Debug)]
struct Fuel(u32);

impl Fuel {
    fn weight(&self) -> Weight {
        Weight(self.0)
    }
}

impl Add for Fuel {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Fuel(self.0 + other.0)
    }
}

impl Sum for Fuel {
    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
        iter.fold(Fuel(0), Add::add)
    }
}