aoclib/
day_24.rs

1use std::fs;
2
3struct Subroutine {
4    should_pop: bool,
5    gate_offset: i64,
6    source_offset: i64,
7}
8
9const RADIX: u32 = 26;
10
11impl Subroutine {
12    fn from_lines(lines: &[&str]) -> Subroutine {
13        Subroutine {
14            should_pop: match lines[4].split_whitespace().nth(2).unwrap().parse().unwrap() {
15                1 => false,
16                RADIX => true,
17                _ => panic!("Unexpected value for b"),
18            },
19            gate_offset: lines[5].split_whitespace().nth(2).unwrap().parse().unwrap(),
20            source_offset: lines[15]
21                .split_whitespace()
22                .nth(2)
23                .unwrap()
24                .parse()
25                .unwrap(),
26        }
27    }
28
29    fn evaluate(&self, w: i64, z: i64) -> (i8, i64) {
30        let radix = RADIX as i64;
31        let should_push = ((z % radix) + self.gate_offset) != w;
32        match (self.should_pop, should_push) {
33            (false, false) => (0, z),
34            (false, true) => (1, z * radix + self.source_offset + w),
35            (true, false) => (-1, z / radix),
36            (true, true) => (0, z / radix * radix + self.source_offset + w),
37        }
38    }
39}
40
41fn _subroutines(text: &str) -> Vec<Subroutine> {
42    text.lines()
43        .collect::<Vec<&str>>()
44        .chunks(18)
45        .map(Subroutine::from_lines)
46        .collect()
47}
48
49fn _fmt_int(mut value: u64, radix: u32) -> String {
50    let mut chars = Vec::new();
51    while value != 0 {
52        chars.push(std::char::from_digit(value as u32 % radix, radix).unwrap());
53        value /= radix as u64;
54    }
55    chars.into_iter().rev().collect()
56}
57
58fn _first_valid(subroutines: &[Subroutine], old_z: i64, path: u64, digits: &[u64]) -> Option<u64> {
59    if subroutines.is_empty() {
60        if old_z == 0 {
61            return Some(path);
62        } else {
63            return None;
64        }
65    }
66
67    for digit in digits {
68        let (delta, new_z) = subroutines[0].evaluate(*digit as i64, old_z);
69        if subroutines[0].should_pop && 0 <= delta {
70            continue;
71        }
72        match _first_valid(&subroutines[1..], new_z, path * 10 + digit, digits) {
73            Some(result) => return Some(result),
74            None => continue,
75        }
76    }
77    None
78}
79
80pub fn part_1(input: &str) -> u64 {
81    let subroutines = _subroutines(input);
82    _first_valid(&subroutines[..], 0, 0, &[9, 8, 7, 6, 5, 4, 3, 2, 1][..]).unwrap()
83}
84
85pub fn part_2(input: &str) -> u64 {
86    let subroutines = _subroutines(input);
87    _first_valid(&subroutines[..], 0, 0, &[1, 2, 3, 4, 5, 6, 7, 8, 9][..]).unwrap()
88}
89
90fn _from_file<F, T>(func: F, stem: &str) -> T
91where
92    F: Fn(&str) -> T,
93{
94    func(&fs::read_to_string(format!("inputs/24/{}.txt", stem)).unwrap())
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn part_1_works_on_input() {
103        assert_eq!(_from_file(part_1, "input"), 41299994879959);
104    }
105
106    #[test]
107    fn part_2_works_on_input() {
108        assert_eq!(_from_file(part_2, "input"), 11189561113216);
109    }
110}