You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
aoc2022/day14/src/grid.rs

181 lines
4.0 KiB

//! Grid of lines and sands
use super::*;
use std::{
collections::BTreeMap,
borrow::Cow,
fmt,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Copy)]
#[repr(u8)]
pub enum Cell
{
#[default]
Air = b'.',
Wall = b'#',
Settled = b'O',
Falling = b'~',
Source = b'+',
}
impl From<Cell> for u8
{
#[inline(always)]
fn from(from: Cell) -> Self
{
from as u8
}
}
impl fmt::Display for Cell
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", u8::from(*self) as char)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Grid
{
map: BTreeMap<Point, Cell>,
}
impl Grid
{
#[inline]
pub const fn new() -> Self
{
Self {
map: BTreeMap::new(),
}
}
#[inline(always)]
pub fn insert_single(&mut self, point: Point, cell: Cell) -> Option<Cell>
{
self.map.insert(point, cell)
}
#[inline]
pub fn draw_line_with<F>(&mut self, line: Line, mut with: F)
where F: for<'a> FnMut(&'a Point) -> Cell
{
for point in line.unroll() {
let cell = with(&point);
self.map.insert(point, cell);
}
}
#[inline]
pub fn draw_line(&mut self, line: Line, cell: Cell)
{
self.draw_line_with(line, move |_| cell)
}
#[inline]
pub fn draw_lines_with<I,F>(&mut self, line: I, mut with: F)
where F: for<'a> FnMut(&'a Point) -> Cell,
I: IntoIterator<Item = Line>
{
for point in line.into_iter().map(|x| x.normalised().unroll()).flatten() {
let cell = with(&point);
self.map.insert(point, cell);
}
}
/// Draw these lines with this cell
#[inline]
pub fn draw_lines(&mut self, lines: impl IntoIterator<Item = Line>, cell: Cell)
{
self.draw_lines_with(lines, move |_| cell)
}
/// Add the source to the grid
#[inline(always)]
pub fn draw_source(&mut self, source: Point)
{
self.map.insert(source, Cell::Source);
}
}
/// Create a new sparse grid, returns the point.
///
/// A sparse grid does not draw air, only the specified `walls`.
#[inline]
pub fn sparse_grid(walls: Lines) -> (grid::Grid, Point)
{
let mut grid = grid::Grid::new();
// Add source
grid.draw_source(ORIGIN_POINT);
// Draw walls
grid.draw_lines(walls, Cell::Wall);
(grid, ORIGIN_POINT)
}
// Non-drawing grid functions
impl Grid
{
/// Number of points in the grid
#[inline]
pub fn len(&self) -> usize
{
self.map.len()
}
/// Find the highest point in the collection
#[inline(always)]
pub fn find_high_bound(&self) -> Point
{
let x = self.map.keys().max_by(|a, b| u64::cmp(&a.x, &b.x)).map(|x| x.x).unwrap_or(0);
let y = self.map.keys().max_by(|a, b| u64::cmp(&a.y, &b.y)).map(|y| y.y).unwrap_or(0);
Point{ x, y }
}
/// Find the lowest point in the collection
#[inline(always)]
pub fn find_low_bound(&self) -> Point
{
let x = self.map.keys().min_by(|a, b| u64::cmp(&a.x, &b.x)).map(|x| x.x).unwrap_or(0);
let y = self.map.keys().min_by(|a, b| u64::cmp(&a.y, &b.y)).map(|y| y.y).unwrap_or(0);
Point{ x, y }
}
/// Find the low and high bound as a flattened line
#[inline]
pub fn find_bound(&self) -> Line
{
let start = self.find_low_bound();
let end = self.find_high_bound();
Line{ start, end }
}
/// Iterate over a sparse grid, bounded by `bound`, any not set cells will be drawn with air.
#[inline]
pub fn iterate_sparse<'g>(&'g self, bound: Line) -> impl Iterator<Item = &'g Cell> + 'g
{
const AIR: Cell = Cell::Air;
//let mut grid = start.map.iter().fuse().peekable();
bound.normalised().unroll().into_iter()
.map(move |point| {
if let Some(cell) = self.map.get(&point) {
// Contains the point, return that reference
return cell;
}
/*
if let Some(&(next_wall, cell)) = grid.peek() {
if next_wall == &point {
// Take this point off the iterator if it matches
return grid.next().unwrap().1;
}
}*/
&AIR
})
}
}