aoclib/
day_20.rs

1use hashbrown::HashMap;
2use itertools::Itertools;
3use std::fs;
4
5type Img = HashMap<(i32, i32), bool>;
6type Key = (bool, bool, bool, bool, bool, bool, bool, bool, bool);
7type Lut = HashMap<Key, bool>;
8
9fn _key(int: usize) -> Key {
10    assert!(int < 512);
11    (
12        int & 0b100000000 != 0,
13        int & 0b010000000 != 0,
14        int & 0b001000000 != 0,
15        int & 0b000100000 != 0,
16        int & 0b000010000 != 0,
17        int & 0b000001000 != 0,
18        int & 0b000000100 != 0,
19        int & 0b000000010 != 0,
20        int & 0b000000001 != 0,
21    )
22}
23
24fn _pixel(ch: char) -> bool {
25    match ch {
26        '.' => false,
27        '#' => true,
28        _ => panic!("Unexpected char '{}'", ch),
29    }
30}
31fn _img(text: &str) -> Img {
32    let mut result = HashMap::new();
33    let mut lines = text.lines();
34    lines.next();
35    lines.next();
36    for (r, line) in lines.enumerate() {
37        for (c, cell) in line.chars().enumerate() {
38            result.insert((r as i32, c as i32), _pixel(cell));
39        }
40    }
41
42    result
43}
44
45fn _lut(text: &str) -> HashMap<Key, bool> {
46    let mut lines = text.lines();
47    let line = lines.next().unwrap();
48    line.chars()
49        .enumerate()
50        .map(|(i, ch)| (_key(i), _pixel(ch)))
51        .collect()
52}
53fn _once_enhanced(img: &Img, lut: &Lut, padding: bool, min: i32, max: i32) -> Img {
54    let mut result = HashMap::with_capacity((max - min + 3).pow(2) as usize);
55    for r in min - 1..=max + 1 {
56        for c in min - 1..=max + 1 {
57            let key: Key = (
58                *img.get(&(r - 1, c - 1)).unwrap_or(&padding),
59                *img.get(&(r - 1, c)).unwrap_or(&padding),
60                *img.get(&(r - 1, c + 1)).unwrap_or(&padding),
61                *img.get(&(r, c - 1)).unwrap_or(&padding),
62                *img.get(&(r, c)).unwrap_or(&padding),
63                *img.get(&(r, c + 1)).unwrap_or(&padding),
64                *img.get(&(r + 1, c - 1)).unwrap_or(&padding),
65                *img.get(&(r + 1, c)).unwrap_or(&padding),
66                *img.get(&(r + 1, c + 1)).unwrap_or(&padding),
67            );
68            result.insert((r, c), *lut.get(&key).unwrap());
69        }
70    }
71    result
72}
73
74fn _multi_enhanced(img: &Img, lut: &Lut, num_round: usize) -> Img {
75    let odd = *lut
76        .get(&(
77            false, false, false, false, false, false, false, false, false,
78        ))
79        .unwrap();
80    let even = match odd {
81        false => false,
82        true => *lut
83            .get(&(true, true, true, true, true, true, true, true, true))
84            .unwrap(),
85    };
86
87    let (min, max) = match img.keys().map(|k| k.0).minmax() {
88        itertools::MinMaxResult::NoElements => panic!("No elements"),
89        itertools::MinMaxResult::OneElement(only) => (only, only),
90        itertools::MinMaxResult::MinMax(min, max) => (min, max),
91    };
92
93    let mut result = _once_enhanced(img, lut, even, min, max);
94    for i in 1..num_round as i32 {
95        result = _once_enhanced(
96            &result,
97            lut,
98            match i % 2 {
99                0 => even,
100                1 => odd,
101                _ => panic!("Oups"),
102            },
103            min - i,
104            max + i,
105        );
106    }
107    result
108}
109
110pub fn part_1(input: &str) -> u64 {
111    let n = 2;
112    let img = _img(input);
113    let lut = _lut(input);
114    let enhanced = _multi_enhanced(&img, &lut, n);
115    enhanced.values().map(|b| *b as u64).sum()
116}
117
118pub fn part_2(input: &str) -> u64 {
119    let n = 50;
120    let img = _img(input);
121    let lut = _lut(input);
122    let enhanced = _multi_enhanced(&img, &lut, n);
123    enhanced.values().map(|b| *b as u64).sum()
124}
125
126fn _from_file<F, T>(func: F, stem: &str) -> T
127where
128    F: Fn(&str) -> T,
129{
130    func(&fs::read_to_string(format!("inputs/20/{}.txt", stem)).unwrap())
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn part_1_works_on_example() {
139        assert_eq!(_from_file(part_1, "example"), 35);
140    }
141
142    #[test]
143    fn part_1_works_on_input() {
144        assert_eq!(_from_file(part_1, "input"), 5571);
145    }
146
147    #[test]
148    fn part_2_works_on_example() {
149        assert_eq!(_from_file(part_2, "example"), 3351);
150    }
151
152    #[test]
153    fn part_2_works_on_input() {
154        assert_eq!(_from_file(part_2, "input"), 17965);
155    }
156}