1
0
Fork 0
2022/src/bin/day14.rs
Matej Focko 06243918fc
vectors: return value instead of reference
There was no use-case for returning reference rather than a value and
in majority of case the dereference was needed.

When using 2D/3D vectors, they are mostly initialized with the numbers
and it is contraproductive to pass around references to numbers as they
are more expensive than the copies.

Signed-off-by: Matej Focko <me@mfocko.xyz>
2023-01-07 20:09:35 +01:00

156 lines
3.7 KiB
Rust

use std::{
cmp::max,
collections::{BTreeSet, VecDeque},
str::FromStr,
};
use aoc_2022::*;
type Input = Vec<Line>;
type Output = usize;
type Coord = Vector2D<i32>;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
struct Line {
from: Coord,
to: Coord,
}
#[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 can_move(grain: &Coord, 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) {
return Some(new_position);
}
}
None
}
fn simulate_falling<F>(lines: &Input, terminate: F) -> usize
where
F: Fn(&Coord, bool) -> bool,
{
let mut grains: VecDeque<Coord> = VecDeque::new();
let mut set_grains: BTreeSet<Coord> = BTreeSet::new();
// convert lines to points
for line in lines {
let d = Coord::new(
(line.to.x() - line.from.x()).signum(),
(line.to.y() - line.from.y()).signum(),
);
let mut p = line.from;
while p != line.to {
set_grains.insert(p);
p = p + d;
}
set_grains.insert(p);
}
let lines_count = set_grains.len();
loop {
for i in (0..grains.len()).rev() {
let grain = grains[i];
let mut is_set = false;
if let Some(new_grain) = can_move(&grain, &set_grains) {
grains[i] = new_grain;
} else {
let grain = grains.pop_back().unwrap();
set_grains.insert(grain);
is_set = true;
}
if terminate(&grain, is_set) {
return set_grains.len() - lines_count;
}
}
grains.push_front(Coord::new(500, 0));
}
}
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 max_y = input
.iter()
.map(|l| max(l.from.y(), l.to.y()))
.max()
.unwrap();
let terminate = |grain: &Coord, _is_set: bool| grain.y() >= max_y;
simulate_falling(input, terminate)
}
fn part_2(input: &Input) -> Output {
fn terminate(grain: &Coord, is_set: bool) -> bool {
is_set && grain == &Coord::new(500, 0)
}
let y = input
.iter()
.map(|l| max(l.from.y(), l.to.y()))
.max()
.unwrap();
let mut lines = input.clone();
lines.push(Line {
from: Coord::new(500 - (y + 2), y + 2),
to: Coord::new(500 + (y + 2), y + 2),
});
simulate_falling(&lines, terminate)
}
}
fn main() -> Result<()> {
// Day14::run("sample")
Day14::main()
}
test_sample!(day_14, Day14, 24, 93);