[Rust/2023/22] Add solution

This commit is contained in:
Felix Bargfeldt 2023-12-22 23:16:35 +01:00
parent 41bb20f78b
commit ca9b7cd4d2
Signed by: Defelo
GPG key ID: 2A05272471204DD3
7 changed files with 254 additions and 3 deletions

File diff suppressed because one or more lines are too long

203
Rust/2023/22.rs Normal file
View file

@ -0,0 +1,203 @@
#![feature(test)]
use itertools::Itertools;
use rayon::prelude::*;
type Input = Vec<Brick>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Brick(Pos3, Pos3);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Rect(Pos2, Pos2);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Pos3 {
x: usize,
y: usize,
z: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Pos2 {
x: usize,
y: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Range(usize, usize);
fn setup(input: &str) -> Input {
input
.lines()
.map(|line| {
let (a, b) = line
.split('~')
.map(|pos| {
let (x, y, z) = pos
.split(',')
.map(|x| x.parse().unwrap())
.collect_tuple()
.unwrap();
Pos3 { x, y, z }
})
.collect_tuple()
.unwrap();
Brick(a, b)
})
.collect()
}
impl Brick {
fn can_support(self, other: Self) -> bool {
self.xy_rect().overlaps(other.xy_rect()) && self.1.z < other.0.z
}
fn xy_rect(self) -> Rect {
Rect(self.0.xy(), self.1.xy())
}
fn height(self) -> usize {
self.1.z - self.0.z + 1
}
}
impl Rect {
fn overlaps(self, other: Self) -> bool {
self.x_range().overlaps(other.x_range()) && self.y_range().overlaps(other.y_range())
}
fn x_range(self) -> Range {
Range(self.0.x, self.1.x)
}
fn y_range(self) -> Range {
Range(self.0.y, self.1.y)
}
}
impl Pos3 {
fn xy(self) -> Pos2 {
Pos2 {
x: self.x,
y: self.y,
}
}
}
impl Range {
fn overlaps(self, other: Self) -> bool {
!(self.1 < other.0 || other.1 < self.0)
}
}
struct Graph {
support_matrix: Vec<Vec<bool>>,
support_lst: Vec<Vec<usize>>,
support_rev: Vec<Vec<usize>>,
unstable: Vec<usize>,
}
fn adj_matrix_to_lists(matrix: &[Vec<bool>]) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
let n = matrix.len();
let lst = (0..n)
.into_par_iter()
.map(|i| (0..n).filter(|&j| matrix[i][j]).collect())
.collect();
let rev = (0..n)
.into_par_iter()
.map(|i| (0..n).filter(|&j| matrix[j][i]).collect())
.collect();
(lst, rev)
}
fn build_graph(bricks: &[Brick]) -> Graph {
let n = bricks.len();
let can_support_matrix = bricks
.par_iter()
.map(|&a| bricks.iter().map(|&b| a.can_support(b)).collect_vec())
.collect::<Vec<_>>();
let (can_support_lst, can_support_rev) = adj_matrix_to_lists(&can_support_matrix);
let mut blocked = can_support_rev.iter().map(|x| x.len()).collect_vec();
let mut queue = (0..n).filter(|&i| blocked[i] == 0).collect_vec();
let mut start_z = vec![0; n];
while let Some(p) = queue.pop() {
start_z[p] = can_support_rev[p]
.iter()
.map(|&i| start_z[i] + bricks[i].height())
.max()
.unwrap_or(1);
queue.extend(can_support_lst[p].iter().filter(|&&q| {
blocked[q] -= 1;
blocked[q] == 0
}));
}
let support_matrix = can_support_matrix
.into_par_iter()
.zip(&start_z)
.zip(bricks)
.map(|((can_support, &z1), brick)| {
can_support
.into_iter()
.zip(&start_z)
.map(|(can_support, &z2)| can_support && z1 + brick.height() == z2)
.collect_vec()
})
.collect::<Vec<_>>();
let (support_lst, support_rev) = adj_matrix_to_lists(&support_matrix);
let unstable = (0..n).filter(|&i| support_rev[i].len() == 1).collect();
Graph {
support_matrix,
support_lst,
support_rev,
unstable,
}
}
fn part1(input: &Input) -> usize {
let Graph {
support_matrix,
unstable,
..
} = build_graph(input);
support_matrix
.par_iter()
.filter(|x| !unstable.iter().any(|&i| x[i]))
.count()
}
fn part2(input: &Input) -> usize {
let Graph {
support_matrix,
support_lst,
support_rev,
unstable,
} = build_graph(input);
support_matrix
.par_iter()
.enumerate()
.filter(|(_, x)| unstable.iter().any(|&i| x[i]))
.map(|(i, _)| {
let mut out = 0;
let mut blocked = support_rev.iter().map(|x| x.len()).collect_vec();
let mut queue = vec![i];
while let Some(p) = queue.pop() {
out += 1;
queue.extend(support_lst[p].iter().filter(|&&q| {
blocked[q] -= 1;
blocked[q] == 0
}));
}
out - 1
})
.sum()
}
aoc::main!(2023, 22, ex: 1);

View file

@ -287,3 +287,6 @@ path = "2023/20.rs"
[[bin]]
name = "2023_21"
path = "2023/21.rs"
[[bin]]
name = "2023_22"
path = "2023/22.rs"

View file

@ -1,4 +1,6 @@
use std::ops::Add;
use std::ops::{Add, Sub};
use num::One;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Range<T> {
@ -30,7 +32,34 @@ impl<T> From<std::ops::Range<T>> for Range<T> {
}
}
impl<T: Sub<Output = T> + One> From<Range<T>> for std::ops::RangeInclusive<T> {
fn from(value: Range<T>) -> Self {
value.start..=value.end - T::one()
}
}
impl<T: Add<Output = T> + One> From<std::ops::RangeInclusive<T>> for Range<T> {
fn from(value: std::ops::RangeInclusive<T>) -> Self {
let (start, end) = value.into_inner();
Self {
start,
end: end + T::one(),
}
}
}
impl<T> Range<T> {
pub fn new(start: T, end: T) -> Self {
Self { start, end }
}
pub fn new_inclusive(start: T, end: T) -> Self
where
T: Add<Output = T> + One,
{
Self::new(start, end + T::one())
}
pub fn into_std(self) -> std::ops::Range<T> {
self.into()
}
@ -60,6 +89,13 @@ impl<T> Range<T> {
}
}
pub fn overlaps(&self, other: &Range<T>) -> bool
where
T: PartialOrd,
{
!matches!(self.rel(other), RangeRel::LeftOf | RangeRel::RightOf)
}
pub fn add<N>(&self, n: N) -> Range<<T as Add<N>>::Output>
where
T: Copy + Add<N>,

7
examples/2023/22/1 Normal file
View file

@ -0,0 +1,7 @@
1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9

1
examples/2023/22/1.1 Normal file
View file

@ -0,0 +1 @@
5

1
examples/2023/22/1.2 Normal file
View file

@ -0,0 +1 @@
7