|
@@ -0,0 +1,200 @@
|
|
1
|
+use std::collections::HashMap;
|
|
2
|
+use std::error::Error;
|
|
3
|
+use std::fs;
|
|
4
|
+use std::result;
|
|
5
|
+use std::str::FromStr;
|
|
6
|
+
|
|
7
|
+const INPUT: &str = "input/input.txt";
|
|
8
|
+
|
|
9
|
+type Result<T> = result::Result<T, Box<dyn Error>>;
|
|
10
|
+
|
|
11
|
+#[derive(Debug, PartialEq)]
|
|
12
|
+struct CrossedWires {
|
|
13
|
+ wires: Vec<Vec<Move>>,
|
|
14
|
+}
|
|
15
|
+
|
|
16
|
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
17
|
+struct Point {
|
|
18
|
+ x: i32,
|
|
19
|
+ y: i32,
|
|
20
|
+}
|
|
21
|
+
|
|
22
|
+#[derive(Debug, PartialEq)]
|
|
23
|
+struct Move {
|
|
24
|
+ direction: Direction,
|
|
25
|
+ distance: i32,
|
|
26
|
+}
|
|
27
|
+
|
|
28
|
+#[derive(Debug, PartialEq)]
|
|
29
|
+enum Direction {
|
|
30
|
+ Up,
|
|
31
|
+ Down,
|
|
32
|
+ Right,
|
|
33
|
+ Left,
|
|
34
|
+}
|
|
35
|
+
|
|
36
|
+impl CrossedWires {
|
|
37
|
+ fn find_intersections(&self) -> HashMap<Point, u32> {
|
|
38
|
+ let mut intersections: HashMap<Point, u32> = HashMap::new();
|
|
39
|
+
|
|
40
|
+ let mut occupied_points: HashMap<Point, u32> = HashMap::new();
|
|
41
|
+ for (wire_index, wire) in self.wires.iter().enumerate() {
|
|
42
|
+ let mut steps = 0;
|
|
43
|
+ let mut end_point = Point { x: 0, y: 0 };
|
|
44
|
+ for movement in wire.iter() {
|
|
45
|
+ let mut point = end_point.clone();
|
|
46
|
+ for _ in 0..movement.distance {
|
|
47
|
+ match movement.direction {
|
|
48
|
+ Direction::Up => point.y += 1,
|
|
49
|
+ Direction::Down => point.y -= 1,
|
|
50
|
+ Direction::Right => point.x += 1,
|
|
51
|
+ Direction::Left => point.x -= 1,
|
|
52
|
+ };
|
|
53
|
+ steps += 1;
|
|
54
|
+ if wire_index == 0 {
|
|
55
|
+ occupied_points.insert(point, steps);
|
|
56
|
+ } else {
|
|
57
|
+ if let Some(first_wire_steps) = occupied_points.get(&point) {
|
|
58
|
+ intersections.insert(point, first_wire_steps + steps);
|
|
59
|
+ }
|
|
60
|
+ }
|
|
61
|
+ }
|
|
62
|
+ end_point = point;
|
|
63
|
+ }
|
|
64
|
+ }
|
|
65
|
+
|
|
66
|
+ intersections
|
|
67
|
+ }
|
|
68
|
+}
|
|
69
|
+
|
|
70
|
+impl FromStr for CrossedWires {
|
|
71
|
+ type Err = Box<dyn Error>;
|
|
72
|
+
|
|
73
|
+ fn from_str(s: &str) -> Result<CrossedWires> {
|
|
74
|
+ let mut wires = s.split("\n");
|
|
75
|
+ let first_moves = wires.next().expect("First wire not found in input");
|
|
76
|
+ let second_moves = wires.next().expect("Second wire not found in input");
|
|
77
|
+
|
|
78
|
+ Ok(CrossedWires {
|
|
79
|
+ wires: vec![
|
|
80
|
+ get_moves_from_string(first_moves)?,
|
|
81
|
+ get_moves_from_string(second_moves)?,
|
|
82
|
+ ],
|
|
83
|
+ })
|
|
84
|
+ }
|
|
85
|
+}
|
|
86
|
+
|
|
87
|
+impl From<char> for Direction {
|
|
88
|
+ fn from(c: char) -> Direction {
|
|
89
|
+ match c {
|
|
90
|
+ 'U' => Direction::Up,
|
|
91
|
+ 'D' => Direction::Down,
|
|
92
|
+ 'R' => Direction::Right,
|
|
93
|
+ 'L' => Direction::Left,
|
|
94
|
+ _ => panic!("Could not parse direction: {}", c),
|
|
95
|
+ }
|
|
96
|
+ }
|
|
97
|
+}
|
|
98
|
+
|
|
99
|
+fn get_moves_from_string(moves_string: &str) -> Result<Vec<Move>> {
|
|
100
|
+ let moves_strings = moves_string.split(",");
|
|
101
|
+ let mut moves = vec![];
|
|
102
|
+
|
|
103
|
+ for wire_move in moves_strings {
|
|
104
|
+ let mut wire_move = wire_move.chars();
|
|
105
|
+ let direction: Direction =
|
|
106
|
+ Direction::from(wire_move.next().expect("Invalid empty wire move"));
|
|
107
|
+ let distance: i32 = wire_move.collect::<String>().parse()?;
|
|
108
|
+
|
|
109
|
+ moves.push(Move {
|
|
110
|
+ direction,
|
|
111
|
+ distance,
|
|
112
|
+ });
|
|
113
|
+ }
|
|
114
|
+ Ok(moves)
|
|
115
|
+}
|
|
116
|
+
|
|
117
|
+fn read_wires(filename: &str) -> Result<CrossedWires> {
|
|
118
|
+ let wires = fs::read_to_string(filename)?;
|
|
119
|
+ Ok(wires.parse()?)
|
|
120
|
+}
|
|
121
|
+
|
|
122
|
+fn solve_part1() -> Result<i32> {
|
|
123
|
+ let wires = read_wires(INPUT)?;
|
|
124
|
+ let intersections = wires.find_intersections();
|
|
125
|
+ let intersect_points = intersections.keys();
|
|
126
|
+ let distances = intersect_points.map(|point| point.x.abs() + point.y.abs());
|
|
127
|
+ Ok(distances.min().expect("No intersections found"))
|
|
128
|
+}
|
|
129
|
+
|
|
130
|
+fn solve_part2() -> Result<i32> {
|
|
131
|
+ let wires = read_wires(INPUT)?;
|
|
132
|
+ let intersections = wires.find_intersections();
|
|
133
|
+ let min_intersection = intersections
|
|
134
|
+ .iter()
|
|
135
|
+ .min_by_key(|(_, steps)| steps.clone()).expect("No intersections found");
|
|
136
|
+ Ok(*min_intersection.1 as i32)
|
|
137
|
+}
|
|
138
|
+
|
|
139
|
+fn main() -> Result<()> {
|
|
140
|
+ println!("Part 1: {}", solve_part1()?);
|
|
141
|
+ println!("Part 2: {}", solve_part2()?);
|
|
142
|
+
|
|
143
|
+ Ok(())
|
|
144
|
+}
|
|
145
|
+
|
|
146
|
+#[cfg(test)]
|
|
147
|
+mod tests {
|
|
148
|
+ use super::*;
|
|
149
|
+
|
|
150
|
+ const TEST_INPUT1: &str = "input/test1.txt";
|
|
151
|
+ // const TEST_INPUT2: &str = "input/test2.txt";
|
|
152
|
+ // const TEST_INPUT3: &str = "input/test3.txt";
|
|
153
|
+
|
|
154
|
+ #[test]
|
|
155
|
+ fn reads_wires() {
|
|
156
|
+ assert_eq!(
|
|
157
|
+ read_wires(TEST_INPUT1).unwrap(),
|
|
158
|
+ CrossedWires {
|
|
159
|
+ wires: vec![
|
|
160
|
+ vec![
|
|
161
|
+ Move {
|
|
162
|
+ direction: Direction::Right,
|
|
163
|
+ distance: 8
|
|
164
|
+ },
|
|
165
|
+ Move {
|
|
166
|
+ direction: Direction::Up,
|
|
167
|
+ distance: 5
|
|
168
|
+ },
|
|
169
|
+ Move {
|
|
170
|
+ direction: Direction::Left,
|
|
171
|
+ distance: 5
|
|
172
|
+ },
|
|
173
|
+ Move {
|
|
174
|
+ direction: Direction::Down,
|
|
175
|
+ distance: 3
|
|
176
|
+ },
|
|
177
|
+ ],
|
|
178
|
+ vec![
|
|
179
|
+ Move {
|
|
180
|
+ direction: Direction::Up,
|
|
181
|
+ distance: 7
|
|
182
|
+ },
|
|
183
|
+ Move {
|
|
184
|
+ direction: Direction::Right,
|
|
185
|
+ distance: 6
|
|
186
|
+ },
|
|
187
|
+ Move {
|
|
188
|
+ direction: Direction::Down,
|
|
189
|
+ distance: 4
|
|
190
|
+ },
|
|
191
|
+ Move {
|
|
192
|
+ direction: Direction::Left,
|
|
193
|
+ distance: 4
|
|
194
|
+ },
|
|
195
|
+ ],
|
|
196
|
+ ],
|
|
197
|
+ }
|
|
198
|
+ );
|
|
199
|
+ }
|
|
200
|
+}
|