Browse Source

Completed day 12

Tyler Hallada 4 years ago
parent
commit
b189630416
6 changed files with 585 additions and 0 deletions
  1. 143 0
      day12/Cargo.lock
  2. 12 0
      day12/Cargo.toml
  3. 4 0
      day12/input/input.txt
  4. 4 0
      day12/input/test1.txt
  5. 4 0
      day12/input/test2.txt
  6. 418 0
      day12/src/main.rs

+ 143 - 0
day12/Cargo.lock

@@ -0,0 +1,143 @@
1
+# This file is automatically @generated by Cargo.
2
+# It is not intended for manual editing.
3
+[[package]]
4
+name = "aho-corasick"
5
+version = "0.7.6"
6
+source = "registry+https://github.com/rust-lang/crates.io-index"
7
+dependencies = [
8
+ "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
9
+]
10
+
11
+[[package]]
12
+name = "autocfg"
13
+version = "1.0.0"
14
+source = "registry+https://github.com/rust-lang/crates.io-index"
15
+
16
+[[package]]
17
+name = "day12"
18
+version = "0.1.0"
19
+dependencies = [
20
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
21
+ "num 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
22
+ "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
23
+]
24
+
25
+[[package]]
26
+name = "lazy_static"
27
+version = "1.4.0"
28
+source = "registry+https://github.com/rust-lang/crates.io-index"
29
+
30
+[[package]]
31
+name = "memchr"
32
+version = "2.3.0"
33
+source = "registry+https://github.com/rust-lang/crates.io-index"
34
+
35
+[[package]]
36
+name = "num"
37
+version = "0.2.1"
38
+source = "registry+https://github.com/rust-lang/crates.io-index"
39
+dependencies = [
40
+ "num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
41
+ "num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
42
+ "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
43
+ "num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
44
+ "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
45
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
46
+]
47
+
48
+[[package]]
49
+name = "num-bigint"
50
+version = "0.2.5"
51
+source = "registry+https://github.com/rust-lang/crates.io-index"
52
+dependencies = [
53
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
54
+ "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
55
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
56
+]
57
+
58
+[[package]]
59
+name = "num-complex"
60
+version = "0.2.4"
61
+source = "registry+https://github.com/rust-lang/crates.io-index"
62
+dependencies = [
63
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
64
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
65
+]
66
+
67
+[[package]]
68
+name = "num-integer"
69
+version = "0.1.42"
70
+source = "registry+https://github.com/rust-lang/crates.io-index"
71
+dependencies = [
72
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
73
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
74
+]
75
+
76
+[[package]]
77
+name = "num-iter"
78
+version = "0.1.40"
79
+source = "registry+https://github.com/rust-lang/crates.io-index"
80
+dependencies = [
81
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
82
+ "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
83
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
84
+]
85
+
86
+[[package]]
87
+name = "num-rational"
88
+version = "0.2.3"
89
+source = "registry+https://github.com/rust-lang/crates.io-index"
90
+dependencies = [
91
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
92
+ "num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
93
+ "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
94
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
95
+]
96
+
97
+[[package]]
98
+name = "num-traits"
99
+version = "0.2.11"
100
+source = "registry+https://github.com/rust-lang/crates.io-index"
101
+dependencies = [
102
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
103
+]
104
+
105
+[[package]]
106
+name = "regex"
107
+version = "1.3.3"
108
+source = "registry+https://github.com/rust-lang/crates.io-index"
109
+dependencies = [
110
+ "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
111
+ "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
112
+ "regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
113
+ "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
114
+]
115
+
116
+[[package]]
117
+name = "regex-syntax"
118
+version = "0.6.13"
119
+source = "registry+https://github.com/rust-lang/crates.io-index"
120
+
121
+[[package]]
122
+name = "thread_local"
123
+version = "1.0.0"
124
+source = "registry+https://github.com/rust-lang/crates.io-index"
125
+dependencies = [
126
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
127
+]
128
+
129
+[metadata]
130
+"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
131
+"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
132
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
133
+"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
134
+"checksum num 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36"
135
+"checksum num-bigint 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f115de20ad793e857f76da2563ff4a09fbcfd6fe93cca0c5d996ab5f3ee38d"
136
+"checksum num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
137
+"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
138
+"checksum num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
139
+"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3"
140
+"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
141
+"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87"
142
+"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90"
143
+"checksum thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab"

