summaryrefslogtreecommitdiff
path: root/2022/src/bin/day_25.rs
blob: d55f30a8b42769bb1090db6962eb36d4c636e8a3 (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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use nom::{
    branch::alt,
    bytes::complete::tag,
    character::complete::line_ending,
    combinator::{map, value},
    multi::{many1, separated_list1},
    IResult,
};
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let input = fs::read_to_string("inputs/day_25.txt")?;
    let parsed = SnafuList::parser(&input).unwrap().1;
    dbg!(to_snafu(parsed.sum()));

    Ok(())
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct SnafuList(Vec<i64>);

impl SnafuList {
    fn parser(input: &str) -> IResult<&str, SnafuList> {
        map(
            separated_list1(
                line_ending,
                map(
                    many1(alt((
                        value(2, tag("2")),
                        value(1, tag("1")),
                        value(0, tag("0")),
                        value(-1, tag("-")),
                        value(-2, tag("=")),
                    ))),
                    |digits| {
                        let mut result: i64 = 0;
                        for digit in digits {
                            result *= 5;
                            result += digit;
                        }
                        result
                    },
                ),
            ),
            SnafuList,
        )(input)
    }

    fn sum(&self) -> i64 {
        self.0.iter().sum()
    }
}

fn to_snafu(mut remaining_value: i64) -> String {
    let mut result = String::new();

    let mut current_power = 5i64.pow(26);

    while current_power > 0 {
        let next_digit_range = {
            let mut next_digit_power = current_power / 5;
            let mut max_next_digit = 0;
            while next_digit_power > 0 {
                max_next_digit += 2 * next_digit_power;
                next_digit_power /= 5;
            }
            -max_next_digit..=max_next_digit
        };

        let (digit, digit_value) = [('=', -2), ('-', -1), ('0', 0), ('1', 1), ('2', 2)]
            .into_iter()
            .filter_map(|(digit, digit_value)| {
                let digit_value = digit_value * current_power;
                let remaining_if_digit_set = remaining_value - digit_value;
                if next_digit_range.contains(&remaining_if_digit_set) {
                    Some((digit, digit_value))
                } else {
                    None
                }
            })
            .next()
            .expect("No digit found");

        remaining_value -= digit_value;
        result.push(digit);

        current_power /= 5;
    }

    result.trim_start_matches("0").to_owned()
}

#[test]
fn round_trip_works() {
    let str_list = vec![
        "1=-0-2", "12111", "2=0=", "21", "2=01", "111", "20012", "112", "1=-1=", "1-12", "12",
        "1=", "122",
    ];
    let list = SnafuList::parser(&str_list.join("\n")).unwrap().1;

    assert_eq!(
        list,
        SnafuList(vec![
            1747, 906, 198, 11, 201, 31, 1257, 32, 353, 107, 7, 3, 37
        ])
    );

    for (i, num) in list.0.iter().enumerate() {
        assert_eq!(to_snafu(*num), str_list[i]);
    }
}