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.
181 lines
4.0 KiB
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
|
|
})
|
|
}
|
|
}
|