|
@@ -1,7 +1,9 @@
|
1
|
|
-use std::collections::HashMap;
|
|
1
|
+use std::cmp::Ordering;
|
|
2
|
+use std::collections::{HashMap, VecDeque};
|
2
|
3
|
use std::error::Error;
|
3
|
4
|
use std::fs::File;
|
4
|
5
|
use std::io::{prelude::*, BufReader};
|
|
6
|
+use std::iter::FromIterator;
|
5
|
7
|
use std::result;
|
6
|
8
|
|
7
|
9
|
use num::integer::gcd;
|
|
@@ -22,47 +24,65 @@ struct AsteroidField {
|
22
|
24
|
}
|
23
|
25
|
|
24
|
26
|
impl AsteroidField {
|
25
|
|
- fn find_monitoring_station(&self) -> (&Point, usize) {
|
26
|
|
- let mut asteroid_detect_scores = HashMap::new();
|
|
27
|
+ fn get_lines_of_sight(&self, from_point: &Point) -> HashMap<(i32, i32), VecDeque<&Point>> {
|
|
28
|
+ let mut lines_of_sight: HashMap<(i32, i32), VecDeque<&Point>> = HashMap::new();
|
27
|
29
|
for asteroid in self.asteroids.iter() {
|
28
|
|
- let mut lines_of_sight: HashMap<(i32, i32), Point> = HashMap::new();
|
29
|
|
- for other in self.asteroids.iter() {
|
30
|
|
- if asteroid != other {
|
31
|
|
- let x_dist: i32 = other.x as i32 - asteroid.x as i32;
|
32
|
|
- let y_dist: i32 = other.y as i32 - asteroid.y as i32;
|
33
|
|
- let mut x_ratio: i32 = 0;
|
34
|
|
- let mut y_ratio: i32 = 0;
|
35
|
|
- if x_dist == 0 {
|
36
|
|
- if y_dist > 0 {
|
37
|
|
- y_ratio = 1;
|
38
|
|
- } else {
|
39
|
|
- y_ratio = -1;
|
40
|
|
- }
|
41
|
|
- } else if y_dist == 0 {
|
42
|
|
- if x_dist > 0 {
|
43
|
|
- x_ratio = 1;
|
44
|
|
- } else {
|
45
|
|
- x_ratio = -1;
|
46
|
|
- }
|
|
30
|
+ if from_point != asteroid {
|
|
31
|
+ let x_dist: i32 = asteroid.x as i32 - from_point.x as i32;
|
|
32
|
+ let y_dist: i32 = asteroid.y as i32 - from_point.y as i32;
|
|
33
|
+ let mut x_ratio: i32 = 0;
|
|
34
|
+ let mut y_ratio: i32 = 0;
|
|
35
|
+ if x_dist == 0 {
|
|
36
|
+ if y_dist > 0 {
|
|
37
|
+ y_ratio = 1;
|
|
38
|
+ } else {
|
|
39
|
+ y_ratio = -1;
|
|
40
|
+ }
|
|
41
|
+ } else if y_dist == 0 {
|
|
42
|
+ if x_dist > 0 {
|
|
43
|
+ x_ratio = 1;
|
47
|
44
|
} else {
|
48
|
|
- let gcd = gcd(x_dist, y_dist);
|
49
|
|
- x_ratio = x_dist / gcd;
|
50
|
|
- y_ratio = y_dist / gcd;
|
|
45
|
+ x_ratio = -1;
|
51
|
46
|
}
|
52
|
|
- lines_of_sight
|
53
|
|
- .entry((x_ratio, y_ratio))
|
54
|
|
- .and_modify(|current| {
|
55
|
|
- if (current.x as i32 - asteroid.x as i32).abs() > x_dist.abs()
|
56
|
|
- && (current.y as i32 - asteroid.y as i32).abs() > y_dist.abs()
|
|
47
|
+ } else {
|
|
48
|
+ let gcd = gcd(x_dist, y_dist);
|
|
49
|
+ x_ratio = x_dist / gcd;
|
|
50
|
+ y_ratio = y_dist / gcd;
|
|
51
|
+ }
|
|
52
|
+
|
|
53
|
+ lines_of_sight
|
|
54
|
+ .entry((x_ratio, y_ratio))
|
|
55
|
+ .and_modify(|deque| {
|
|
56
|
+ let mut insertion_index = None;
|
|
57
|
+ for (index, current) in deque.iter().enumerate() {
|
|
58
|
+ if (current.x as i32 - from_point.x as i32).abs() > x_dist.abs()
|
|
59
|
+ && (current.y as i32 - from_point.y as i32).abs() > y_dist.abs()
|
57
|
60
|
{
|
58
|
|
- current.x = other.x;
|
59
|
|
- current.y = other.y;
|
|
61
|
+ insertion_index = Some(index);
|
|
62
|
+ break;
|
60
|
63
|
}
|
61
|
|
- })
|
62
|
|
- .or_insert(other.clone());
|
63
|
|
- }
|
|
64
|
+ }
|
|
65
|
+ if let Some(index) = insertion_index {
|
|
66
|
+ deque.insert(index, asteroid);
|
|
67
|
+ } else {
|
|
68
|
+ deque.push_back(asteroid);
|
|
69
|
+ }
|
|
70
|
+ })
|
|
71
|
+ .or_insert_with(|| {
|
|
72
|
+ let mut deque = VecDeque::new();
|
|
73
|
+ deque.push_back(asteroid);
|
|
74
|
+ deque
|
|
75
|
+ });
|
64
|
76
|
}
|
|
77
|
+ }
|
|
78
|
+ lines_of_sight
|
|
79
|
+ }
|
65
|
80
|
|
|
81
|
+ fn find_monitoring_station(&self) -> (&Point, usize) {
|
|
82
|
+ let mut asteroid_detect_scores = HashMap::new();
|
|
83
|
+
|
|
84
|
+ for asteroid in self.asteroids.iter() {
|
|
85
|
+ let lines_of_sight = self.get_lines_of_sight(asteroid);
|
66
|
86
|
asteroid_detect_scores.insert(asteroid, lines_of_sight.len());
|
67
|
87
|
}
|
68
|
88
|
|
|
@@ -71,6 +91,48 @@ impl AsteroidField {
|
71
|
91
|
.max_by_key(|score| score.1)
|
72
|
92
|
.expect("No asteroid detect scores")
|
73
|
93
|
}
|
|
94
|
+
|
|
95
|
+ fn vaporize_asteroids(&mut self, laser_point: &Point) -> Option<&Point> {
|
|
96
|
+ let mut vaporized_counter = 0;
|
|
97
|
+ let mut lines_of_sight = self.get_lines_of_sight(laser_point);
|
|
98
|
+ let mut directions: Vec<(i32, i32)> = lines_of_sight.keys().map(|key| *key).collect();
|
|
99
|
+ directions.sort_by(|a, b| {
|
|
100
|
+ let det = a.0 * b.1 - b.0 * a.1;
|
|
101
|
+ if det > 0 {
|
|
102
|
+ Ordering::Less
|
|
103
|
+ } else if det < 0 {
|
|
104
|
+ Ordering::Greater
|
|
105
|
+ } else {
|
|
106
|
+ Ordering::Equal
|
|
107
|
+ }
|
|
108
|
+ });
|
|
109
|
+ let up = directions
|
|
110
|
+ .iter()
|
|
111
|
+ .position(|&dir| dir == (0, -1))
|
|
112
|
+ .expect("No asteroid directly up from laser");
|
|
113
|
+ directions.rotate_left(up);
|
|
114
|
+ dbg!(&directions);
|
|
115
|
+
|
|
116
|
+ for direction in directions.iter() {
|
|
117
|
+ // dbg!(direction);
|
|
118
|
+ let in_sight = lines_of_sight.get_mut(direction);
|
|
119
|
+ if let Some(in_sight) = in_sight {
|
|
120
|
+ // dbg!(&in_sight);
|
|
121
|
+ if let Some(vaporized_asteroid) = in_sight.pop_back() {
|
|
122
|
+ vaporized_counter += 1;
|
|
123
|
+
|
|
124
|
+ // dbg!(&vaporized_counter);
|
|
125
|
+ // dbg!(&vaporized_asteroid);
|
|
126
|
+
|
|
127
|
+ if vaporized_counter == 200 {
|
|
128
|
+ return Some(vaporized_asteroid);
|
|
129
|
+ }
|
|
130
|
+ }
|
|
131
|
+ }
|
|
132
|
+ }
|
|
133
|
+
|
|
134
|
+ None
|
|
135
|
+ }
|
74
|
136
|
}
|
75
|
137
|
|
76
|
138
|
fn read_asteroid_field(filename: &str) -> Result<AsteroidField> {
|
|
@@ -94,8 +156,10 @@ fn solve_part1() -> Result<usize> {
|
94
|
156
|
Ok(asteroid_field.find_monitoring_station().1)
|
95
|
157
|
}
|
96
|
158
|
|
97
|
|
-fn solve_part2() -> Result<i64> {
|
98
|
|
- Ok(1)
|
|
159
|
+fn solve_part2() -> Result<usize> {
|
|
160
|
+ let mut asteroid_field = read_asteroid_field("input/test5.txt")?;
|
|
161
|
+ let vaporized200 = asteroid_field.vaporize_asteroids(&Point { x: 11, y: 13 }).unwrap();
|
|
162
|
+ Ok(vaporized200.x * 100 + vaporized200.y)
|
99
|
163
|
}
|
100
|
164
|
|
101
|
165
|
fn main() -> Result<()> {
|