refactor: use matrices for day 19
This commit is contained in:
parent
2143563b80
commit
13d65c6a86
245
src/day19.rs
245
src/day19.rs
@ -1,32 +1,8 @@
|
||||
use std::{collections::{HashSet, HashMap}, ops::{Add, Sub, Neg}};
|
||||
use std::{collections::{HashSet, HashMap}, ops::{Add, Sub, Neg, Mul}};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Point(i32, i32, i32);
|
||||
|
||||
fn parse_point(line: &str) -> Point {
|
||||
let nums: Vec<&str> = line.splitn(3, ',').collect();
|
||||
let x = nums[0].parse().unwrap();
|
||||
let y = nums[1].parse().unwrap();
|
||||
let z = nums[2].parse().unwrap();
|
||||
Point(x, y, z)
|
||||
}
|
||||
|
||||
fn parse_scanner(input: &str) -> Vec<Point> {
|
||||
let mut beacons = Vec::new();
|
||||
for line in input.lines().skip(1) {
|
||||
beacons.push(parse_point(line));
|
||||
}
|
||||
beacons
|
||||
}
|
||||
|
||||
pub fn parse_input(input: &str) -> Vec<Vec<Point>> {
|
||||
let mut scanners = Vec::new();
|
||||
for scanner_section in input.split("\n\n") {
|
||||
scanners.push(parse_scanner(scanner_section));
|
||||
}
|
||||
scanners
|
||||
}
|
||||
|
||||
impl Add for Point {
|
||||
type Output = Self;
|
||||
|
||||
@ -59,6 +35,113 @@ impl Neg for Point {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct Rotation(i32, i32, i32, i32, i32, i32, i32, i32, i32);
|
||||
|
||||
impl Mul<Point> for Rotation {
|
||||
type Output = Point;
|
||||
|
||||
fn mul(self, rhs: Point) -> Self::Output {
|
||||
Point(
|
||||
self.0*rhs.0 + self.1*rhs.1 + self.2*rhs.2,
|
||||
self.3*rhs.0 + self.4*rhs.1 + self.5*rhs.2,
|
||||
self.6*rhs.0 + self.7*rhs.1 + self.8*rhs.2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Rotation {
|
||||
type Output = Rotation;
|
||||
|
||||
// https://www.fhybea.com/en/multiplication-matrix-3x3.html
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Rotation(
|
||||
(self.0 * rhs.0) + (self.1 * rhs.3) + (self.2 * rhs.6),
|
||||
(self.0 * rhs.1) + (self.1 * rhs.4) + (self.2 * rhs.7),
|
||||
(self.0 * rhs.2) + (self.1 * rhs.5) + (self.2 * rhs.8),
|
||||
(self.3 * rhs.0) + (self.4 * rhs.3) + (self.5 * rhs.6),
|
||||
(self.3 * rhs.1) + (self.4 * rhs.4) + (self.5 * rhs.7),
|
||||
(self.3 * rhs.2) + (self.4 * rhs.5) + (self.5 * rhs.8),
|
||||
(self.6 * rhs.0) + (self.7 * rhs.3) + (self.8 * rhs.6),
|
||||
(self.6 * rhs.1) + (self.7 * rhs.4) + (self.8 * rhs.7),
|
||||
(self.6 * rhs.2) + (self.7 * rhs.5) + (self.8 * rhs.8)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rotation {
|
||||
fn identity() -> Rotation {
|
||||
Rotation(1, 0, 0, 0, 1, 0, 0, 0, 1)
|
||||
}
|
||||
|
||||
fn transpose(self) -> Rotation {
|
||||
Rotation(
|
||||
self.0,
|
||||
self.3,
|
||||
self.6,
|
||||
|
||||
self.1,
|
||||
self.4,
|
||||
self.7,
|
||||
|
||||
self.2,
|
||||
self.5,
|
||||
self.8,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// https://i.imgur.com/Ff1vGT9.png
|
||||
const ROTATIONS: [Rotation; 24] = [
|
||||
Rotation( 1, 0, 0, 0, 1, 0, 0, 0, 1), // (x ,y ,z)
|
||||
Rotation( 1, 0, 0, 0, 0, 1, 0,-1, 0), // (x ,z ,-y)
|
||||
Rotation( 1, 0, 0, 0,-1, 0, 0, 0,-1), // (x ,-y,-z)
|
||||
Rotation( 1, 0, 0, 0, 0,-1, 0, 1, 0), // (x ,-z,y )
|
||||
Rotation(-1, 0, 0, 0,-1, 0, 0, 0, 1), // (-x,-y,z)
|
||||
Rotation(-1, 0, 0, 0, 0, 1, 0, 1, 0), // (-x,z ,y )
|
||||
Rotation(-1, 0, 0, 0, 1, 0, 0, 0,-1), // (-x,y ,-z)
|
||||
Rotation(-1, 0, 0, 0, 0,-1, 0,-1, 0), // (-x,-z,-y )
|
||||
Rotation( 0, 1, 0, 0, 0, 1, 1, 0, 0), // (y ,z ,x)
|
||||
Rotation( 0, 1, 0, 1, 0, 0, 0, 0,-1), // (y ,x ,-z)
|
||||
Rotation( 0, 1, 0, 0, 0,-1,-1, 0, 0), // (y ,-z,-x)
|
||||
Rotation( 0, 1, 0,-1, 0, 0, 0, 0, 1), // (y ,-x,z )
|
||||
Rotation( 0,-1, 0, 0, 0,-1, 1, 0, 0), // (-y,-z,x)
|
||||
Rotation( 0,-1, 0, 1, 0, 0, 0, 0, 1), // (-y,x ,z )
|
||||
Rotation( 0,-1, 0, 0, 0, 1,-1, 0, 0), // (-y,z ,-x)
|
||||
Rotation( 0,-1, 0,-1, 0, 0, 0, 0,-1), // (-y,-x,-z )
|
||||
Rotation( 0, 0, 1, 1, 0, 0, 0, 1, 0), // (z ,x ,y)
|
||||
Rotation( 0, 0, 1, 0, 1, 0,-1, 0, 0), // (z ,y ,-x)
|
||||
Rotation( 0, 0, 1,-1, 0, 0, 0,-1, 0), // (z ,-x,-y)
|
||||
Rotation( 0, 0, 1, 0,-1, 0, 1, 0, 0), // (z ,-y,x )
|
||||
Rotation( 0, 0,-1,-1, 0, 0, 0, 1, 0), // (-z,-x,y)
|
||||
Rotation( 0, 0,-1, 0, 1, 0, 1, 0, 0), // (-z,y ,x )
|
||||
Rotation( 0, 0,-1, 1, 0, 0, 0,-1, 0), // (-z,x ,-y)
|
||||
Rotation( 0, 0,-1, 0,-1, 0,-1, 0, 0), // (-z,-y,-x)
|
||||
];
|
||||
|
||||
fn parse_point(line: &str) -> Point {
|
||||
let nums: Vec<&str> = line.splitn(3, ',').collect();
|
||||
let x = nums[0].parse().unwrap();
|
||||
let y = nums[1].parse().unwrap();
|
||||
let z = nums[2].parse().unwrap();
|
||||
Point(x, y, z)
|
||||
}
|
||||
|
||||
fn parse_scanner(input: &str) -> Vec<Point> {
|
||||
let mut beacons = Vec::new();
|
||||
for line in input.lines().skip(1) {
|
||||
beacons.push(parse_point(line));
|
||||
}
|
||||
beacons
|
||||
}
|
||||
|
||||
pub fn parse_input(input: &str) -> Vec<Vec<Point>> {
|
||||
let mut scanners = Vec::new(); for scanner_section in input.split("\n\n") {
|
||||
scanners.push(parse_scanner(scanner_section));
|
||||
}
|
||||
scanners
|
||||
}
|
||||
|
||||
fn calc_diff(a: &Point, b: &Point) -> Point {
|
||||
let x = (a.0 - b.0).abs();
|
||||
let y = (a.1 - b.1).abs();
|
||||
@ -105,57 +188,19 @@ fn find_possibly_overlapping_scanners(scanners: &[Vec<Point>]) -> Vec<(usize, us
|
||||
pairs
|
||||
}
|
||||
|
||||
fn calc_rotations(p: Point) -> Vec<Point> {
|
||||
// https://i.imgur.com/Ff1vGT9.png
|
||||
let Point(x, y, z) = p;
|
||||
vec![
|
||||
Point(x ,y ,z), Point(x ,z,-y), Point(x ,-y,-z), Point(x ,-z,y ),
|
||||
Point(-x,-y,z), Point(-x,z,y ), Point(-x,y ,-z), Point(-x,-z,-y ),
|
||||
Point(y ,z ,x), Point(y ,x,-z), Point(y ,-z,-x), Point(y ,-x,z ),
|
||||
Point(-y,-z,x), Point(-y,x,z ), Point(-y,z ,-x), Point(-y,-x,-z ),
|
||||
Point(z ,x ,y), Point(z ,y,-x), Point(z ,-x,-y), Point(z ,-y,x ),
|
||||
Point(-z,-x,y), Point(-z,y,x ), Point(-z,x ,-y), Point(-z,-y,-x)
|
||||
]
|
||||
}
|
||||
|
||||
fn calc_rotation(p: Point, rotation: usize) -> Point {
|
||||
calc_rotations(p)[rotation]
|
||||
}
|
||||
|
||||
fn combine_rotations(r1: usize, r2: usize) -> usize {
|
||||
let target_point = calc_rotation(calc_rotation(Point(1, 2, 3), r1), r2);
|
||||
for (i, rotated) in calc_rotations(Point(1, 2, 3)).iter().enumerate() {
|
||||
if *rotated == target_point {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn invert_rotation(r: usize) -> usize {
|
||||
let target_point = calc_rotation(Point(1, 2, 3), r);
|
||||
for (i, rotated) in calc_rotations(target_point).iter().enumerate() {
|
||||
if *rotated == Point(1, 2, 3) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn find_possible_rotations(dir1: Point, dir2: Point) -> Vec<usize> {
|
||||
fn find_possible_rotations(dir1: Point, dir2: Point) -> Vec<Rotation> {
|
||||
let mut possible_rotations = vec![];
|
||||
|
||||
let rotated_dirs2 = calc_rotations(dir2);
|
||||
for (i, rotated_dir2) in rotated_dirs2.iter().enumerate() {
|
||||
if *rotated_dir2 == dir1 {
|
||||
possible_rotations.push(i);
|
||||
for rot in ROTATIONS {
|
||||
if rot.mul(dir2) == dir1 {
|
||||
possible_rotations.push(rot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i, rotated_dir2) in rotated_dirs2.iter().enumerate() {
|
||||
if *rotated_dir2 == -dir1 {
|
||||
possible_rotations.push(i);
|
||||
for rot in ROTATIONS {
|
||||
if rot.mul(dir2) == -dir1 {
|
||||
possible_rotations.push(rot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -163,13 +208,7 @@ fn find_possible_rotations(dir1: Point, dir2: Point) -> Vec<usize> {
|
||||
possible_rotations
|
||||
}
|
||||
|
||||
fn apply_transformation(points: &[Point], translation: Point, rotation: usize) -> Vec<Point> {
|
||||
points.iter()
|
||||
.map(|p| calc_rotation(*p, rotation) + translation)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn find_transformation(scanner1: &[Point], scanner2: &[Point]) -> Option<(Point, usize)> {
|
||||
fn find_transformation(scanner1: &[Point], scanner2: &[Point]) -> Option<(Point, Rotation)> {
|
||||
let point_pairs1 = get_point_pairs(scanner1);
|
||||
let point_pairs2 = get_point_pairs(scanner2);
|
||||
|
||||
@ -184,9 +223,9 @@ fn find_transformation(scanner1: &[Point], scanner2: &[Point]) -> Option<(Point,
|
||||
let dir2 = pair2.0 - pair2.1;
|
||||
|
||||
for rotation in find_possible_rotations(dir1, dir2) {
|
||||
let translation = pair1.0 - calc_rotation(pair2.0, rotation);
|
||||
let translation = pair1.0 - rotation.mul(pair2.0);
|
||||
let transformed_scanner2: HashSet<_> = scanner2.iter()
|
||||
.map(|p| calc_rotation(*p, rotation) + translation)
|
||||
.map(|p| rotation.mul(*p) + translation)
|
||||
.collect();
|
||||
|
||||
if scanner1_set.intersection(&transformed_scanner2).count() >= 12 {
|
||||
@ -199,16 +238,16 @@ fn find_transformation(scanner1: &[Point], scanner2: &[Point]) -> Option<(Point,
|
||||
None
|
||||
}
|
||||
|
||||
fn find_canonical_transformations(scanners: &[Vec<Point>]) -> Vec<(Point, usize)> {
|
||||
fn find_canonical_transformations(scanners: &[Vec<Point>]) -> Vec<(Point, Rotation)> {
|
||||
let mut canonical = vec![];
|
||||
for _ in 0..scanners.len() {
|
||||
canonical.push((Point(0, 0, 0), 0))
|
||||
canonical.push((Point(0, 0, 0), Rotation::identity()))
|
||||
}
|
||||
|
||||
let mut transforms = HashMap::new();
|
||||
for (scanner1, scanner2) in find_possibly_overlapping_scanners(scanners) {
|
||||
let (translation, rotation) = find_transformation(&scanners[scanner1], &scanners[scanner2]).unwrap();
|
||||
let inv_rotation = invert_rotation(rotation);
|
||||
let inv_rotation = rotation.transpose();
|
||||
|
||||
transforms.entry(scanner1)
|
||||
.or_insert_with(Vec::new)
|
||||
@ -216,10 +255,10 @@ fn find_canonical_transformations(scanners: &[Vec<Point>]) -> Vec<(Point, usize)
|
||||
|
||||
transforms.entry(scanner2)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((scanner1, calc_rotation(-translation, inv_rotation), inv_rotation));
|
||||
.push((scanner1, inv_rotation.mul(-translation), inv_rotation));
|
||||
}
|
||||
|
||||
let mut stack = vec![(0, Point(0, 0, 0), 0)];
|
||||
let mut stack = vec![(0, Point(0, 0, 0), Rotation::identity())];
|
||||
let mut visited = HashSet::new();
|
||||
while !stack.is_empty() {
|
||||
let (id, translation, rotation) = stack.pop().unwrap();
|
||||
@ -230,15 +269,13 @@ fn find_canonical_transformations(scanners: &[Vec<Point>]) -> Vec<(Point, usize)
|
||||
for nbr in transforms.get(&id).unwrap() {
|
||||
if visited.contains(&nbr.0) { continue; }
|
||||
|
||||
let new_translation = calc_rotation(nbr.1, rotation) + translation;
|
||||
let new_rotation = combine_rotations(nbr.2, rotation);
|
||||
let new_translation = rotation.mul(nbr.1) + translation;
|
||||
let new_rotation = rotation.mul(nbr.2);
|
||||
stack.push((nbr.0, new_translation, new_rotation));
|
||||
canonical[nbr.0] = (new_translation, new_rotation);
|
||||
}
|
||||
}
|
||||
|
||||
// dbg!(&canonical);
|
||||
|
||||
canonical
|
||||
}
|
||||
|
||||
@ -248,7 +285,7 @@ pub fn part1(scanners: &[Vec<Point>]) -> u32 {
|
||||
|
||||
for (i, points) in scanners.iter().enumerate() {
|
||||
let (translation, rotation) = transforms[i];
|
||||
for point in apply_transformation(points, translation, rotation) {
|
||||
for point in points.iter().map(|p| rotation.mul(*p) + translation) {
|
||||
beacons.insert(point);
|
||||
}
|
||||
}
|
||||
@ -332,6 +369,34 @@ mod tests {
|
||||
let scanner2 = vec![Point(686, 422, 578), Point(605, 423, 415), Point(515, 917, -361), Point(-336, 658, 858), Point(95, 138, 22), Point(-476, 619, 847), Point(-340, -569, -846), Point(567, -361, 727), Point(-460, 603, -452), Point(669, -402, 600), Point(729, 430, 532), Point(-500, -761, 534), Point(-322, 571, 750), Point(-466, -666, -811), Point(-429, -592, 574), Point(-355, 545, -477), Point(703, -491, -529), Point(-328, -685, 520), Point(413, 935, -424), Point(-391, 539, -444), Point(586, -435, 557), Point(-364, -763, -893), Point(807, -499, -711), Point(755, -354, -619), Point(553, 889, -390)];
|
||||
let (translation, rotation) = find_transformation(&scanner1, &scanner2).unwrap();
|
||||
assert_eq!(translation, Point(68, -1246, -43));
|
||||
assert_eq!(rotation, 6);
|
||||
assert_eq!(rotation, ROTATIONS[6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn rotation_matrices() {
|
||||
let p = Point(1, 2, 3);
|
||||
let Point(x, y, z) = p;
|
||||
let ref_rotations = vec![
|
||||
Point(x ,y ,z), Point(x ,z,-y), Point(x ,-y,-z), Point(x ,-z,y ),
|
||||
Point(-x,-y,z), Point(-x,z,y ), Point(-x,y ,-z), Point(-x,-z,-y ),
|
||||
Point(y ,z ,x), Point(y ,x,-z), Point(y ,-z,-x), Point(y ,-x,z ),
|
||||
Point(-y,-z,x), Point(-y,x,z ), Point(-y,z ,-x), Point(-y,-x,-z ),
|
||||
Point(z ,x ,y), Point(z ,y,-x), Point(z ,-x,-y), Point(z ,-y,x ),
|
||||
Point(-z,-x,y), Point(-z,y,x ), Point(-z,x ,-y), Point(-z,-y,-x)
|
||||
];
|
||||
let calculated: Vec<_> = ROTATIONS.iter().map(|rot| rot.mul(p)).collect();
|
||||
for (i, rot) in calculated.iter().enumerate() {
|
||||
assert_eq!(*rot, ref_rotations[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn multiply_matrices() {
|
||||
let a = Rotation(2, 3, 1, 7, 4, 1, 9, -2, 1);
|
||||
let b = Rotation(9, -2, -1, 5, 7, 3, 8, 1, 0);
|
||||
let c = a.mul(b);
|
||||
assert_eq!(c, Rotation(41, 18, 7, 91, 15, 5, 79, -31, -15));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user