+ 12 - 0
day12/Cargo.toml

@@ -0,0 +1,12 @@
1
+[package]
2
+name = "day12"
3
+version = "0.1.0"
4
+authors = ["Tyler Hallada <tyler@hallada.net>"]
5
+edition = "2018"
6
+
7
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
+
9
+[dependencies]
10
+lazy_static = "1.4.0"
11
+num = "0.2.1"
12
+regex = "1.3.3"

+ 4 - 0
day12/input/input.txt

@@ -0,0 +1,4 @@
1
+<x=3, y=2, z=-6>
2
+<x=-13, y=18, z=10>
3
+<x=-8, y=-1, z=13>
4
+<x=5, y=10, z=4>

+ 4 - 0
day12/input/test1.txt

@@ -0,0 +1,4 @@
1
+<x=-1, y=0, z=2>
2
+<x=2, y=-10, z=-7>
3
+<x=4, y=-8, z=8>
4
+<x=3, y=5, z=-1>

+ 4 - 0
day12/input/test2.txt

@@ -0,0 +1,4 @@
1
+<x=-8, y=-10, z=0>
2
+<x=5, y=5, z=10>
3
+<x=2, y=-7, z=3>
4
+<x=9, y=-8, z=-3>

+ 418 - 0
day12/src/main.rs

