adventofcode2022/day14/common.rs

164 lines
5.6 KiB
Rust

// Copyright 2022 Christian Ulrich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use std::io;
use std::ops::Range;
pub struct CaveScan {
tiles: Vec<Vec<bool>>,
origin: (usize, usize),
x_window: Range<usize>,
y_window: Range<usize>,
}
pub struct Falls<'a> {
scan: &'a mut CaveScan,
}
impl CaveScan {
fn insert_hline(&mut self, start: (usize, usize), end: (usize, usize)) {
assert!(start.1 == end.1);
for i in start.0..=end.0 {
self.set_tile((i, start.1), true);
}
}
fn insert_vline(&mut self, start: (usize, usize), end: (usize, usize)) {
assert!(start.0 == end.0);
for i in start.1..=end.1 {
self.set_tile((start.0, i), true);
}
}
pub fn from_stdin(origin: (usize, usize)) -> Result<CaveScan, &'static str> {
const ERROR: &str = "input file contains invalid line";
let mut result = Self {
tiles: vec![vec![false]],
origin: origin,
x_window: origin.0..origin.0,
y_window: origin.1..origin.1,
};
for line in io::stdin().lines() {
let line = match line {
Ok(line) if { line.len() > 0 } => line,
_ => break,
};
let points: Result<Vec<(usize, usize)>, _> = line
.split(" -> ")
.map(|part| part.split_once(',').ok_or(ERROR))
.map(|split| {
Ok((
split?.0.parse().map_err(|_| ERROR)?,
split?.1.parse().map_err(|_| ERROR)?,
))
})
.collect();
for points in points?.windows(2) {
let start = points.get(0).ok_or(ERROR)?;
let end = points.get(1).ok_or(ERROR)?;
match (start, end) {
(s, e) if { s.0 == e.0 && s.1 > e.1 } => result.insert_vline(*e, *s),
(s, e) if { s.0 == e.0 } => result.insert_vline(*s, *e),
(s, e) if { s.1 == e.1 && s.0 > e.0 } => result.insert_hline(*e, *s),
(s, e) if { s.1 == e.1 } => result.insert_hline(*s, *e),
_ => return Err(ERROR),
}
}
}
Ok(result)
}
fn bottom(&self) -> usize {
self.tiles.len()
}
fn tile(&self, pos: (usize, usize)) -> bool {
if pos.0 < self.x_window.start
|| pos.0 > self.x_window.end
|| pos.1 < self.y_window.start
|| pos.1 > self.y_window.end
{
return false;
}
self.tiles[pos.1 - self.y_window.start][pos.0 - self.x_window.start]
}
fn set_tile(&mut self, pos: (usize, usize), value: bool) {
if !self.x_window.contains(&pos.0) {
let new_x_window =
usize::min(self.x_window.start, pos.0)..usize::max(self.x_window.end, pos.0);
for line in self.tiles.iter_mut() {
let old_line = line.clone();
*line = vec![false; new_x_window.len() + 1];
let x_offset = self.x_window.start - new_x_window.start;
line[x_offset..old_line.len() + x_offset].copy_from_slice(&old_line[..]);
}
self.x_window = new_x_window;
}
if !self.y_window.contains(&pos.1) {
let new_y_window =
usize::min(self.y_window.start, pos.1)..usize::max(self.y_window.end, pos.1);
let head = vec![
vec![false; self.x_window.len() + 1];
new_y_window.start - self.y_window.start
];
let tail =
vec![vec![false; self.x_window.len() + 1]; new_y_window.end - self.y_window.end];
self.tiles = [&head[..], &self.tiles[..], &tail[..]].concat();
self.y_window = new_y_window;
}
self.tiles[pos.1 - self.y_window.start][pos.0 - self.x_window.start] = value;
}
fn step_down(&self, origin: (usize, usize)) -> Option<(usize, usize)> {
let down_pos = (origin.0, origin.1.checked_add(1)?);
let down_left_pos = (origin.0.checked_sub(1)?, down_pos.1);
let down_right_pos = (origin.0.checked_add(1)?, down_pos.1);
let result = match (
self.tile(down_left_pos),
self.tile(down_pos),
self.tile(down_right_pos),
) {
(_, false, _) => Some(down_pos),
(false, true, _) => Some(down_left_pos),
(true, true, false) => Some(down_right_pos),
(true, true, true) => None,
};
result
}
pub fn falls(&mut self) -> Falls {
Falls { scan: self }
}
}
impl Iterator for Falls<'_> {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {
let mut result = self.scan.origin;
while let Some(pos) = self.scan.step_down(result) {
result = pos;
if result.1 == self.scan.bottom() {
return Some((result.0, usize::MAX));
}
}
self.scan.set_tile(result, true);
Some(result)
}
}