diff --git a/src/day19.rs b/src/day19.rs index a8a0ee4..6daf589 100644 --- a/src/day19.rs +++ b/src/day19.rs @@ -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 { - 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> { - 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 { - 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) -> 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, scanner2: &'b Vec) -> (Vec, Vec) { - let mut common_points1: Vec = Vec::new(); - let mut common_points2: Vec = 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 { + get_point_pairs(a).iter() + .map(|(p1, p2)| calc_diff(p1, p2)) + .collect() } -fn apply_offset_rotation(points: &Vec, offset: &Point, rotation: &RotationMatrix) -> Vec { - 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, points2: Vec) -> Option<(Point, RotationMatrix)> { - points1.sort(); +fn find_possibly_overlapping_scanners(scanners: &[Vec]) -> 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>) { - 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 { + // 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 { - 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>) -> 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>) -> 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 { + 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 { + 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]) -> 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]) -> 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]) -> 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); + } } - diff --git a/src/main.rs b/src/main.rs index f618c10..ed10ad9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 = env::args().collect(); - if args.len() < 3 { - println!("Usage: {} [input-file]", args[0]); - process::exit(0); - } + let args: Vec = env::args().collect(); + if args.len() < 3 { + println!("Usage: {} [input-file]", args[0]); + process::exit(0); + } - let day = args[1].parse::() - .expect("Failed to parse day"); + let day = args[1].parse::().expect("Failed to parse day"); - let part = args[2].parse::() - .expect("Failed to parse part"); + let part = args[2].parse::().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); }