aoclib/
day_25.rs

1use std::fs;
2
3use hashbrown::HashSet;
4use itertools::Itertools;
5
6type Herd = HashSet<(usize, usize)>;
7
8fn _herds(text: &str) -> (Herd, Herd) {
9    let mut east = HashSet::new();
10    let mut south = HashSet::new();
11    for (row, line) in text.lines().enumerate() {
12        for (col, cell) in line.trim().chars().enumerate() {
13            match cell {
14                '>' => {
15                    east.insert((row, col));
16                }
17                'v' => {
18                    south.insert((row, col));
19                }
20                '.' => (),
21                _ => panic!("Unexpected cell {}", cell),
22            }
23        }
24    }
25    (east, south)
26}
27
28fn _fmt_herds(east: &Herd, south: &Herd, height: usize, width: usize) -> String {
29    (0..height)
30        .map(|row| {
31            (0..width)
32                .map(|col| {
33                    if east.contains(&(row, col)) {
34                        '>'
35                    } else if south.contains(&(row, col)) {
36                        'v'
37                    } else {
38                        '.'
39                    }
40                })
41                .collect::<String>()
42        })
43        .join("\n")
44}
45
46fn _new_herds(old_east: &Herd, old_south: &Herd, height: usize, width: usize) -> (Herd, Herd) {
47    let mut new_east = HashSet::with_capacity(old_east.len());
48    for old_k in old_east {
49        let new_k = (old_k.0, (old_k.1 + 1) % width);
50        new_east.insert({
51            if old_east.contains(&new_k) || old_south.contains(&new_k) {
52                *old_k
53            } else {
54                new_k
55            }
56        });
57    }
58    let mut new_south = HashSet::with_capacity(old_south.len());
59    for old_k in old_south {
60        let new_k = ((old_k.0 + 1) % height, old_k.1);
61        new_south.insert({
62            if new_east.contains(&new_k) || old_south.contains(&new_k) {
63                *old_k
64            } else {
65                new_k
66            }
67        });
68    }
69    (new_east, new_south)
70}
71fn _num_herds(east: Herd, south: Herd) -> usize {
72    let height = *east
73        .iter()
74        .chain(south.iter())
75        .map(|(r, _)| r)
76        .max()
77        .unwrap()
78        + 1;
79    let width = *east
80        .iter()
81        .chain(south.iter())
82        .map(|(_, c)| c)
83        .max()
84        .unwrap()
85        + 1;
86
87    let mut herds = [(east, south), (HashSet::new(), HashSet::new())];
88    for herd_num in 1.. {
89        let old = &herds[(herd_num + 1) % 2];
90        let new = _new_herds(&old.0, &old.1, height, width);
91
92        if old.0 == new.0 && old.1 == new.1 {
93            return herd_num;
94        }
95        herds[herd_num % 2] = new;
96    }
97    panic!("Oups")
98}
99
100pub fn part_1(input: &str) -> u64 {
101    let (east, south) = _herds(input);
102    _num_herds(east, south) as u64
103}
104
105fn _from_file<F, T>(func: F, stem: &str) -> T
106where
107    F: Fn(&str) -> T,
108{
109    func(&fs::read_to_string(format!("inputs/25/{}.txt", stem)).unwrap())
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn part_1_works_on_example() {
118        assert_eq!(_from_file(part_1, "example"), 58);
119    }
120
121    #[test]
122    fn part_1_works_on_input() {
123        assert_eq!(_from_file(part_1, "input"), 516);
124    }
125}