day(14): add initial solution
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
bf8c4ddc67
commit
6789a40038
2 changed files with 185 additions and 0 deletions
2
samples/day14.txt
Normal file
2
samples/day14.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
498,4 -> 498,6 -> 496,6
|
||||||
|
503,4 -> 502,4 -> 502,9 -> 494,9
|
183
src/bin/day14.rs
Normal file
183
src/bin/day14.rs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
use std::{
|
||||||
|
cmp::max,
|
||||||
|
collections::{BTreeSet, VecDeque},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use aoc_2022::*;
|
||||||
|
|
||||||
|
use color_eyre::Report;
|
||||||
|
use itertools::Itertools;
|
||||||
|
// use tracing::debug;
|
||||||
|
|
||||||
|
type Input = Vec<Line>;
|
||||||
|
type Output = usize;
|
||||||
|
|
||||||
|
type Coord = Vector2D<i32>;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
|
struct Line {
|
||||||
|
from: Coord,
|
||||||
|
to: Coord,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Line {
|
||||||
|
fn direction(&self) -> Coord {
|
||||||
|
self.to - self.from
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend_to_x(&self, x: i32) -> Option<Coord> {
|
||||||
|
let d = self.direction();
|
||||||
|
|
||||||
|
match d.x() {
|
||||||
|
0 => None,
|
||||||
|
_ => {
|
||||||
|
let k = (self.from.x() - x) / d.x();
|
||||||
|
let y = self.from.y() + k * d.y();
|
||||||
|
|
||||||
|
Some(Coord::new(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, coord: &Coord) -> bool {
|
||||||
|
let mut xs = [self.from.x(), self.to.x()];
|
||||||
|
xs.sort();
|
||||||
|
|
||||||
|
let mut ys = [self.from.y(), self.to.y()];
|
||||||
|
ys.sort();
|
||||||
|
|
||||||
|
(xs[0]..=xs[1]).contains(&coord.x()) && (ys[0]..=ys[1]).contains(&coord.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Multiline {
|
||||||
|
lines: BTreeSet<Line>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Multiline {
|
||||||
|
type Err = Report;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
fn parse_coord(s: &str) -> Result<Coord, Report> {
|
||||||
|
let (x, y) = s.split(',').collect_tuple().unwrap();
|
||||||
|
|
||||||
|
Ok(Coord::new(x.parse()?, y.parse()?))
|
||||||
|
}
|
||||||
|
|
||||||
|
let segs: Vec<Coord> = s
|
||||||
|
.split(" -> ")
|
||||||
|
.map(|c| parse_coord(c).unwrap())
|
||||||
|
.collect_vec();
|
||||||
|
let mut lines: BTreeSet<Line> = BTreeSet::new();
|
||||||
|
|
||||||
|
for i in 0..segs.len() - 1 {
|
||||||
|
lines.insert(Line {
|
||||||
|
from: segs[i],
|
||||||
|
to: segs[i + 1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Multiline { lines })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_falling(grain: &Coord, lines: &[Line], set: &BTreeSet<Coord>) -> bool {
|
||||||
|
set.iter().all(|g| g.x() != grain.x() || g.y() < grain.y())
|
||||||
|
&& lines
|
||||||
|
.iter()
|
||||||
|
.filter_map(|l| l.extend_to_x(*grain.x()))
|
||||||
|
.all(|g| g.y() < grain.y())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_move(grain: &Coord, lines: &[Line], set: &BTreeSet<Coord>) -> Option<Coord> {
|
||||||
|
for direction in [Coord::new(0, 1), Coord::new(-1, 1), Coord::new(1, 1)] {
|
||||||
|
let new_position = *grain + direction;
|
||||||
|
|
||||||
|
if !set.contains(&new_position) && lines.iter().all(|l| !l.contains(&new_position)) {
|
||||||
|
return Some(new_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Day14;
|
||||||
|
impl Solution<Input, Output> for Day14 {
|
||||||
|
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
||||||
|
file_to_structs::<P, Multiline>(pathname)
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|m| m.lines)
|
||||||
|
.collect_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_1(input: &Input) -> Output {
|
||||||
|
let mut grains: VecDeque<Coord> = VecDeque::new();
|
||||||
|
let mut set_grains: BTreeSet<Coord> = BTreeSet::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
for i in (0..grains.len()).rev() {
|
||||||
|
let grain = &mut grains[i];
|
||||||
|
|
||||||
|
if free_falling(grain, input, &set_grains) {
|
||||||
|
return set_grains.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(new_grain) = can_move(grain, input, &set_grains) {
|
||||||
|
*grain = new_grain;
|
||||||
|
} else {
|
||||||
|
let grain = grains.pop_back().unwrap();
|
||||||
|
set_grains.insert(grain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grains.push_front(Coord::new(500, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_2(input: &Input) -> Output {
|
||||||
|
let mut lines = input.clone();
|
||||||
|
let y = lines
|
||||||
|
.iter()
|
||||||
|
.map(|l| max(l.from.y(), l.to.y()))
|
||||||
|
.max()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
lines.push(Line {
|
||||||
|
from: Coord::new(500 - 2 * (*y + 2), *y + 2),
|
||||||
|
to: Coord::new(500 + 2 * (*y + 2), *y + 2),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut grains: VecDeque<Coord> = VecDeque::new();
|
||||||
|
let mut set_grains: BTreeSet<Coord> = BTreeSet::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// debug!("Grains setting: {:?}, set grains: {:?}", grains.len(), set_grains.len());
|
||||||
|
for i in (0..grains.len()).rev() {
|
||||||
|
let grain = &mut grains[i];
|
||||||
|
|
||||||
|
if let Some(new_grain) = can_move(grain, &lines, &set_grains) {
|
||||||
|
*grain = new_grain;
|
||||||
|
} else {
|
||||||
|
let grain = grains.pop_back().unwrap();
|
||||||
|
set_grains.insert(grain);
|
||||||
|
|
||||||
|
// debug!("Set grain {:?}", grain);
|
||||||
|
if grain == Coord::new(500, 0) {
|
||||||
|
return set_grains.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grains.push_front(Coord::new(500, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// Day14::run("sample")
|
||||||
|
Day14::main()
|
||||||
|
}
|
||||||
|
|
||||||
|
test_sample!(day_14, Day14, 24, 93);
|
Loading…
Reference in a new issue