day 22 done part 2 takes too long but works
This commit is contained in:
9
2023/Cargo.lock
generated
9
2023/Cargo.lock
generated
@@ -221,6 +221,15 @@ dependencies = [
|
||||
"rstest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-22"
|
||||
version = "2023.0.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"itertools",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-3"
|
||||
version = "2023.0.0"
|
||||
|
||||
13
2023/day-22/Cargo.toml
Normal file
13
2023/day-22/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "day-22"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom = { workspace = true }
|
||||
itertools = {workspace = true }
|
||||
glam.workspace = true
|
||||
4
2023/day-22/src/lib.rs
Normal file
4
2023/day-22/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod part1;
|
||||
pub use crate::part1::*;
|
||||
pub mod part2;
|
||||
pub use crate::part2::*;
|
||||
12
2023/day-22/src/main.rs
Normal file
12
2023/day-22/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use day_22::part1;
|
||||
use day_22::part2;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let part1_result = part1(input);
|
||||
println!("part 1: {part1_result}");
|
||||
let part2_result = part2(input);
|
||||
println!("part 2: {part2_result}");
|
||||
}
|
||||
156
2023/day-22/src/part1.rs
Normal file
156
2023/day-22/src/part1.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use glam::{UVec3, Vec3Swizzles};
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
bytes::complete::tag, character::complete, multi::separated_list1, sequence::separated_pair,
|
||||
IResult, Parser,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
struct Brick {
|
||||
pub cubes: Vec<UVec3>,
|
||||
}
|
||||
|
||||
impl Brick {
|
||||
fn supports(&self, other: &Self) -> bool {
|
||||
let max_cubes = self.cubes.iter().max_set_by_key(|cube| cube.z);
|
||||
let min_cubes = other.cubes.iter().min_set_by_key(|cube| cube.z);
|
||||
let top = max_cubes[0].z;
|
||||
let bottom = min_cubes[0].z;
|
||||
if top + 1 != bottom {
|
||||
return false;
|
||||
}
|
||||
max_cubes.iter().any(|cube| {
|
||||
min_cubes
|
||||
.iter()
|
||||
.map(|t_cube| t_cube.xy())
|
||||
.contains(&cube.xy())
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn supported_by(&self, other: &Self) -> bool {
|
||||
other.supports(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// day 22 part 1 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
#[must_use]
|
||||
pub fn part1(input: &str) -> String {
|
||||
let (_, mut bricks) = parse_input(input).expect("AOC should have valid input");
|
||||
bricks.sort_by(|a, b| {
|
||||
a.cubes
|
||||
.iter()
|
||||
.map(|cube| cube.z)
|
||||
.min()
|
||||
.unwrap()
|
||||
.cmp(&b.cubes.iter().map(|cube| cube.z).min().unwrap())
|
||||
});
|
||||
//lower the bricks
|
||||
let stacked = bricks.iter().fold(Vec::new(), |mut acc, brick| {
|
||||
let bottom_cubes = brick.cubes.iter().min_set_by_key(|cube| cube.z);
|
||||
let bottom_z = bottom_cubes.iter().map(|cube| cube.z).min().unwrap();
|
||||
let top_underneath_cubes = acc
|
||||
.iter()
|
||||
.flat_map(|settle_brick: &Brick| settle_brick.cubes.iter())
|
||||
.filter_map(|cube| {
|
||||
bottom_cubes
|
||||
.iter()
|
||||
.map(|c| c.xy())
|
||||
.contains(&cube.xy())
|
||||
.then_some(cube.z)
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let step_down = bottom_z - top_underneath_cubes - 1;
|
||||
let new_cubes = brick
|
||||
.cubes
|
||||
.iter()
|
||||
.map(|cube| *cube - (UVec3::Z * step_down))
|
||||
.collect();
|
||||
acc.push(Brick { cubes: new_cubes });
|
||||
|
||||
acc
|
||||
});
|
||||
let stack_len = stacked.len();
|
||||
|
||||
//do the check
|
||||
let undisolvable = stacked
|
||||
.into_iter()
|
||||
.permutations(2)
|
||||
//.inspect(|a| println!("{a:?}"))
|
||||
.filter_map(|a| {
|
||||
let [ref a_brick, ref b_brick] = a[..] else {
|
||||
unimplemented!("should neer reach here")
|
||||
};
|
||||
a_brick
|
||||
.supports(b_brick)
|
||||
.then_some((a_brick.clone(), b_brick.clone()))
|
||||
})
|
||||
.fold(HashMap::new(), |mut acc, (a_brick, b_brick)| {
|
||||
acc.entry(b_brick)
|
||||
.and_modify(|v: &mut Vec<Brick>| v.push(a_brick.clone()))
|
||||
.or_insert(vec![a_brick.clone()]);
|
||||
acc
|
||||
})
|
||||
.values()
|
||||
.filter_map(|v| (v.len() == 1).then_some(v[0].clone()))
|
||||
.unique()
|
||||
//.inspect(|a| println!("{a:?}"))
|
||||
.count();
|
||||
(stack_len - undisolvable).to_string()
|
||||
}
|
||||
|
||||
fn parse_corner(input: &str) -> IResult<&str, UVec3> {
|
||||
let (input, x) = complete::u32(input)?;
|
||||
let (input, _) = tag(",")(input)?;
|
||||
let (input, y) = complete::u32(input)?;
|
||||
let (input, _) = tag(",")(input)?;
|
||||
let (input, z) = complete::u32(input)?;
|
||||
Ok((input, UVec3::new(x, y, z)))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Brick>> {
|
||||
separated_list1(
|
||||
complete::line_ending,
|
||||
separated_pair(parse_corner, tag("~"), parse_corner).map(|(a, b)| {
|
||||
let mut cubes = Vec::new();
|
||||
for x in (a.x.min(b.x))..=(a.x.max(b.x)) {
|
||||
for y in (a.y.min(b.y))..=(a.y.max(b.y)) {
|
||||
for z in (a.z.min(b.z))..=(a.z.max(b.z)) {
|
||||
cubes.push(UVec3::new(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
Brick { cubes }
|
||||
}),
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part1_works() {
|
||||
let result = part1(INPUT);
|
||||
assert_eq!(result, "5".to_string());
|
||||
}
|
||||
}
|
||||
151
2023/day-22/src/part2.rs
Normal file
151
2023/day-22/src/part2.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
use glam::{UVec3, Vec3Swizzles};
|
||||
use itertools::Itertools;
|
||||
use nom::{
|
||||
bytes::complete::tag, character::complete, multi::separated_list1, sequence::separated_pair,
|
||||
IResult, Parser,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
struct Brick {
|
||||
pub cubes: Vec<UVec3>,
|
||||
}
|
||||
|
||||
impl Brick {
|
||||
fn supports(&self, other: &Self) -> bool {
|
||||
let max_cubes = self.cubes.iter().max_set_by_key(|cube| cube.z);
|
||||
let min_cubes = other.cubes.iter().min_set_by_key(|cube| cube.z);
|
||||
let top = max_cubes[0].z;
|
||||
let bottom = min_cubes[0].z;
|
||||
if top + 1 != bottom {
|
||||
return false;
|
||||
}
|
||||
max_cubes.iter().any(|cube| {
|
||||
min_cubes
|
||||
.iter()
|
||||
.map(|t_cube| t_cube.xy())
|
||||
.contains(&cube.xy())
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn supported_by(&self, other: &Self) -> bool {
|
||||
other.supports(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn stacks_up(stack: &[Brick]) -> (Vec<Brick>, usize) {
|
||||
stack
|
||||
.iter()
|
||||
.fold((Vec::new(), 0_usize), |(mut acc, mut dropped), brick| {
|
||||
let bottom_cubes = brick.cubes.iter().min_set_by_key(|cube| cube.z);
|
||||
let bottom_z = bottom_cubes.iter().map(|cube| cube.z).min().unwrap();
|
||||
let top_underneath_cubes = acc
|
||||
.iter()
|
||||
.flat_map(|settle_brick: &Brick| settle_brick.cubes.iter())
|
||||
.filter_map(|cube| {
|
||||
bottom_cubes
|
||||
.iter()
|
||||
.map(|c| c.xy())
|
||||
.contains(&cube.xy())
|
||||
.then_some(cube.z)
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let step_down = bottom_z - top_underneath_cubes - 1;
|
||||
if step_down != 0 {
|
||||
dropped += 1;
|
||||
}
|
||||
let new_cubes = brick
|
||||
.cubes
|
||||
.iter()
|
||||
.map(|cube| *cube - (UVec3::Z * step_down))
|
||||
.collect();
|
||||
acc.push(Brick { cubes: new_cubes });
|
||||
|
||||
(acc, dropped)
|
||||
})
|
||||
}
|
||||
|
||||
/// day 22 part 2 of aoc 2023
|
||||
///
|
||||
/// # Arguments
|
||||
/// - input the input for today's puzzle
|
||||
///
|
||||
/// # Panics
|
||||
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||
#[must_use]
|
||||
pub fn part2(input: &str) -> String {
|
||||
let (_, mut bricks) = parse_input(input).expect("AOC should have valid input");
|
||||
bricks.sort_by(|a, b| {
|
||||
a.cubes
|
||||
.iter()
|
||||
.map(|cube| cube.z)
|
||||
.min()
|
||||
.unwrap()
|
||||
.cmp(&b.cubes.iter().map(|cube| cube.z).min().unwrap())
|
||||
});
|
||||
//lower the bricks
|
||||
let (stacked, _) = stacks_up(&bricks);
|
||||
|
||||
//do the check
|
||||
stacked
|
||||
.iter()
|
||||
.map(|brick| {
|
||||
let to_drop = &stacked
|
||||
.iter()
|
||||
.filter(|b| *b != brick)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let (_, fell) = stacks_up(to_drop);
|
||||
fell
|
||||
})
|
||||
.sum::<usize>()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_corner(input: &str) -> IResult<&str, UVec3> {
|
||||
let (input, x) = complete::u32(input)?;
|
||||
let (input, _) = tag(",")(input)?;
|
||||
let (input, y) = complete::u32(input)?;
|
||||
let (input, _) = tag(",")(input)?;
|
||||
let (input, z) = complete::u32(input)?;
|
||||
Ok((input, UVec3::new(x, y, z)))
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> IResult<&str, Vec<Brick>> {
|
||||
separated_list1(
|
||||
complete::line_ending,
|
||||
separated_pair(parse_corner, tag("~"), parse_corner).map(|(a, b)| {
|
||||
let mut cubes = Vec::new();
|
||||
for x in (a.x.min(b.x))..=(a.x.max(b.x)) {
|
||||
for y in (a.y.min(b.y))..=(a.y.max(b.y)) {
|
||||
for z in (a.z.min(b.z))..=(a.z.max(b.z)) {
|
||||
cubes.push(UVec3::new(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
Brick { cubes }
|
||||
}),
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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";
|
||||
|
||||
#[test]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "7".to_string());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user