Files
advent2021/src/map.rs
2021-12-11 10:34:15 -08:00

270 lines
5.9 KiB
Rust

use core::fmt;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Oopsie {
#[error("Tried to parse an empty graph?")]
EmptyGraph,
#[error("Got a weird, inconsistent line width around {0}")]
InconsistentGraphWidth(usize),
#[error("Got weird character parsing graph: {0}")]
BadCharacter(char),
}
pub struct Graph<T> {
data: Vec<T>,
width: usize,
height: usize,
}
impl<T: TryFrom<char, Error = Oopsie>> Graph<T> {
pub fn from_file_data(file_data: &str) -> Result<Graph<T>, Oopsie> {
let mut data = Vec::with_capacity(file_data.len());
let mut width = None;
let mut height = 0;
let mut temp_width = 0;
for c in file_data.chars() {
if c == '\n' {
height += 1;
if let Some(x) = width {
if x != temp_width {
return Err(Oopsie::InconsistentGraphWidth(height));
}
} else {
width = Some(temp_width);
}
temp_width = 0;
} else {
data.push(T::try_from(c)?);
temp_width += 1;
}
}
if temp_width != 0 {
height += 1;
}
if height == 0 {
return Err(Oopsie::EmptyGraph);
}
Ok(Graph {
data,
width: width.unwrap(),
height,
})
}
}
impl<T> Graph<T> {
pub fn size(&self) -> usize {
self.width * self.height
}
pub fn get(&self, x: usize, y: usize) -> Option<Point<T>> {
if x >= self.width || y >= self.height {
return None;
}
Some(Point {
x,
y,
value: &self.data[(y * self.width) + x],
})
}
pub fn get_mut(&mut self, x: usize, y: usize) -> Option<PointMut<T>> {
if x >= self.width || y >= self.height {
return None;
}
Some(PointMut {
x,
y,
value: &mut self.data[(y * self.width) + x],
})
}
pub fn coordinates(&self) -> Coord<T> {
Coord {
graph: self,
curx: 0,
cury: 0,
}
}
pub fn points(&self) -> Points<'_, T> {
Points {
graph: self,
curx: 0,
cury: 0,
}
}
pub fn points_mut(&mut self) -> impl Iterator<Item = PointMut<T>> {
let coords: Vec<(usize, usize)> = self.coordinates().collect();
self.data
.iter_mut()
.zip(coords)
.map(|(value, (x, y))| PointMut { x, y, value })
}
pub fn neighbors(&self, x: usize, y: usize) -> Vec<Point<T>> {
let mut retval = Vec::new();
if x > 0 {
retval.push(self.get(x - 1, y).unwrap());
}
if y > 0 {
retval.push(self.get(x, y - 1).unwrap());
}
if let Some(v) = self.get(x + 1, y) {
retval.push(v);
}
if let Some(v) = self.get(x, y + 1) {
retval.push(v);
}
retval
}
pub fn neighbor_points(&mut self, x: usize, y: usize) -> Vec<(usize, usize)> {
let mut retval = Vec::new();
if y > 0 {
if x > 0 {
retval.push((x - 1, y - 1));
}
retval.push((x, y - 1));
if x + 1 < self.width {
retval.push((x + 1, y - 1))
};
}
if x > 0 {
retval.push((x - 1, y))
};
if x + 1 < self.width {
retval.push((x + 1, y))
};
if y + 1 < self.width {
if x > 0 {
retval.push((x - 1, y + 1))
};
retval.push((x, y + 1));
if x + 1 < self.width {
retval.push((x + 1, y + 1))
};
}
retval
}
}
impl<T: fmt::Display> Graph<T> {
pub fn print(&self) {
for y in 0..self.height {
for x in 0..self.width {
print!("{}", self.get(x, y).unwrap().value);
}
println!();
}
}
}
#[derive(Clone)]
pub struct Point<'a, T> {
pub x: usize,
pub y: usize,
pub value: &'a T,
}
impl<'a, T> fmt::Debug for Point<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({},{})", self.x, self.y)
}
}
impl<'a, T> PartialEq for Point<'a, T> {
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
pub struct PointMut<'a, T> {
pub x: usize,
pub y: usize,
pub value: &'a mut T,
}
impl<'a, T> fmt::Debug for PointMut<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({},{})", self.x, self.y)
}
}
impl<'a, T> PartialEq for PointMut<'a, T> {
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
pub struct Coord<'a, T> {
graph: &'a Graph<T>,
curx: usize,
cury: usize,
}
impl<'a, T> Iterator for Coord<'a, T> {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.cury == self.graph.height {
return None;
}
let next_value = (self.curx, self.cury);
self.curx += 1;
if self.curx == self.graph.width {
self.curx = 0;
self.cury += 1;
}
Some(next_value)
}
}
pub struct Points<'a, T> {
graph: &'a Graph<T>,
curx: usize,
cury: usize,
}
impl<'a, T> Iterator for Points<'a, T>
where
T: Clone + TryFrom<char, Error = Oopsie>,
{
type Item = Point<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
if self.cury == self.graph.height {
return None;
}
let next_value = self.graph.get(self.curx, self.cury)?;
self.curx += 1;
if self.curx == self.graph.width {
self.curx = 0;
self.cury += 1;
}
Some(next_value)
}
}