day 14
This commit is contained in:
parent
c0407504c4
commit
e9444779ce
|
@ -3,6 +3,8 @@ use std::{cmp::Ordering, fmt::Debug};
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use itertools::{EitherOrBoth, Itertools};
|
use itertools::{EitherOrBoth, Itertools};
|
||||||
|
|
||||||
|
use crate::util::parse_num;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
enum Data {
|
enum Data {
|
||||||
Integer(u32),
|
Integer(u32),
|
||||||
|
@ -68,13 +70,8 @@ impl Data {
|
||||||
let rest2 = rest2.strip_prefix(',')?;
|
let rest2 = rest2.strip_prefix(',')?;
|
||||||
rest = rest2;
|
rest = rest2;
|
||||||
}
|
}
|
||||||
} else if s.chars().next()?.is_alphanumeric() {
|
} else if let Some((num, rest)) = parse_num(s) {
|
||||||
let (num_part, rest) = s
|
Some((Self::Integer(num), rest))
|
||||||
.char_indices()
|
|
||||||
.find_map(|(i, c)| (!c.is_numeric()).then_some(s.split_at(i)))
|
|
||||||
.unwrap_or((s, ""));
|
|
||||||
|
|
||||||
Some((Self::Integer(num_part.parse().ok()?), rest))
|
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
158
src/days/day14.rs
Normal file
158
src/days/day14.rs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
use std::{collections::HashSet, ops::Add};
|
||||||
|
|
||||||
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::util::parse_num;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
struct Coord {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Coord {
|
||||||
|
fn parse(s: &str) -> Option<(Self, &str)> {
|
||||||
|
let (x, s) = parse_num(s)?;
|
||||||
|
let s = s.strip_prefix(',')?;
|
||||||
|
let (y, s) = parse_num(s)?;
|
||||||
|
Some((Self { x, y }, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<(i32, i32)> for Coord {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: (i32, i32)) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x + rhs.0,
|
||||||
|
y: self.y + rhs.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Path(Vec<Coord>);
|
||||||
|
|
||||||
|
impl Path {
|
||||||
|
fn parse(s: &str) -> Option<(Self, &str)> {
|
||||||
|
let (first, mut s) = Coord::parse(s)?;
|
||||||
|
let mut vec = Vec::from([first]);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let Some(s2) = s.strip_prefix(" -> ") else {
|
||||||
|
return Some((Self(vec), s))
|
||||||
|
};
|
||||||
|
let (coord, s2) = Coord::parse(s2)?;
|
||||||
|
|
||||||
|
if coord.x != vec.last().unwrap().x && coord.y != vec.last().unwrap().y {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec.push(coord);
|
||||||
|
s = s2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersects(&self, coord: Coord) -> bool {
|
||||||
|
for (a, b) in self.0.iter().tuple_windows() {
|
||||||
|
if a.y == b.y {
|
||||||
|
if coord.y == a.y
|
||||||
|
&& ((a.x <= coord.x && coord.x <= b.x) || (b.x <= coord.x && coord.x <= a.x))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if a.x == b.x {
|
||||||
|
if coord.x == a.x
|
||||||
|
&& ((a.y <= coord.y && coord.y <= b.y) || (b.y <= coord.y && coord.y <= a.y))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc_generator(day14)]
|
||||||
|
fn generator(input: &str) -> Vec<Path> {
|
||||||
|
input.lines().map(|l| Path::parse(l).unwrap().0).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_blocked(rocks: &[Path], sand: &HashSet<Coord>, coord: Coord) -> bool {
|
||||||
|
sand.contains(&coord) || rocks.iter().any(|path| path.intersects(coord))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day14, part1)]
|
||||||
|
fn part1(input: &[Path]) -> usize {
|
||||||
|
let abyss_depth = input
|
||||||
|
.iter()
|
||||||
|
.flat_map(|path| path.0.iter().map(|coord| coord.y))
|
||||||
|
.max()
|
||||||
|
.unwrap()
|
||||||
|
+ 1; // for good measure
|
||||||
|
|
||||||
|
let mut sand = HashSet::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut particle = Coord { x: 500, y: 0 };
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if !is_blocked(input, &sand, particle + (0, 1)) {
|
||||||
|
particle = particle + (0, 1);
|
||||||
|
} else if !is_blocked(input, &sand, particle + (-1, 1)) {
|
||||||
|
particle = particle + (-1, 1);
|
||||||
|
} else if !is_blocked(input, &sand, particle + (1, 1)) {
|
||||||
|
particle = particle + (1, 1);
|
||||||
|
} else {
|
||||||
|
sand.insert(particle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if particle.y >= abyss_depth {
|
||||||
|
return sand.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day14, part2)]
|
||||||
|
fn part2(input: &[Path]) -> usize {
|
||||||
|
let floor_depth = input
|
||||||
|
.iter()
|
||||||
|
.flat_map(|path| path.0.iter().map(|coord| coord.y))
|
||||||
|
.max()
|
||||||
|
.unwrap()
|
||||||
|
+ 2;
|
||||||
|
|
||||||
|
let mut sand = HashSet::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut particle = Coord { x: 500, y: 0 };
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if particle.y + 1 == floor_depth {
|
||||||
|
sand.insert(particle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_blocked(input, &sand, particle + (0, 1)) {
|
||||||
|
particle = particle + (0, 1);
|
||||||
|
} else if !is_blocked(input, &sand, particle + (-1, 1)) {
|
||||||
|
particle = particle + (-1, 1);
|
||||||
|
} else if !is_blocked(input, &sand, particle + (1, 1)) {
|
||||||
|
particle = particle + (1, 1);
|
||||||
|
} else {
|
||||||
|
sand.insert(particle);
|
||||||
|
|
||||||
|
if particle == (Coord { x: 500, y: 0 }) {
|
||||||
|
return sand.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
mod util;
|
||||||
|
|
||||||
use aoc_runner_derive::aoc_lib;
|
use aoc_runner_derive::aoc_lib;
|
||||||
|
|
||||||
mod days {
|
mod days {
|
||||||
|
@ -15,6 +17,7 @@ mod days {
|
||||||
mod day11;
|
mod day11;
|
||||||
mod day12;
|
mod day12;
|
||||||
mod day13;
|
mod day13;
|
||||||
|
mod day14;
|
||||||
}
|
}
|
||||||
|
|
||||||
aoc_lib! { year = 2022 }
|
aoc_lib! { year = 2022 }
|
||||||
|
|
10
src/util.rs
Normal file
10
src/util.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub fn parse_num<T: FromStr>(s: &str) -> Option<(T, &str)> {
|
||||||
|
let (num_part, rest) = s
|
||||||
|
.char_indices()
|
||||||
|
.find_map(|(i, c)| (!c.is_numeric()).then_some(s.split_at(i)))
|
||||||
|
.unwrap_or((s, ""));
|
||||||
|
|
||||||
|
Some((num_part.parse().ok()?, rest))
|
||||||
|
}
|
Loading…
Reference in a new issue