1
0

feat: complete day 19 part 2

This commit is contained in:
Rokas Puzonas 2022-06-14 00:27:25 +03:00
parent 2e495f674c
commit 2143563b80
2 changed files with 418 additions and 303 deletions

View File

@ -1,263 +1,337 @@
// Solution for this problem by somone else: https://github.com/Jellycious/aoc-2021/blob/main/src/days/day19.rs
use std::{collections::{HashSet, HashMap}, ops::{Add, Sub}};
use std::{collections::{HashSet, HashMap}, ops::{Add, Sub, Neg}};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Point(i32, i32, i32);
#[derive(Debug, PartialEq)]
pub struct RotationMatrix(i32, i32, i32, i32, i32, i32, i32, i32, i32);
impl Point {
fn apply_rotation(&self, t: &RotationMatrix) -> Point {
return Point(
self.0 * t.0 + self.1 * t.1 + self.2 * t.2,
self.0 * t.3 + self.1 * t.4 + self.2 * t.5,
self.0 * t.6 + self.1 * t.7 + self.2 * t.8
);
}
fn sqr_dist(&self, other: &Point) -> i32 {
let dx = self.0 - other.0;
let dy = self.1 - other.1;
let dz = self.2 - other.2;
return dx*dx + dy*dy + dz*dz;
}
}
impl Add for Point {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Point(
self.0 + rhs.0,
self.1 + rhs.1,
self.2 + rhs.2
)
}
}
impl Sub for Point {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Point(
self.0 - rhs.0,
self.1 - rhs.1,
self.2 - rhs.2
)
}
}
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();
return Point(x, y, z);
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));
}
return beacons;
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));
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;
fn add(self, rhs: Self) -> Self::Output {
Point(
self.0 + rhs.0,
self.1 + rhs.1,
self.2 + rhs.2
)
}
}
impl Sub for Point {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Point(
self.0 - rhs.0,
self.1 - rhs.1,
self.2 - rhs.2
)
}
}
impl Neg for Point {
type Output = Point;
fn neg(self) -> Self::Output {
Point(-self.0, -self.1, -self.2)
}
return scanners;
}
fn generate_rotations() -> Vec<RotationMatrix> {
let mut transforms = Vec::new();
fn calc_diff(a: &Point, b: &Point) -> Point {
let x = (a.0 - b.0).abs();
let y = (a.1 - b.1).abs();
let z = (a.2 - b.2).abs();
// +z axis
transforms.push(RotationMatrix( 1, 0, 0, 0, 1, 0, 0, 0, 1));
transforms.push(RotationMatrix( 0, 1, 0, -1, 0, 0, 0, 0, 1));
transforms.push(RotationMatrix(-1, 0, 0, 0, -1, 0, 0, 0, 1));
transforms.push(RotationMatrix(0, -1, 0, 1, 0, 0, 0, 0, 1));
let lowest = x.min(y).min(z);
let highest = x.max(y).max(z);
let middle = x + y + z - lowest - highest;
// -z axis
transforms.push(RotationMatrix( 1, 0, 0, 0, -1, 0, 0, 0, -1));
transforms.push(RotationMatrix( 0, 1, 0, 1, 0, 0, 0, 0, -1));
transforms.push(RotationMatrix(-1, 0, 0, 0, 1, 0, 0, 0, -1));
transforms.push(RotationMatrix(0, -1, 0, -1, 0, 0, 0, 0, -1));
// +y axis
transforms.push(RotationMatrix( 1, 0, 0, 0, 0, -1, 0, 1, 0));
transforms.push(RotationMatrix( 0, 0, 1, 1, 0, 0, 0, 1, 0));
transforms.push(RotationMatrix(-1, 0, 0, 0, 0, 1, 0, 1, 0));
transforms.push(RotationMatrix( 0, 0,-1, -1, 0, 0, 0, 1, 0));
// -y axis
transforms.push(RotationMatrix( 1, 0, 0, 0, 0, 1, 0,-1, 0));
transforms.push(RotationMatrix( 0, 0, 1, -1, 0, 0, 0,-1, 0));
transforms.push(RotationMatrix(-1, 0, 0, 0, 0, -1, 0,-1, 0));
transforms.push(RotationMatrix( 0, 0,-1, 1, 0, 0, 0,-1, 0));
// +x axis
transforms.push(RotationMatrix( 0,-1, 0, 0, 0, -1, 1, 0, 0));
transforms.push(RotationMatrix( 0, 0,-1, 0, 1, 0, 1, 0, 0));
transforms.push(RotationMatrix( 0, 1, 0, 0, 0, 1, 1, 0, 0));
transforms.push(RotationMatrix( 0, 0, 1, 0,-1, 0, 1, 0, 0));
// -x axis
transforms.push(RotationMatrix( 0, 1, 0, 0, 0, -1, -1, 0, 0));
transforms.push(RotationMatrix( 0, 0, 1, 0, 1, 0, -1, 0, 0));
transforms.push(RotationMatrix( 0,-1, 0, 0, 0, 1, -1, 0, 0));
transforms.push(RotationMatrix( 0, 0,-1, 0,-1, 0, -1, 0, 0));
return transforms;
Point(lowest, middle, highest)
}
fn find_distances_pairs<'a>(points: &'a Vec<Point>) -> Vec<(i32, &'a Point, &'a Point)> {
let mut distances = Vec::new();
let n = points.len();
for i in 0..n-1 {
for j in i+1..n {
let p1 = &points[i];
let p2 = &points[j];
let dist = p1.sqr_dist(&p2);
distances.push((dist, p1, p2));
}
}
return distances;
fn get_point_pairs(points: &[Point]) -> Vec<(Point, Point)> {
points.iter()
.enumerate()
.flat_map(|(i, p1)| points.iter().skip(i + 1).map(move |p2| (*p1, *p2)))
.collect()
}
fn find_common_points<'a, 'b>(scanner1: &'a Vec<Point>, scanner2: &'b Vec<Point>) -> (Vec<Point>, Vec<Point>) {
let mut common_points1: Vec<Point> = Vec::new();
let mut common_points2: Vec<Point> = Vec::new();
for pair1 in find_distances_pairs(scanner1) {
for pair2 in find_distances_pairs(scanner2) {
if pair1.0 == pair2.0 {
if !common_points1.contains(pair1.1) {
common_points1.push(*pair1.1);
}
if !common_points1.contains(pair1.2) {
common_points1.push(*pair1.2);
}
if !common_points2.contains(pair2.1) {
common_points2.push(*pair2.1);
}
if !common_points2.contains(pair2.2) {
common_points2.push(*pair2.2);
}
}
}
}
return (common_points1, common_points2);
fn get_point_diffs(a: &[Point]) -> HashSet<Point> {
get_point_pairs(a).iter()
.map(|(p1, p2)| calc_diff(p1, p2))
.collect()
}
fn apply_offset_rotation(points: &Vec<Point>, offset: &Point, rotation: &RotationMatrix) -> Vec<Point> {
points.iter()
.map(|p| p.apply_rotation(&rotation) + *offset)
.collect()
fn do_scanners_possibly_overlap(a: &[Point], b: &[Point]) -> bool {
let a_diffs = get_point_diffs(a);
let b_diffs = get_point_diffs(b);
a_diffs.intersection(&b_diffs).count() >= 66
}
fn find_offset_rotation(mut points1: Vec<Point>, points2: Vec<Point>) -> Option<(Point, RotationMatrix)> {
points1.sort();
fn find_possibly_overlapping_scanners(scanners: &[Vec<Point>]) -> Vec<(usize, usize)> {
let mut pairs = vec![];
'outer: for rotation in generate_rotations() {
let mut transformed_points: Vec<_> = points2.iter()
.map(|p| p.apply_rotation(&rotation))
.collect();
transformed_points.sort();
for (i, scanner1) in scanners.iter().enumerate() {
for (j, scanner2) in scanners.iter().enumerate().skip(i + 1) {
if do_scanners_possibly_overlap(scanner1, scanner2) {
pairs.push((i, j));
}
}
}
let offset = points1[0] - transformed_points[0];
if rotation == RotationMatrix(-1, 0, 0, 0, -1, 0, 0, 0, 1) {
println!("\n\nTransform: {:?}", rotation);
println!("Offset: {:?}\n", offset);
println!("{:?}\n", points1);
// println!("{:?}", transformed_points);
}
for i in 1..points1.len() {
let diff = points1[i] - transformed_points[i];
if offset != diff {
continue 'outer;
}
}
return Some((offset, rotation));
}
return None;
pairs
}
fn align_scanners(scanners: &mut Vec<Vec<Point>>) {
let mut unaligned = HashSet::new();
let mut unchecked = vec![0];
for i in 1..scanners.len() {
unaligned.insert(i);
}
while !unchecked.is_empty() {
let aligned_id = unchecked.pop().unwrap();
for i in unaligned.clone() {
let unaligned_scanner = &scanners[i];
let (common_points1, common_points2) = find_common_points(&scanners[aligned_id], unaligned_scanner);
if common_points1.len() >= 12 {
let transform = find_offset_rotation(common_points1, common_points2);
if let Some((offset, rotation)) = transform {
scanners[i] = apply_offset_rotation(&scanners[i], &offset, &rotation);
unchecked.push(i);
unaligned.remove(&i);
}
}
}
}
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 find_unique_points(scanners: &Vec<Vec<Point>>) -> Vec<Point> {
let mut cloned = scanners.clone();
align_scanners(&mut cloned);
let mut point_set = HashSet::new();
for scanner in cloned {
for point in scanner {
point_set.insert(point);
}
}
return point_set.into_iter().collect();
fn calc_rotation(p: Point, rotation: usize) -> Point {
calc_rotations(p)[rotation]
}
pub fn part1(scanners: &Vec<Vec<Point>>) -> u32 {
find_unique_points(scanners).len() as u32
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!()
}
pub fn part2(scanners: &Vec<Vec<Point>>) -> u32 {
0
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> {
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);
break;
}
}
for (i, rotated_dir2) in rotated_dirs2.iter().enumerate() {
if *rotated_dir2 == -dir1 {
possible_rotations.push(i);
break;
}
}
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)> {
let point_pairs1 = get_point_pairs(scanner1);
let point_pairs2 = get_point_pairs(scanner2);
let scanner1_set: HashSet<_> = scanner1.iter().copied().collect();
for pair1 in &point_pairs1 {
for pair2 in &point_pairs2 {
if calc_diff(&pair1.0, &pair1.1) != calc_diff(&pair2.0, &pair2.1) {
continue;
}
let dir1 = pair1.0 - pair1.1;
let dir2 = pair2.0 - pair2.1;
for rotation in find_possible_rotations(dir1, dir2) {
let translation = pair1.0 - calc_rotation(pair2.0, rotation);
let transformed_scanner2: HashSet<_> = scanner2.iter()
.map(|p| calc_rotation(*p, rotation) + translation)
.collect();
if scanner1_set.intersection(&transformed_scanner2).count() >= 12 {
return Some((translation, rotation));
}
}
}
}
None
}
fn find_canonical_transformations(scanners: &[Vec<Point>]) -> Vec<(Point, usize)> {
let mut canonical = vec![];
for _ in 0..scanners.len() {
canonical.push((Point(0, 0, 0), 0))
}
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);
transforms.entry(scanner1)
.or_insert_with(Vec::new)
.push((scanner2, translation, rotation));
transforms.entry(scanner2)
.or_insert_with(Vec::new)
.push((scanner1, calc_rotation(-translation, inv_rotation), inv_rotation));
}
let mut stack = vec![(0, Point(0, 0, 0), 0)];
let mut visited = HashSet::new();
while !stack.is_empty() {
let (id, translation, rotation) = stack.pop().unwrap();
if visited.contains(&id) { continue; }
visited.insert(id);
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);
stack.push((nbr.0, new_translation, new_rotation));
canonical[nbr.0] = (new_translation, new_rotation);
}
}
// dbg!(&canonical);
canonical
}
pub fn part1(scanners: &[Vec<Point>]) -> u32 {
let transforms = find_canonical_transformations(scanners);
let mut beacons = HashSet::new();
for (i, points) in scanners.iter().enumerate() {
let (translation, rotation) = transforms[i];
for point in apply_transformation(points, translation, rotation) {
beacons.insert(point);
}
}
beacons.len() as u32
}
pub fn part2(scanners: &[Vec<Point>]) -> i32 {
let transforms = find_canonical_transformations(scanners);
let mut max_distance = 0;
for (i, transform1) in transforms.iter().enumerate() {
for transform2 in transforms.iter().skip(i+1) {
let diff = transform1.0 - transform2.0;
let distance = diff.0 + diff.1 + diff.2;
max_distance = max_distance.max(distance);
}
}
max_distance
}
#[cfg(test)]
mod tests {
use super::*;
use super::*;
#[test]
fn part1_example() {
let scanners = vec![
vec![Point(404,-588,-901), Point(528,-643,409), Point(-838,591,734), Point(390,-675,-793), Point(-537,-823,-458), Point(-485,-357,347), Point(-345,-311,381), Point(-661,-816,-575), Point(-876,649,763), Point(-618,-824,-621), Point(553,345,-567), Point(474,580,667), Point(-447,-329,318), Point(-584,868,-557), Point(544,-627,-890), Point(564,392,-477), Point(455,729,728), Point(-892,524,684), Point(-689,845,-530), Point(423,-701,434), Point(7,-33,-71), Point(630,319,-379), Point(443,580,662), Point(-789,900,-551), Point(459,-707,401)],
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)],
vec![Point(649,640,665), Point(682,-795,504), Point(-784,533,-524), Point(-644,584,-595), Point(-588,-843,648), Point(-30,6,44), Point(-674,560,763), Point(500,723,-460), Point(609,671,-379), Point(-555,-800,653), Point(-675,-892,-343), Point(697,-426,-610), Point(578,704,681), Point(493,664,-388), Point(-671,-858,530), Point(-667,343,800), Point(571,-461,-707), Point(-138,-166,112), Point(-889,563,-600), Point(646,-828,498), Point(640,759,510), Point(-630,509,768), Point(-681,-892,-333), Point(673,-379,-804), Point(-742,-814,-386), Point(577,-820,562)],
vec![Point(-589,542,597), Point(605,-692,669), Point(-500,565,-823), Point(-660,373,557), Point(-458,-679,-417), Point(-488,449,543), Point(-626,468,-788), Point(338,-750,-386), Point(528,-832,-391), Point(562,-778,733), Point(-938,-730,414), Point(543,643,-506), Point(-524,371,-870), Point(407,773,750), Point(-104,29,83), Point(378,-903,-323), Point(-778,-728,485), Point(426,699,580), Point(-438,-605,-362), Point(-469,-447,-387), Point(509,732,623), Point(647,635,-688), Point(-868,-804,481), Point(614,-800,639), Point(595,780,-596)],
vec![Point(727,592,562), Point(-293,-554,779), Point(441,611,-461), Point(-714,465,-776), Point(-743,427,-804), Point(-660,-479,-426), Point(832,-632,460), Point(927,-485,-438), Point(408,393,-506), Point(466,436,-512), Point(110,16,151), Point(-258,-428,682), Point(-393,719,612), Point(-211,-452,876), Point(808,-476,-593), Point(-575,615,604), Point(-485,667,467), Point(-680,325,-822), Point(-627,-443,-432), Point(872,-547,-609), Point(833,512,582), Point(807,604,487), Point(839,-516,451), Point(891,-625,532), Point(-652,-548,-490), Point(30,-46,-14)]
];
let result = part1(&scanners);
assert_eq!(result, 79);
}
#[test]
#[rustfmt::skip]
fn part1_example() {
let scanners = vec![
vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)],
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)],
vec![Point(649, 640, 665), Point(682, -795, 504), Point(-784, 533, -524), Point(-644, 584, -595), Point(-588, -843, 648), Point(-30, 6, 44), Point(-674, 560, 763), Point(500, 723, -460), Point(609, 671, -379), Point(-555, -800, 653), Point(-675, -892, -343), Point(697, -426, -610), Point(578, 704, 681), Point(493, 664, -388), Point(-671, -858, 530), Point(-667, 343, 800), Point(571, -461, -707), Point(-138, -166, 112), Point(-889, 563, -600), Point(646, -828, 498), Point(640, 759, 510), Point(-630, 509, 768), Point(-681, -892, -333), Point(673, -379, -804), Point(-742, -814, -386), Point(577, -820, 562)],
vec![Point(-589, 542, 597), Point(605, -692, 669), Point(-500, 565, -823), Point(-660, 373, 557), Point(-458, -679, -417), Point(-488, 449, 543), Point(-626, 468, -788), Point(338, -750, -386), Point(528, -832, -391), Point(562, -778, 733), Point(-938, -730, 414), Point(543, 643, -506), Point(-524, 371, -870), Point(407, 773, 750), Point(-104, 29, 83), Point(378, -903, -323), Point(-778, -728, 485), Point(426, 699, 580), Point(-438, -605, -362), Point(-469, -447, -387), Point(509, 732, 623), Point(647, 635, -688), Point(-868, -804, 481), Point(614, -800, 639), Point(595, 780, -596)],
vec![Point(727, 592, 562), Point(-293, -554, 779), Point(441, 611, -461), Point(-714, 465, -776), Point(-743, 427, -804), Point(-660, -479, -426), Point(832, -632, 460), Point(927, -485, -438), Point(408, 393, -506), Point(466, 436, -512), Point(110, 16, 151), Point(-258, -428, 682), Point(-393, 719, 612), Point(-211, -452, 876), Point(808, -476, -593), Point(-575, 615, 604), Point(-485, 667, 467), Point(-680, 325, -822), Point(-627, -443, -432), Point(872, -547, -609), Point(833, 512, 582), Point(807, 604, 487), Point(839, -516, 451), Point(891, -625, 532), Point(-652, -548, -490), Point(30, -46, -14)],
];
let result = part1(&scanners);
assert_eq!(result, 79);
}
#[test]
#[rustfmt::skip]
fn part2_example() {
let scanners = vec![
vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)],
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)],
vec![Point(649, 640, 665), Point(682, -795, 504), Point(-784, 533, -524), Point(-644, 584, -595), Point(-588, -843, 648), Point(-30, 6, 44), Point(-674, 560, 763), Point(500, 723, -460), Point(609, 671, -379), Point(-555, -800, 653), Point(-675, -892, -343), Point(697, -426, -610), Point(578, 704, 681), Point(493, 664, -388), Point(-671, -858, 530), Point(-667, 343, 800), Point(571, -461, -707), Point(-138, -166, 112), Point(-889, 563, -600), Point(646, -828, 498), Point(640, 759, 510), Point(-630, 509, 768), Point(-681, -892, -333), Point(673, -379, -804), Point(-742, -814, -386), Point(577, -820, 562)],
vec![Point(-589, 542, 597), Point(605, -692, 669), Point(-500, 565, -823), Point(-660, 373, 557), Point(-458, -679, -417), Point(-488, 449, 543), Point(-626, 468, -788), Point(338, -750, -386), Point(528, -832, -391), Point(562, -778, 733), Point(-938, -730, 414), Point(543, 643, -506), Point(-524, 371, -870), Point(407, 773, 750), Point(-104, 29, 83), Point(378, -903, -323), Point(-778, -728, 485), Point(426, 699, 580), Point(-438, -605, -362), Point(-469, -447, -387), Point(509, 732, 623), Point(647, 635, -688), Point(-868, -804, 481), Point(614, -800, 639), Point(595, 780, -596)],
vec![Point(727, 592, 562), Point(-293, -554, 779), Point(441, 611, -461), Point(-714, 465, -776), Point(-743, 427, -804), Point(-660, -479, -426), Point(832, -632, 460), Point(927, -485, -438), Point(408, 393, -506), Point(466, 436, -512), Point(110, 16, 151), Point(-258, -428, 682), Point(-393, 719, 612), Point(-211, -452, 876), Point(808, -476, -593), Point(-575, 615, 604), Point(-485, 667, 467), Point(-680, 325, -822), Point(-627, -443, -432), Point(872, -547, -609), Point(833, 512, 582), Point(807, 604, 487), Point(839, -516, 451), Point(891, -625, 532), Point(-652, -548, -490), Point(30, -46, -14)],
];
let result = part2(&scanners);
assert_eq!(result, 3621);
}
#[test]
#[rustfmt::skip]
fn part1_overlap() {
let scanner1 = vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)];
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)];
assert!(do_scanners_possibly_overlap(&scanner1, &scanner2));
}
#[test]
#[rustfmt::skip]
fn part1_overlapping_scanners() {
let scanners = vec![
vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)],
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)],
vec![Point(649, 640, 665), Point(682, -795, 504), Point(-784, 533, -524), Point(-644, 584, -595), Point(-588, -843, 648), Point(-30, 6, 44), Point(-674, 560, 763), Point(500, 723, -460), Point(609, 671, -379), Point(-555, -800, 653), Point(-675, -892, -343), Point(697, -426, -610), Point(578, 704, 681), Point(493, 664, -388), Point(-671, -858, 530), Point(-667, 343, 800), Point(571, -461, -707), Point(-138, -166, 112), Point(-889, 563, -600), Point(646, -828, 498), Point(640, 759, 510), Point(-630, 509, 768), Point(-681, -892, -333), Point(673, -379, -804), Point(-742, -814, -386), Point(577, -820, 562)],
vec![Point(-589, 542, 597), Point(605, -692, 669), Point(-500, 565, -823), Point(-660, 373, 557), Point(-458, -679, -417), Point(-488, 449, 543), Point(-626, 468, -788), Point(338, -750, -386), Point(528, -832, -391), Point(562, -778, 733), Point(-938, -730, 414), Point(543, 643, -506), Point(-524, 371, -870), Point(407, 773, 750), Point(-104, 29, 83), Point(378, -903, -323), Point(-778, -728, 485), Point(426, 699, 580), Point(-438, -605, -362), Point(-469, -447, -387), Point(509, 732, 623), Point(647, 635, -688), Point(-868, -804, 481), Point(614, -800, 639), Point(595, 780, -596)],
vec![Point(727, 592, 562), Point(-293, -554, 779), Point(441, 611, -461), Point(-714, 465, -776), Point(-743, 427, -804), Point(-660, -479, -426), Point(832, -632, 460), Point(927, -485, -438), Point(408, 393, -506), Point(466, 436, -512), Point(110, 16, 151), Point(-258, -428, 682), Point(-393, 719, 612), Point(-211, -452, 876), Point(808, -476, -593), Point(-575, 615, 604), Point(-485, 667, 467), Point(-680, 325, -822), Point(-627, -443, -432), Point(872, -547, -609), Point(833, 512, 582), Point(807, 604, 487), Point(839, -516, 451), Point(891, -625, 532), Point(-652, -548, -490), Point(30, -46, -14)],
];
let overlapping = find_possibly_overlapping_scanners(&scanners);
assert_eq!(overlapping, [(0, 1), (1, 3), (1, 4), (2, 4)]);
}
#[test]
#[rustfmt::skip]
fn part1_find_transformation() {
let scanner1 = vec![Point(404, -588, -901), Point(528, -643, 409), Point(-838, 591, 734), Point(390, -675, -793), Point(-537, -823, -458), Point(-485, -357, 347), Point(-345, -311, 381), Point(-661, -816, -575), Point(-876, 649, 763), Point(-618, -824, -621), Point(553, 345, -567), Point(474, 580, 667), Point(-447, -329, 318), Point(-584, 868, -557), Point(544, -627, -890), Point(564, 392, -477), Point(455, 729, 728), Point(-892, 524, 684), Point(-689, 845, -530), Point(423, -701, 434), Point(7, -33, -71), Point(630, 319, -379), Point(443, 580, 662), Point(-789, 900, -551), Point(459, -707, 401)];
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);
}
}

