Created
December 22, 2021 11:13
-
-
Save neofight78/8d8c086220c558a41b80bccaf3beb479 to your computer and use it in GitHub Desktop.
Advent of Code 2021 - Day 22: Reactor Reboot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn main() { | |
let steps = parse_steps(INPUT); | |
let regions = execute_steps(&steps); | |
let total = regions.iter().map(|c| c.count_cubes()).sum::<i64>(); | |
let inner_region = Cuboid::new(Vector::new(-50, -50, -50), Vector::new(50, 50, 50)); | |
let steps = once((true, inner_region)) | |
.chain(regions.iter().map(|&r| (false, r))) | |
.collect::<Vec<_>>(); | |
let regions = execute_steps(&steps); | |
let inner_total = | |
inner_region.count_cubes() - regions.iter().map(|c| c.count_cubes()).sum::<i64>(); | |
println!("Within the inner region {} cubes are on.", inner_total); | |
println!("In total {} cubes are on.", total); | |
} | |
fn parse_steps(steps: &str) -> Vec<(bool, Cuboid)> { | |
steps | |
.lines() | |
.map(|l| { | |
let (instruction, cuboid) = l.split_once(' ').unwrap(); | |
let mut ranges = cuboid.split(',').map(|r| { | |
let (_, range) = r.split_once('=').unwrap(); | |
let bounds = range.split_once("..").unwrap(); | |
( | |
bounds.0.parse::<i64>().unwrap(), | |
bounds.1.parse::<i64>().unwrap(), | |
) | |
}); | |
let (from_x, to_x) = ranges.next().unwrap(); | |
let (from_y, to_y) = ranges.next().unwrap(); | |
let (from_z, to_z) = ranges.next().unwrap(); | |
let cuboid = Cuboid::new( | |
Vector::new(from_x, from_y, from_z), | |
Vector::new(to_x, to_y, to_z), | |
); | |
(instruction == "on", cuboid) | |
}) | |
.collect() | |
} | |
fn execute_steps(steps: &[(bool, Cuboid)]) -> Vec<Cuboid> { | |
let mut existing_regions = Vec::new(); | |
for &(switch_on, cuboid) in steps { | |
if switch_on { | |
let mut new_regions = Vec::from([cuboid]); | |
for region in &existing_regions { | |
let mut updated_new_regions = Vec::new(); | |
for new in new_regions { | |
updated_new_regions.append(&mut (new - *region)); | |
} | |
new_regions = updated_new_regions; | |
} | |
existing_regions.append(&mut new_regions); | |
} else { | |
let mut new_regions = Vec::new(); | |
for ®ion in &existing_regions { | |
new_regions.append(&mut (region - cuboid)); | |
} | |
existing_regions = new_regions; | |
} | |
} | |
existing_regions | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
struct Vector { | |
x: i64, | |
y: i64, | |
z: i64, | |
} | |
impl Vector { | |
fn new(x: i64, y: i64, z: i64) -> Self { | |
Self { x, y, z } | |
} | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
struct Cuboid { | |
from: Vector, | |
to: Vector, | |
} | |
impl Cuboid { | |
fn new(from: Vector, to: Vector) -> Self { | |
Self { from, to } | |
} | |
fn count_cubes(&self) -> i64 { | |
((self.from.x - self.to.x).abs() + 1) | |
* ((self.from.y - self.to.y).abs() + 1) | |
* ((self.from.z - self.to.z).abs() + 1) | |
} | |
} | |
impl Sub for Cuboid { | |
type Output = Vec<Self>; | |
fn sub(self, rhs: Self) -> Self::Output { | |
let mut new_regions = Vec::new(); | |
let find_overlap_along_axis = |axis: fn(Vector) -> i64| { | |
let lower_point = i64::max(axis(self.from), axis(rhs.from)); | |
let upper_point = i64::min(axis(self.to), axis(rhs.to)); | |
if lower_point > upper_point { | |
None | |
} else { | |
Some((lower_point, upper_point)) | |
} | |
}; | |
if let Some((x_lower_point, x_upper_point)) = find_overlap_along_axis(|v| v.x) { | |
if let Some((y_lower_point, y_upper_point)) = find_overlap_along_axis(|v| v.y) { | |
if let Some((z_lower_point, z_higher_point)) = find_overlap_along_axis(|v| v.z) { | |
let mut remainder = self; | |
// Split X | |
if x_lower_point > remainder.from.x { | |
let mut to = remainder.to; | |
to.x = x_lower_point - 1; | |
let left = Cuboid::new(remainder.from, to); | |
new_regions.push(left); | |
let mut from = remainder.from; | |
from.x = x_lower_point; | |
remainder = Cuboid::new(from, remainder.to); | |
} | |
if x_upper_point < remainder.to.x { | |
let mut from = remainder.from; | |
from.x = x_upper_point + 1; | |
let right = Cuboid::new(from, remainder.to); | |
new_regions.push(right); | |
let mut to = remainder.to; | |
to.x = x_upper_point; | |
remainder = Cuboid::new(remainder.from, to); | |
} | |
// Split Y | |
if y_lower_point > remainder.from.y { | |
let mut to = remainder.to; | |
to.y = y_lower_point - 1; | |
let left = Cuboid::new(remainder.from, to); | |
new_regions.push(left); | |
let mut from = remainder.from; | |
from.y = y_lower_point; | |
remainder = Cuboid::new(from, remainder.to); | |
} | |
if y_upper_point < remainder.to.y { | |
let mut from = remainder.from; | |
from.y = y_upper_point + 1; | |
let right = Cuboid::new(from, remainder.to); | |
new_regions.push(right); | |
let mut to = remainder.to; | |
to.y = y_upper_point; | |
remainder = Cuboid::new(remainder.from, to); | |
} | |
// Split Z | |
if z_lower_point > remainder.from.z { | |
let mut to = remainder.to; | |
to.z = z_lower_point - 1; | |
let left = Cuboid::new(remainder.from, to); | |
new_regions.push(left); | |
} | |
if z_higher_point < remainder.to.z { | |
let mut from = remainder.from; | |
from.z = z_higher_point + 1; | |
let right = Cuboid::new(from, remainder.to); | |
new_regions.push(right); | |
} | |
return new_regions; | |
} | |
} | |
} | |
new_regions.push(self); | |
new_regions | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment