-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.rs
129 lines (112 loc) · 3.01 KB
/
main.rs
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Problem: https://adventofcode.com/2023/day/8
use std::collections::HashMap;
type Result = usize;
#[derive(Debug)]
struct Node {
left: String,
right: String,
}
#[derive(Debug, Hash, PartialEq, Eq)]
enum Dir {
R,
L,
}
type Input = (Vec<Dir>, HashMap<String, Node>);
fn parse_input(input: &str) -> Input {
let mut map = HashMap::new();
let (dirs, nodes) = input.trim().split_once("\n\n").unwrap();
for line in nodes.lines() {
let (id, r) = line.split_once(" = ").unwrap();
let (left, right) = r[1..r.len() - 1].split_once(", ").unwrap();
map.insert(
id.to_string(),
Node {
left: left.to_string(),
right: right.to_string(),
},
);
}
(
dirs.chars()
.map(|ch| match ch {
'R' => Dir::R,
'L' => Dir::L,
_ => panic!(),
})
.collect(),
map,
)
}
// ------------------------------------------
fn part1((dirs, map): &Input) -> Result {
let mut cur = "AAA".to_string();
let mut dirs = dirs.iter().cycle();
let mut count = 0;
while cur != "ZZZ" {
count += 1;
cur = match dirs.next().unwrap() {
Dir::R => map[&cur].right.clone(),
Dir::L => map[&cur].left.clone(),
};
}
count
}
#[test]
fn test_part1() {
let input = parse_input(include_str!("test1.txt"));
assert_eq!(part1(&input), 2);
let input = parse_input(include_str!("test2.txt"));
assert_eq!(part1(&input), 6);
}
// ------------------------------------------
fn steps_to_end(cur: &String, dirs: &[Dir], map: &HashMap<String, Node>) -> usize {
let mut dirs = dirs.iter().cycle();
let mut cur = cur.to_string();
let mut count = 0;
while !cur.ends_with('Z') {
count += 1;
let dir = dirs.next().unwrap();
cur = match dir {
Dir::R => map[&cur].right.clone(),
Dir::L => map[&cur].left.clone(),
};
}
count
}
fn lcm(first: usize, second: usize) -> usize {
first * second / gcd(first, second)
}
fn gcd(first: usize, second: usize) -> usize {
let mut max = first;
let mut min = second;
if min > max {
std::mem::swap(&mut max, &mut min);
}
loop {
let res = max % min;
if res == 0 {
return min;
}
max = min;
min = res;
}
}
fn part2((dirs, map): &Input) -> Result {
let starts = map.keys().filter(|k| k.ends_with('A')).collect::<Vec<_>>();
let ends = starts
.iter()
.map(|start| steps_to_end(start, dirs, map))
.collect::<Vec<_>>();
ends.iter().cloned().reduce(lcm).unwrap()
}
#[test]
fn test_part2() {
let input = parse_input(include_str!("test3.txt"));
assert_eq!(part2(&input), 6);
}
// ------------------------------------------
fn main() {
let input = parse_input(include_str!("input.txt"));
println!("Part 1: {:?}", part1(&input));
println!("Part 2: {:?}", part2(&input));
}