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}