@@ -0,0 +1,418 @@
1
+#[macro_use]
2
+extern crate lazy_static;
3
+
4
+use std::collections::HashSet;
5
+use std::error::Error;
6
+use std::fs::File;
7
+use std::io::{prelude::*, BufReader};
8
+use std::ops::AddAssign;
9
+use std::ops::Index;
10
+use std::result;
11
+use std::str::FromStr;
12
+
13
+use num::integer::lcm;
14
+use regex::Regex;
15
+
16
+type Result<T> = result::Result<T, Box<dyn Error>>;
17
+
18
+const INPUT: &str = "input/input.txt";
19
+
20
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
21
+struct Vector {
22
+    x: i64,
23
+    y: i64,
24
+    z: i64,
25
+}
26
+
27
+#[derive(Debug, PartialEq, Eq, Hash, Clone)]
28
+struct Body {
29
+    position: Vector,
30
+    velocity: Vector,
31
+}
32
+
33
+#[derive(Debug, PartialEq, Eq, Hash, Clone)]
34
+struct NBody {
35
+    bodies: Vec<Body>,
36
+}
37
+
38
+impl FromStr for Body {
39
+    type Err = Box<dyn Error>;
40
+
41
+    fn from_str(s: &str) -> Result<Body> {
42
+        lazy_static! {
43
+            static ref RE: Regex =
44
+                Regex::new(r"<x=(?P<x>-?\d+), y=(?P<y>-?\d+), z=(?P<z>-?\d+)>").unwrap();
45
+        }
46
+
47
+        let captures = match RE.captures(s) {
48
+            None => {
49
+                return Err(From::from("Malformed scan, no positions could be found"));
50
+            }
51
+            Some(captures) => captures,
52
+        };
53
+
54
+        Ok(Body {
55
+            position: Vector {
56
+                x: captures["x"].parse()?,
57
+                y: captures["y"].parse()?,
58
+                z: captures["z"].parse()?,
59
+            },
60
+            velocity: Vector::new(),
61
+        })
62
+    }
63
+}
64
+
65
+impl Vector {
66
+    fn new() -> Vector {
67
+        Vector { x: 0, y: 0, z: 0 }
68
+    }
69
+}
70
+
71
+impl AddAssign for Vector {
72
+    fn add_assign(&mut self, other: Self) {
73
+        *self = Self {
74
+            x: self.x + other.x,
75
+            y: self.y + other.y,
76
+            z: self.z + other.z,
77
+        }
78
+    }
79
+}
80
+
81
+impl Index<&str> for Vector {
82
+    type Output = i64;
83
+
84
+    fn index(&self, index: &str) -> &i64 {
85
+        match index {
86
+            "x" => &self.x,
87
+            "y" => &self.y,
88
+            "z" => &self.z,
89
+            _ => panic!("unknown field: {}", index),
90
+        }
91
+    }
92
+}
93
+
94
+impl Body {
95
+    fn add_gravity(&self, gravity: &mut Vector, other: &Self) {
96
+        if self.position.x > other.position.x {
97
+            gravity.x -= 1;
98
+        } else if self.position.x < other.position.x {
99
+            gravity.x += 1;
100
+        }
101
+
102
+        if self.position.y > other.position.y {
103
+            gravity.y -= 1;
104
+        } else if self.position.y < other.position.y {
105
+            gravity.y += 1;
106
+        }
107
+
108
+        if self.position.z > other.position.z {
109
+            gravity.z -= 1;
110
+        } else if self.position.z < other.position.z {
111
+            gravity.z += 1;
112
+        }
113
+    }
114
+}
115
+
116
+impl NBody {
117
+    fn run_step(&mut self) {
118
+        let mut gravities = Vec::new();
119
+        for body in self.bodies.iter() {
120
+            let mut gravity = Vector::new();
121
+            for other_body in self.bodies.iter() {
122
+                body.add_gravity(&mut gravity, other_body);
123
+            }
124
+            gravities.push(gravity);
125
+        }
126
+
127
+        for (index, gravity) in gravities.into_iter().enumerate() {
128
+            self.bodies[index].velocity += gravity;
129
+            let velocity = self.bodies[index].velocity;
130
+            self.bodies[index].position += velocity;
131
+        }
132
+    }
133
+
134
+    fn total_energy(&self) -> i64 {
135
+        let mut total_energy = 0;
136
+        for body in self.bodies.iter() {
137
+            let potential_energy =
138
+                body.position.x.abs() + body.position.y.abs() + body.position.z.abs();
139
+            let kinetic_energy =
140
+                body.velocity.x.abs() + body.velocity.y.abs() + body.velocity.z.abs();
141
+            total_energy += potential_energy * kinetic_energy;
142
+        }
143
+        total_energy
144
+    }
145
+
146
+    fn state(&self, component: &str) -> [(i64, i64); 4] {
147
+        [
148
+            (
149
+                self.bodies[0].position[component],
150
+                self.bodies[0].velocity[component],
151
+            ),
152
+            (
153
+                self.bodies[1].position[component],
154
+                self.bodies[1].velocity[component],
155
+            ),
156
+            (
157
+                self.bodies[2].position[component],
158
+                self.bodies[2].velocity[component],
159
+            ),
160
+            (
161
+                self.bodies[3].position[component],
162
+                self.bodies[3].velocity[component],
163
+            ),
164
+        ]
165
+    }
166
+}
167
+
168
+fn read_moon_scan(filename: &str) -> Result<NBody> {
169
+    let file = File::open(filename)?;
170
+    let reader = BufReader::new(file);
171
+    let mut moons = vec![];
172
+
173
+    for line in reader.lines() {
174
+        moons.push(line?.parse()?);
175
+    }
176
+
177
+    Ok(NBody { bodies: moons })
178
+}
179
+
180
+fn solve_part1(filename: &str) -> Result<i64> {
181
+    let mut nbody = read_moon_scan(filename)?;
182
+    for _ in 0..1000 {
183
+        nbody.run_step();
184
+    }
185
+    Ok(nbody.total_energy())
186
+}
187
+
188
+fn solve_part2(filename: &str) -> Result<u64> {
189
+    let mut step_count = 0;
190
+    let mut x_states: HashSet<[(i64, i64); 4]> = HashSet::new();
191
+    let mut y_states: HashSet<[(i64, i64); 4]> = HashSet::new();
192
+    let mut z_states: HashSet<[(i64, i64); 4]> = HashSet::new();
193
+    let mut x_repeated_step_count = None;
194
+    let mut y_repeated_step_count = None;
195
+    let mut z_repeated_step_count = None;
196
+    let mut nbody = read_moon_scan(filename)?;
197
+    while x_repeated_step_count == None
198
+        || y_repeated_step_count == None
199
+        || z_repeated_step_count == None
200
+    {
201
+        if x_repeated_step_count == None {
202
+            let x_state = nbody.state("x");
203
+            if x_states.contains(&x_state) {
204
+                x_repeated_step_count = Some(step_count);
205
+            } else {
206
+                x_states.insert(x_state);
207
+            }
208
+        }
209
+
210
+        if y_repeated_step_count == None {
211
+            let y_state = nbody.state("y");
212
+            if y_states.contains(&y_state) {
213
+                y_repeated_step_count = Some(step_count);
214
+            } else {
215
+                y_states.insert(y_state);
216
+            }
217
+        }
218
+
219
+        if z_repeated_step_count == None {
220
+            let z_state = nbody.state("z");
221
+            if z_states.contains(&z_state) {
222
+                z_repeated_step_count = Some(step_count);
223
+            } else {
224
+                z_states.insert(z_state);
225
+            }
226
+        }
227
+
228
+        nbody.run_step();
229
+        step_count += 1;
230
+    }
231
+
232
+    Ok(lcm(
233
+        x_repeated_step_count.unwrap(),
234
+        lcm(
235
+            y_repeated_step_count.unwrap(),
236
+            z_repeated_step_count.unwrap(),
237
+        ),
238
+    ))
239
+}
240
+
241
+fn main() -> Result<()> {
242
+    println!("Part 1: {}", solve_part1(INPUT)?);
243
+    println!("Part 2: {}", solve_part2(INPUT)?);
244
+
245
+    Ok(())
246
+}
247
+
248
+#[cfg(test)]
249
+mod tests {
250
+    use super::*;
251
+
252
+    const TEST_INPUT1: &str = "input/test1.txt";
253
+    const TEST_INPUT2: &str = "input/test2.txt";
254
+    fn nbody_1() -> NBody {
255
+        NBody {
256
+            bodies: vec![
257
+                Body {
258
+                    position: Vector { x: -1, y: 0, z: 2 },
259
+                    velocity: Vector { x: 0, y: 0, z: 0 },
260
+                },
261
+                Body {
262
+                    position: Vector {
263
+                        x: 2,
264
+                        y: -10,
265
+                        z: -7,
266
+                    },
267
+                    velocity: Vector { x: 0, y: 0, z: 0 },
268
+                },
269
+                Body {
270
+                    position: Vector { x: 4, y: -8, z: 8 },
271
+                    velocity: Vector { x: 0, y: 0, z: 0 },
272
+                },
273
+                Body {
274
+                    position: Vector { x: 3, y: 5, z: -1 },
275
+                    velocity: Vector { x: 0, y: 0, z: 0 },
276
+                },
277
+            ],
278
+        }
279
+    }
280
+    fn nbody_1_after_10_steps() -> NBody {
281
+        NBody {
282
+            bodies: vec![
283
+                Body {
284
+                    position: Vector { x: 2, y: 1, z: -3 },
285
+                    velocity: Vector { x: -3, y: -2, z: 1 },
286
+                },
287
+                Body {
288
+                    position: Vector { x: 1, y: -8, z: 0 },
289
+                    velocity: Vector { x: -1, y: 1, z: 3 },
290
+                },
291
+                Body {
292
+                    position: Vector { x: 3, y: -6, z: 1 },
293
+                    velocity: Vector { x: 3, y: 2, z: -3 },
294
+                },
295
+                Body {
296
+                    position: Vector { x: 2, y: 0, z: 4 },
297
+                    velocity: Vector { x: 1, y: -1, z: -1 },
298
+                },
299
+            ],
300
+        }
301
+    }
302
+    fn nbody_2() -> NBody {
303
+        NBody {
304
+            bodies: vec![
305
+                Body {
306
+                    position: Vector {
307
+                        x: -8,
308
+                        y: -10,
309
+                        z: 0,
310
+                    },
311
+                    velocity: Vector { x: 0, y: 0, z: 0 },
312
+                },
313
+                Body {
314
+                    position: Vector { x: 5, y: 5, z: 10 },
315
+                    velocity: Vector { x: 0, y: 0, z: 0 },
316
+                },
317
+                Body {
318
+                    position: Vector { x: 2, y: -7, z: 3 },
319
+                    velocity: Vector { x: 0, y: 0, z: 0 },
320
+                },
321
+                Body {
322
+                    position: Vector { x: 9, y: -8, z: -3 },
323
+                    velocity: Vector { x: 0, y: 0, z: 0 },
324
+                },
325
+            ],
326
+        }
327
+    }
328
+    fn nbody_2_after_100_steps() -> NBody {
329
+        NBody {
330
+            bodies: vec![
331
+                Body {
332
+                    position: Vector {
333
+                        x: 8,
334
+                        y: -12,
335
+                        z: -9,
336
+                    },
337
+                    velocity: Vector { x: -7, y: 3, z: 0 },
338
+                },
339
+                Body {
340
+                    position: Vector {
341
+                        x: 13,
342
+                        y: 16,
343
+                        z: -3,
344
+                    },
345
+                    velocity: Vector {
346
+                        x: 3,
347
+                        y: -11,
348
+                        z: -5,
349
+                    },
350
+                },
351
+                Body {
352
+                    position: Vector {
353
+                        x: -29,
354
+                        y: -11,
355
+                        z: -1,
356
+                    },
357
+                    velocity: Vector { x: -3, y: 7, z: 4 },
358
+                },
359
+                Body {
360
+                    position: Vector {
361
+                        x: 16,
362
+                        y: -13,
363
+                        z: 23,
364
+                    },
365
+                    velocity: Vector { x: 7, y: 1, z: 1 },
366
+                },
367
+            ],
368
+        }
369
+    }
370
+
371
+    #[test]
372
+    fn reads_moon_scan_file() {
373
+        assert_eq!(read_moon_scan(TEST_INPUT1).unwrap(), nbody_1());
374
+        assert_eq!(read_moon_scan(TEST_INPUT2).unwrap(), nbody_2());
375
+    }
376
+
377
+    #[test]
378
+    fn runs_10_steps() {
379
+        let mut nbody = read_moon_scan(TEST_INPUT1).unwrap();
380
+        for _ in 0..10 {
381
+            nbody.run_step();
382
+        }
383
+        assert_eq!(nbody, nbody_1_after_10_steps());
384
+    }
385
+
386
+    #[test]
387
+    fn runs_100_steps() {
388
+        let mut nbody = read_moon_scan(TEST_INPUT2).unwrap();
389
+        for _ in 0..100 {
390
+            nbody.run_step();
391
+        }
392
+        assert_eq!(nbody, nbody_2_after_100_steps());
393
+    }
394
+
395
+    #[test]
396
+    fn calculates_total_energy_after_10_steps() {
397
+        let mut nbody = read_moon_scan(TEST_INPUT1).unwrap();
398
+        for _ in 0..10 {
399
+            nbody.run_step();
400
+        }
401
+        assert_eq!(nbody.total_energy(), 179);
402
+    }
403
+
404
+    #[test]
405
+    fn calculates_total_energy_after_100_steps() {
406
+        let mut nbody = read_moon_scan(TEST_INPUT2).unwrap();
407
+        for _ in 0..100 {
408
+            nbody.run_step();
409
+        }
410
+        assert_eq!(nbody.total_energy(), 1940);
411
+    }
412
+
413
+    #[test]
414
+    fn finds_repeated_states() {
415
+        assert_eq!(solve_part2(TEST_INPUT1).unwrap(), 2772);
416
+        assert_eq!(solve_part2(TEST_INPUT2).unwrap(), 4686774924);
417
+    }
418
+}