View File

@ -1,12 +1,4 @@
mod day1;
mod day2;
mod day3;
mod day4;
mod day5;
mod day6;
mod day7;
mod day8;
mod day9;
mod day10;
mod day11;
mod day12;
@ -17,94 +9,143 @@ mod day16;
mod day17;
mod day18;
mod day19;
mod day2;
mod day20;
mod day21;
mod day22;
mod day23;
mod day3;
mod day4;
mod day5;
mod day6;
mod day7;
mod day8;
mod day9;
use std::{env, process};
use std::fs::File;
use std::io::prelude::*;
use std::{env, process};
fn run(day: i32, part: i32, input_filename: &str) {
let mut input_file = File::open(input_filename)
.expect(format!("Input file '{}' not found", input_filename).as_str());
let mut input_file = File::open(input_filename)
.expect(format!("Input file '{}' not found", input_filename).as_str());
let mut contents = String::new();
input_file.read_to_string(&mut contents)
.expect("Failed to read input file");
let mut contents = String::new();
input_file
.read_to_string(&mut contents)
.expect("Failed to read input file");
// Janky solution so that I could specify what I want to run
let parse_error_msg = "Failed to parse input";
match format!("{}.{}", day, part).as_str() {
"1.1" => println!("{}", day1::part1(&day1::parse_input(&contents).expect(parse_error_msg))),
"1.2" => println!("{}", day1::part2(&day1::parse_input(&contents).expect(parse_error_msg))),
"2.1" => println!("{}", day2::part1(&day2::parse_input(&contents).expect(parse_error_msg))),
"2.2" => println!("{}", day2::part2(&day2::parse_input(&contents).expect(parse_error_msg))),
"3.1" => println!("{}", day3::part1(&day3::parse_input(&contents).expect(parse_error_msg))),
"3.2" => println!("{}", day3::part2(&day3::parse_input(&contents).expect(parse_error_msg))),
"4.1" => println!("{}", day4::part1(&day4::parse_input(&contents).expect(parse_error_msg))),
"4.2" => println!("{}", day4::part2(&day4::parse_input(&contents).expect(parse_error_msg))),
"5.1" => println!("{}", day5::part1(&day5::parse_input(&contents).expect(parse_error_msg))),
"5.2" => println!("{}", day5::part2(&day5::parse_input(&contents).expect(parse_error_msg))),
"6.1" => println!("{}", day6::part1(&day6::parse_input(&contents).expect(parse_error_msg))),
"6.2" => println!("{}", day6::part2(&day6::parse_input(&contents).expect(parse_error_msg))),
"7.1" => println!("{}", day7::part1(&day7::parse_input(&contents).expect(parse_error_msg))),
"7.2" => println!("{}", day7::part2(&day7::parse_input(&contents).expect(parse_error_msg))),
"8.1" => println!("{}", day8::part1(&day8::parse_input(&contents))),
"8.2" => println!("{}", day8::part2(&day8::parse_input(&contents))),
"9.1" => println!("{}", day9::part1(day9::parse_input(&contents))),
"9.2" => println!("{}", day9::part2(day9::parse_input(&contents))),
"10.1" => println!("{}", day10::part1(&day10::parse_input(&contents))),
"10.2" => println!("{}", day10::part2(&day10::parse_input(&contents))),
"11.1" => println!("{}", day11::part1(&day11::parse_input(&contents))),
"11.2" => println!("{}", day11::part2(&day11::parse_input(&contents))),
"12.1" => println!("{}", day12::part1(&day12::parse_input(&contents))),
"12.2" => println!("{}", day12::part2(&day12::parse_input(&contents))),
"13.1" => println!("{}", day13::part1(&day13::parse_input(&contents))),
"13.2" => day13::part2(&day13::parse_input(&contents)),
"14.1" => println!("{}", day14::part1(&day14::parse_input(&contents))),
"14.2" => println!("{}", day14::part2(&day14::parse_input(&contents))),
"15.1" => println!("{}", day15::part1(&day15::parse_input(&contents))),
"15.2" => println!("{}", day15::part2(&mut day15::parse_input(&contents))),
"16.1" => println!("{}", day16::part1(&day16::parse_input(&contents))),
"16.2" => println!("{}", day16::part2(&day16::parse_input(&contents))),
"17.1" => println!("{}", day17::part1(&day17::parse_input(&contents))),
"17.2" => println!("{}", day17::part2(&day17::parse_input(&contents))),
"18.1" => println!("{}", day18::part1(&day18::parse_input(&contents))),
"18.2" => println!("{}", day18::part2(&day18::parse_input(&contents))),
"19.1" => println!("{}", day19::part1(&day19::parse_input(&contents))),
"19.2" => println!("{}", day19::part2(&day19::parse_input(&contents))),
"20.1" => println!("{}", day20::part1(&day20::parse_input(&contents))),
"20.2" => println!("{}", day20::part2(&day20::parse_input(&contents))),
"21.1" => println!("{}", day21::part1(&day21::parse_input(&contents))),
"21.2" => println!("{}", day21::part2(&day21::parse_input(&contents))),
"22.1" => println!("{}", day22::part1(&day22::parse_input(&contents))),
"22.2" => println!("{}", day22::part2(day22::parse_input(&contents))),
"23.1" => println!("{}", day23::part1(&day23::parse_input(&contents))),
"23.2" => println!("{}", day23::part2(&day23::parse_input(&contents))),
_ => println!("Day {} part {} not found", day, part)
}
// Janky solution so that I could specify what I want to run
let parse_error_msg = "Failed to parse input";
match format!("{}.{}", day, part).as_str() {
"1.1" => println!(
"{}",
day1::part1(&day1::parse_input(&contents).expect(parse_error_msg))
),
"1.2" => println!(
"{}",
day1::part2(&day1::parse_input(&contents).expect(parse_error_msg))
),
"2.1" => println!(
"{}",
day2::part1(&day2::parse_input(&contents).expect(parse_error_msg))
),
"2.2" => println!(
"{}",
day2::part2(&day2::parse_input(&contents).expect(parse_error_msg))
),
"3.1" => println!(
"{}",
day3::part1(&day3::parse_input(&contents).expect(parse_error_msg))
),
"3.2" => println!(
"{}",
day3::part2(&day3::parse_input(&contents).expect(parse_error_msg))
),
"4.1" => println!(
"{}",
day4::part1(&day4::parse_input(&contents).expect(parse_error_msg))
),
"4.2" => println!(
"{}",
day4::part2(&day4::parse_input(&contents).expect(parse_error_msg))
),
"5.1" => println!(
"{}",
day5::part1(&day5::parse_input(&contents).expect(parse_error_msg))
),
"5.2" => println!(
"{}",
day5::part2(&day5::parse_input(&contents).expect(parse_error_msg))
),
"6.1" => println!(
"{}",
day6::part1(&day6::parse_input(&contents).expect(parse_error_msg))
),
"6.2" => println!(
"{}",
day6::part2(&day6::parse_input(&contents).expect(parse_error_msg))
),
"7.1" => println!(
"{}",
day7::part1(&day7::parse_input(&contents).expect(parse_error_msg))
),
"7.2" => println!(
"{}",
day7::part2(&day7::parse_input(&contents).expect(parse_error_msg))
),
"8.1" => println!("{}", day8::part1(&day8::parse_input(&contents))),
"8.2" => println!("{}", day8::part2(&day8::parse_input(&contents))),
"9.1" => println!("{}", day9::part1(day9::parse_input(&contents))),
"9.2" => println!("{}", day9::part2(day9::parse_input(&contents))),
"10.1" => println!("{}", day10::part1(&day10::parse_input(&contents))),
"10.2" => println!("{}", day10::part2(&day10::parse_input(&contents))),
"11.1" => println!("{}", day11::part1(&day11::parse_input(&contents))),
"11.2" => println!("{}", day11::part2(&day11::parse_input(&contents))),
"12.1" => println!("{}", day12::part1(&day12::parse_input(&contents))),
"12.2" => println!("{}", day12::part2(&day12::parse_input(&contents))),
"13.1" => println!("{}", day13::part1(&day13::parse_input(&contents))),
"13.2" => day13::part2(&day13::parse_input(&contents)),
"14.1" => println!("{}", day14::part1(&day14::parse_input(&contents))),
"14.2" => println!("{}", day14::part2(&day14::parse_input(&contents))),
"15.1" => println!("{}", day15::part1(&day15::parse_input(&contents))),
"15.2" => println!("{}", day15::part2(&mut day15::parse_input(&contents))),
"16.1" => println!("{}", day16::part1(&day16::parse_input(&contents))),
"16.2" => println!("{}", day16::part2(&day16::parse_input(&contents))),
"17.1" => println!("{}", day17::part1(&day17::parse_input(&contents))),
"17.2" => println!("{}", day17::part2(&day17::parse_input(&contents))),
"18.1" => println!("{}", day18::part1(&day18::parse_input(&contents))),
"18.2" => println!("{}", day18::part2(&day18::parse_input(&contents))),
"19.1" => println!("{}", day19::part1(&day19::parse_input(&contents))),
"19.2" => println!("{}", day19::part2(&day19::parse_input(&contents))),
"20.1" => println!("{}", day20::part1(&day20::parse_input(&contents))),
"20.2" => println!("{}", day20::part2(&day20::parse_input(&contents))),
"21.1" => println!("{}", day21::part1(&day21::parse_input(&contents))),
"21.2" => println!("{}", day21::part2(&day21::parse_input(&contents))),
"22.1" => println!("{}", day22::part1(&day22::parse_input(&contents))),
"22.2" => println!("{}", day22::part2(day22::parse_input(&contents))),
"23.1" => println!("{}", day23::part1(&day23::parse_input(&contents))),
"23.2" => println!("{}", day23::part2(&day23::parse_input(&contents))),
_ => println!("Day {} part {} not found", day, part),
}
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
println!("Usage: {} <day> <part> [input-file]", args[0]);
process::exit(0);
}
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
println!("Usage: {} <day> <part> [input-file]", args[0]);
process::exit(0);
}
let day = args[1].parse::<i32>()
.expect("Failed to parse day");
let day = args[1].parse::<i32>().expect("Failed to parse day");
let part = args[2].parse::<i32>()
.expect("Failed to parse part");
let part = args[2].parse::<i32>().expect("Failed to parse part");
let input_filename;
if args.len() > 3 {
input_filename = args[3].clone();
} else {
input_filename = format!("input/{}.txt", day);
}
run(day, part, &input_filename);
let input_filename;
if args.len() > 3 {
input_filename = args[3].clone();
} else {
input_filename = format!("input/{}.txt", day);
}
run(day, part, &input_filename);
}