2021 day22/rust: add solution
This commit is contained in:
parent
e5ae9b546a
commit
e3c6a8f29c
2 changed files with 176 additions and 0 deletions
9
2021/day22/day22_rs/Cargo.toml
Normal file
9
2021/day22/day22_rs/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "day22_rs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom = "7.1.0"
|
167
2021/day22/day22_rs/src/main.rs
Normal file
167
2021/day22/day22_rs/src/main.rs
Normal file
|
@ -0,0 +1,167 @@
|
|||
#![warn(clippy::pedantic)]
|
||||
use std::{
|
||||
cmp::{max, min},
|
||||
io::{stdin, BufRead},
|
||||
ops::RangeInclusive,
|
||||
};
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::tag,
|
||||
character::complete::i64,
|
||||
combinator::map,
|
||||
error::ParseError,
|
||||
sequence::{pair, preceded, separated_pair, terminated, tuple},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct Cuboid {
|
||||
x: RangeInclusive<i64>,
|
||||
y: RangeInclusive<i64>,
|
||||
z: RangeInclusive<i64>,
|
||||
}
|
||||
|
||||
fn range_len(range: &RangeInclusive<i64>) -> u64 {
|
||||
(range.end() - range.start() + 1).try_into().unwrap_or(0)
|
||||
}
|
||||
|
||||
fn range_intersect(a: &RangeInclusive<i64>, b: &RangeInclusive<i64>) -> RangeInclusive<i64> {
|
||||
max(*a.start(), *b.start())..=min(*a.end(), *b.end())
|
||||
}
|
||||
|
||||
impl Cuboid {
|
||||
pub fn cubes(&self) -> u64 {
|
||||
range_len(&self.x) * range_len(&self.y) * range_len(&self.z)
|
||||
}
|
||||
|
||||
pub fn intersect(&self, other: &Cuboid) -> Option<Cuboid> {
|
||||
let x = range_intersect(&self.x, &other.x);
|
||||
let y = range_intersect(&self.y, &other.y);
|
||||
let z = range_intersect(&self.z, &other.z);
|
||||
|
||||
if x.is_empty() || y.is_empty() || z.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Self { x, y, z })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_small_range(&self) -> bool {
|
||||
let check = |r: &RangeInclusive<i64>| -50 <= *r.start() && *r.end() <= 50;
|
||||
check(&self.x) && check(&self.y) && check(&self.z)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum CubeState {
|
||||
Off,
|
||||
On,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct CuboidSpec {
|
||||
state: CubeState,
|
||||
cuboid: Cuboid,
|
||||
}
|
||||
|
||||
impl CuboidSpec {
|
||||
fn parse<'input, E: ParseError<&'input str>>(
|
||||
input: &'input str,
|
||||
) -> nom::IResult<&'input str, Self, E> {
|
||||
let dimension_range = |dim| {
|
||||
map(
|
||||
preceded(
|
||||
pair(tag(dim), tag("=")),
|
||||
separated_pair(i64, tag(".."), i64),
|
||||
),
|
||||
|(start, end)| start..=end,
|
||||
)
|
||||
};
|
||||
|
||||
map(
|
||||
separated_pair(
|
||||
alt((
|
||||
map(tag("off"), |_| CubeState::Off),
|
||||
map(tag("on"), |_| CubeState::On),
|
||||
)),
|
||||
tag(" "),
|
||||
map(
|
||||
tuple((
|
||||
terminated(dimension_range("x"), tag(",")),
|
||||
terminated(dimension_range("y"), tag(",")),
|
||||
dimension_range("z"),
|
||||
)),
|
||||
|(x, y, z)| Cuboid { x, y, z },
|
||||
),
|
||||
),
|
||||
|(state, cuboid)| Self { state, cuboid },
|
||||
)(input)
|
||||
}
|
||||
}
|
||||
|
||||
fn count_intersecting(first: &Cuboid, rest: impl IntoIterator<Item = Cuboid>) -> u64 {
|
||||
let intersections = rest.into_iter().filter_map(|c| c.intersect(first));
|
||||
count_union(intersections, Entry::Use)
|
||||
}
|
||||
|
||||
enum Entry {
|
||||
Ignore(Cuboid),
|
||||
Use(Cuboid),
|
||||
}
|
||||
|
||||
fn count_union<T>(cuboids: impl IntoIterator<Item = T>, get_cuboid: impl Fn(T) -> Entry) -> u64 {
|
||||
cuboids
|
||||
.into_iter()
|
||||
.scan(Vec::new(), |previous, x| match get_cuboid(x) {
|
||||
Entry::Ignore(c) => {
|
||||
previous.push(c);
|
||||
Some(0)
|
||||
}
|
||||
Entry::Use(c) => {
|
||||
let count = c.cubes() - count_intersecting(&c, previous.iter().cloned());
|
||||
previous.push(c);
|
||||
Some(count)
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut small_range = None;
|
||||
let mut lit_cubes: u64 = 0;
|
||||
let mut placed_cuboids: Vec<CuboidSpec> = Vec::new();
|
||||
|
||||
for line in stdin().lock().lines() {
|
||||
let spec = CuboidSpec::parse::<()>(&line.unwrap()).unwrap().1;
|
||||
|
||||
if !spec.cuboid.in_small_range() {
|
||||
small_range.get_or_insert(lit_cubes);
|
||||
}
|
||||
|
||||
// all overlaps with previous cuboids
|
||||
let intersections = placed_cuboids.iter().cloned().filter_map(|mut s| {
|
||||
s.cuboid = s.cuboid.intersect(&spec.cuboid)?;
|
||||
Some(s)
|
||||
});
|
||||
|
||||
// count the cubes that overlap with currently-ON cubes
|
||||
let overlap = count_union(intersections.rev(), |s| {
|
||||
(if s.state == CubeState::On {
|
||||
Entry::Use
|
||||
} else {
|
||||
Entry::Ignore
|
||||
})(s.cuboid)
|
||||
});
|
||||
|
||||
if let CubeState::On = spec.state {
|
||||
let size = spec.cuboid.cubes();
|
||||
lit_cubes += size;
|
||||
};
|
||||
lit_cubes -= overlap;
|
||||
|
||||
placed_cuboids.push(spec);
|
||||
}
|
||||
|
||||
println!("{}", small_range.unwrap());
|
||||
println!("{lit_cubes}");
|
||||
}
|
Loading…
Reference in a new issue