mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Use pre-matching strategy
Ensure that both scanners to be matched have a set of enough distances in common to avoid matching between groups that cannot possibly be related.
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
|
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
@@ -23,6 +25,10 @@ impl Point3 {
|
|||||||
pub fn manhattan(&self) -> i32 {
|
pub fn manhattan(&self) -> i32 {
|
||||||
self.0.into_iter().map(i32::abs).sum()
|
self.0.into_iter().map(i32::abs).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn euler_square(&self) -> i32 {
|
||||||
|
self.0.into_iter().map(|c| c * c).sum()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub for Point3 {
|
impl Sub for Point3 {
|
||||||
@@ -49,6 +55,44 @@ impl Add for Point3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Scanner {
|
||||||
|
visible: Vec<Point3>,
|
||||||
|
distances: HashMap<i32, (Point3, Point3)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scanner {
|
||||||
|
pub fn new(visible: Vec<Point3>) -> Self {
|
||||||
|
let distances = visible
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(skip, &a)| {
|
||||||
|
visible[(skip + 1)..]
|
||||||
|
.iter()
|
||||||
|
.map(move |&b| ((a - b).euler_square(), (a, b)))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Self { visible, distances }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_overlap(&self, other: &Self) -> bool {
|
||||||
|
other
|
||||||
|
.distances
|
||||||
|
.keys()
|
||||||
|
.filter(|&k| self.distances.contains_key(k))
|
||||||
|
.count()
|
||||||
|
>= 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Scanner {
|
||||||
|
type Target = [Point3];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Rotations<'a> {
|
struct Rotations<'a> {
|
||||||
points: &'a [Point3],
|
points: &'a [Point3],
|
||||||
axes: [usize; 3],
|
axes: [usize; 3],
|
||||||
@@ -119,20 +163,29 @@ fn parse_point(input: &[u8]) -> IResult<&[u8], Point3> {
|
|||||||
)(input)
|
)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Vec<Point3>>> {
|
fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Scanner>> {
|
||||||
use nom::character::complete::i32;
|
use nom::character::complete::i32;
|
||||||
let parse_header = delimited(tag("--- scanner "), i32, tag(" ---\n"));
|
let parse_header = delimited(tag("--- scanner "), i32, tag(" ---\n"));
|
||||||
|
|
||||||
let parse_scanner = preceded(parse_header, many1(terminated(parse_point, newline)));
|
let parse_scanner = map(
|
||||||
|
preceded(parse_header, many1(terminated(parse_point, newline))),
|
||||||
|
Scanner::new,
|
||||||
|
);
|
||||||
separated_list1(newline, parse_scanner)(input)
|
separated_list1(newline, parse_scanner)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_overlap(
|
fn try_overlap(matched: &Scanner, candidate: &Scanner) -> Option<(Point3, Scanner)> {
|
||||||
correct: &[(Point3, HashSet<Point3>)],
|
if !matched.can_overlap(candidate) {
|
||||||
candidate: &[Point3],
|
return None;
|
||||||
) -> Option<(Point3, Vec<Point3>)> {
|
}
|
||||||
|
|
||||||
|
let correct: Vec<(Point3, HashSet<Point3>)> = matched
|
||||||
|
.iter()
|
||||||
|
.map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut relative = HashSet::new();
|
let mut relative = HashSet::new();
|
||||||
for rot in Rotations::new(candidate) {
|
for rot in Rotations::new(&candidate.visible) {
|
||||||
for &start in &rot {
|
for &start in &rot {
|
||||||
relative.clear();
|
relative.clear();
|
||||||
|
|
||||||
@@ -144,7 +197,7 @@ fn try_overlap(
|
|||||||
// Found a solution, build the correct output
|
// Found a solution, build the correct output
|
||||||
let translated = relative.drain().map(|point| point + *base).collect();
|
let translated = relative.drain().map(|point| point + *base).collect();
|
||||||
|
|
||||||
return Some((start - *base, translated));
|
return Some((start - *base, Scanner::new(translated)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,29 +210,24 @@ fn parts_common(input: &mut dyn Read) -> (HashSet<Point3>, Vec<Point3>) {
|
|||||||
|
|
||||||
let mut points: HashSet<_> = scanners[0].iter().copied().collect();
|
let mut points: HashSet<_> = scanners[0].iter().copied().collect();
|
||||||
|
|
||||||
let mut todo = vec![std::mem::take(&mut scanners[0])];
|
let mut todo = vec![scanners.remove(0)];
|
||||||
let mut scanners_found = vec![Point3::default()];
|
let mut scanners_found = vec![Point3::default()];
|
||||||
|
|
||||||
while let Some(matched) = todo.pop() {
|
while let Some(matched) = todo.pop() {
|
||||||
if scanners.iter().all(Vec::is_empty) {
|
if scanners.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let relative: Vec<(Point3, HashSet<Point3>)> = matched
|
let mut i = 0;
|
||||||
.iter()
|
|
||||||
.map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for candidate in &mut scanners {
|
while i < scanners.len() {
|
||||||
if candidate.is_empty() {
|
if let Some((scanner, result)) = try_overlap(&matched, &scanners[i]) {
|
||||||
continue;
|
scanners.remove(i);
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((scanner, result)) = try_overlap(&relative, candidate) {
|
|
||||||
scanners_found.push(scanner);
|
scanners_found.push(scanner);
|
||||||
points.extend(result.iter().copied());
|
points.extend(result.iter().copied());
|
||||||
todo.push(result);
|
todo.push(result);
|
||||||
candidate.clear();
|
} else {
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user