-
-
Notifications
You must be signed in to change notification settings - Fork 18
/
main.rs
124 lines (114 loc) · 3.77 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
// Used strategy from: https://is.gd/j9dCKv
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use nalgebra::{Matrix3, Rotation3, Vector3};
type Fp = (i32, i32);
pub fn main() {
let mut rots: HashSet<_> = [Matrix3::identity()].into_iter().collect();
[Vector3::x_axis(), Vector3::y_axis(), Vector3::z_axis()]
.iter()
.for_each(|axis| {
(0..4)
.map(|n| std::f32::consts::FRAC_PI_2 * n as f32)
.for_each(|angle| {
let r = Matrix3::from_iterator(
Rotation3::from_axis_angle(axis, angle)
.matrix()
.iter()
.map(|n| n.round() as i32),
);
rots.extend(rots.clone().into_iter().map(|m| r * m));
});
});
let scans = include_str!("../input.txt")
.split("\n\n")
.map(|s| {
s.lines()
.skip(1)
.map(|l| {
let mut c = l.splitn(3, ',').map(|c| c.parse().unwrap());
[c.next().unwrap(), c.next().unwrap(), c.next().unwrap()].into()
})
.collect()
})
.collect::<Vec<Vec<_>>>();
let mut scans = scans
.iter()
.map(|scan| {
(
scan,
scan.iter()
.tuple_combinations()
.map(|(a, b)| (fp(a, b), [a, b]))
.collect(),
)
})
.collect::<Vec<(_, Vec<_>)>>();
let first = scans.remove(0);
let mut scans_pos = vec![[0, 0, 0].into()];
let mut beacons = first.0.iter().cloned().collect();
let mut fps = HashMap::new();
add_scan_fps(&mut fps, first.0);
while !scans.is_empty() {
let (i, (pos, scan)) = scans
.iter()
.enumerate()
.flat_map(|(i, (s, s_fps))| matching(&beacons, &fps, &rots, s, s_fps).map(|m| (i, m)))
.next()
.unwrap();
scans.remove(i);
add_scan_fps(&mut fps, &scan);
beacons.extend(scan.into_iter());
scans_pos.push(pos);
}
println!(
"{}",
scans_pos
.into_iter()
.tuple_combinations()
.map(|(a, b)| (a - b).iter().map(|n| n.abs()).sum::<i32>())
.max()
.unwrap()
);
}
fn fp(a: &Vector3<i32>, b: &Vector3<i32>) -> Fp {
(
(a - b).iter().map(|n| n.abs()).sum(),
(a - b).iter().map(|n| n.abs()).max().unwrap(),
)
}
fn add_scan_fps(fps: &mut HashMap<Fp, Vec<[Vector3<i32>; 2]>>, scan: &[Vector3<i32>]) {
scan.iter().tuple_combinations().for_each(|(a, b)| {
fps.entry(fp(a, b)).or_default().push([*a, *b]);
})
}
fn matching(
beacons: &HashSet<Vector3<i32>>,
fps: &HashMap<Fp, Vec<[Vector3<i32>; 2]>>,
rots: &HashSet<Matrix3<i32>>,
scan: &[Vector3<i32>],
scan_fps: &[(Fp, [&Vector3<i32>; 2])],
) -> Option<(Vector3<i32>, Vec<Vector3<i32>>)> {
match scan_fps
.iter()
.filter(|(f, _)| fps.contains_key(f))
.take(12)
.collect::<Vec<_>>()
{
match_fps if match_fps.len() < 12 => None,
match_fps => match_fps
.into_iter()
.flat_map(|(fp, [ma, mb])| {
fps[fp]
.iter()
.flat_map(|[a, b]| {
rots.iter().find(|&m| a - m * *ma == b - m * *mb).map(|r| {
let t = a - r * *ma;
(t, scan.iter().map(|p| r * p + t).collect::<Vec<_>>())
})
})
.filter(|(_, s)| s.iter().filter(|b| beacons.contains(b)).take(2).count() >= 2)
})
.next(),
}
}