words words
words words
TODO words
use crate::space::point::Point2d;
pub struct Space2d<Star> {
data: Vec<Vec<Star>>,
x_len: usize,
y_len: usize,
x_offset: isize,
y_offset: isize
}
impl<Star: std::clone::Clone> Space2d<Star> {
#[allow(dead_code)]
pub fn new_grid_sized (default_object: Star, num_rows: usize, num_cols: usize) -> Space2d<Star> {
return Space2d {
data: vec![vec![default_object; num_cols]; num_rows],
x_len: num_cols,
y_len: num_rows,
x_offset: 0,
y_offset: 0
};
}
}
impl Space2d<String> {
pub fn new_from_string (input: String) -> Space2d<String> {
let data = input.lines()
.map(|row| row.chars().map(|x| x.to_string()).collect::<Vec<_>>())
.collect::<Vec<_>>();
return Space2d {
x_len: data[0].len(),
y_len: data.len(),
x_offset: 0,
y_offset: 0,
data: data
};
}
pub fn new_from_file (file_path: &str) -> Space2d<String> {
let file_contents = std::fs::read_to_string(file_path)
.expect(&format!("Could not find \"{}\". Did you make a typo?", file_path));
return Space2d::new_from_string(file_contents);
}
}
impl<Star> Space2d<Star> {
#[allow(dead_code)]
// get 1d vectors from different things
// get row, get col, get height, get xxx
// insert vectors that nudge like things to the side
// insert row, col, height, etc
// remove row, col, etc, etc
// rotate and reflect
pub fn indices (&self) -> impl Iterator<Item = Point2d> + '_ {
(self.y_offset..(self.y_offset + self.y_len as isize)).flat_map(move |y|
(self.x_offset..(self.x_offset + self.x_len as isize)).map(move |x|
Point2d::new(x, y)
)
)
}
pub fn point_is_in_bounds (&self, point: &Point2d) -> bool {
return true
&& self.x_offset <= point.x() && point.x() < self.x_offset + self.x_len as isize
&& self.y_offset <= point.y() && point.y() < self.y_offset + self.y_len as isize;
}
pub fn get (&self, point: &Point2d) -> Option<&Star> {
if self.point_is_in_bounds(point) {
Some(&self.data[(point.y() + self.y_offset) as usize][(point.x() + self.x_offset) as usize])
} else {
None
}
}
pub fn set (&mut self, point: &Point2d, value: Star) {
if self.point_is_in_bounds(point) {
self.data[(point.y() + self.y_offset) as usize][(point.x() + self.x_offset) as usize] = value;
} else {
// Do nothing
}
}
pub fn num_cols (&self) -> usize { self.x_len }
pub fn num_rows (&self) -> usize { self.y_len }
// pub fn x_offset (&self) -> usize { self.x_offset }
// pub fn y_offset (&self) -> usize { self.y_offset }
}
impl<Star: std::cmp::PartialEq> Space2d<Star> {
#[allow(dead_code)]
pub fn find_all <'a> (&'a self, needle: &'a Star) -> impl Iterator<Item = Point2d> + 'a {
return self.indices().filter(move |x| self.get(x).unwrap() == needle);
}
}
impl<Star: std::string::ToString> Space2d<Star> {
#[allow(dead_code)]
pub fn print_compact (&self) {
for row in self.y_offset..(self.y_offset + self.y_len as isize) {
for col in self.x_offset..(self.x_offset + self.x_len as isize) {
print!("{}", self.get(&Point2d::new(col, row)).unwrap().to_string().chars().nth(0).unwrap());
}
println!();
}
}
}
const DIMENSIONS_2D_SIZE: usize = 2;
const DIMENSIONS_2D_ORIGIN: Point2d = Point2d { coordinates: [ 0, 0] };
const DIMENSIONS_2D_DIRECTION_EAST: Point2d = Point2d { coordinates: [ 1, 0] };
const DIMENSIONS_2D_DIRECTION_WEST: Point2d = Point2d { coordinates: [-1, 0] };
const DIMENSIONS_2D_DIRECTION_NORTH: Point2d = Point2d { coordinates: [0, 1] };
const DIMENSIONS_2D_DIRECTION_SOUTH: Point2d = Point2d { coordinates: [0, -1] };
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct Point2d {
coordinates: [isize; DIMENSIONS_2D_SIZE]
}
impl Point2d {
const fn dimensions () -> usize { DIMENSIONS_2D_SIZE }
pub fn dimension_value (&self, dimension: usize) -> isize { self.coordinates[dimension] }
pub fn x (&self) -> isize { self.coordinates[0] }
pub fn y (&self) -> isize { self.coordinates[1] }
}
// Took great inspiration from ndarray's macro
// https://docs.rs/ndarray/latest/src/ndarray/impl_ops.rs.html#53
macro_rules! impl_elementwise_operation {
($type1:ty, $type2:ty, $std_ops_name:ident, $fn_name:ident, $operator:tt) => {
// Both owned
impl std::ops::$std_ops_name<$type2> for $type1 {
type Output = $type1;
fn $fn_name (self, other: $type2) -> $type1 {
let mut coordinates = [0; Self::Output::dimensions()];
for i in 0..Self::Output::dimensions() {
coordinates[i] = self.dimension_value(i) $operator other.dimension_value(i);
}
Self::Output { coordinates }
}
}
// lhs is reference
impl<'a> std::ops::$std_ops_name<$type2> for &'a $type1 {
type Output = $type1;
fn $fn_name (self, other: $type2) -> $type1 {
let mut coordinates = [0; Self::Output::dimensions()];
for i in 0..Self::Output::dimensions() {
coordinates[i] = self.dimension_value(i) $operator other.dimension_value(i);
}
Self::Output { coordinates }
}
}
// rhs is reference
impl<'b> std::ops::$std_ops_name<&'b $type2> for $type1 {
type Output = $type1;
fn $fn_name (self, other: &'b $type2) -> $type1 {
let mut coordinates = [0; Self::Output::dimensions()];
for i in 0..Self::Output::dimensions() {
coordinates[i] = self.dimension_value(i) $operator other.dimension_value(i);
}
Self::Output { coordinates }
}
}
// Both references
impl<'a, 'b> std::ops::$std_ops_name<&'b $type2> for &'a $type1 {
type Output = $type1;
fn $fn_name (self, other: &'b $type2) -> $type1 {
let mut coordinates = [0; Self::Output::dimensions()];
for i in 0..Self::Output::dimensions() {
coordinates[i] = self.dimension_value(i) $operator other.dimension_value(i);
}
Self::Output { coordinates }
}
}
}
}
pub trait FakeDimensions {
fn dimension_value (self, dimension: usize) -> Self;
}
impl FakeDimensions for isize {
fn dimension_value (self, _dimension: usize) -> Self { self }
}
impl_elementwise_operation!(Point2d, isize, Add, add, +);
impl_elementwise_operation!(Point2d, isize, Sub, sub, -);
impl_elementwise_operation!(Point2d, isize, Mul, mul, *);
impl_elementwise_operation!(Point2d, isize, Div, div, /);
impl_elementwise_operation!(Point2d, isize, Rem, rem, %);
impl_elementwise_operation!(Point2d, Point2d, Add, add, +);
impl_elementwise_operation!(Point2d, Point2d, Sub, sub, -);
impl Point2d {
pub fn origin () -> Point2d { DIMENSIONS_2D_ORIGIN }
pub fn cardinal_north () -> Point2d { DIMENSIONS_2D_DIRECTION_NORTH }
pub fn cardinal_east () -> Point2d { DIMENSIONS_2D_DIRECTION_EAST }
pub fn cardinal_south () -> Point2d { DIMENSIONS_2D_DIRECTION_SOUTH }
pub fn cardinal_west () -> Point2d { DIMENSIONS_2D_DIRECTION_WEST }
}
impl Point2d {
#[allow(dead_code)]
pub fn new (x: isize, y: isize) -> Point2d {
Point2d { coordinates: [x, y] }
}
// Higher dimensions include up, down, ana, kata
pub fn north (&self) -> Point2d { self + DIMENSIONS_2D_DIRECTION_NORTH }
pub fn east (&self) -> Point2d { self + DIMENSIONS_2D_DIRECTION_EAST }
pub fn south (&self) -> Point2d { self + DIMENSIONS_2D_DIRECTION_SOUTH }
pub fn west (&self) -> Point2d { self + DIMENSIONS_2D_DIRECTION_WEST }
pub fn neighbor_cross (&self) -> impl Iterator<Item = Point2d> + '_ {
return vec![self.north(), self.east(), self.west(), self.south()].into_iter();
}
pub fn neighbor_block (&self) -> impl Iterator<Item = Point2d> + '_ {
return vec![
self.north().west(), self.north(), self.north().east(),
self.west(), self.east(),
self.south().west(), self.south(), self.south().east()
].into_iter();
}
}
impl Point2d {
#[allow(dead_code)]
pub fn points_in_bounding_box <'a> (start: &'a Point2d, end: &'a Point2d) -> impl Iterator<Item = Point2d> + 'a {
(start.y()..=end.y()).flat_map(move |y|
(start.x()..=end.x()).map(move |x|
Point2d::new(x, y)
)
)
}
}
// https://stackoverflow.com/questions/77588838/how-to-create-a-custom-hash-function-in-rust
impl std::hash::Hash for Point2d {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let x = (self.x().unsigned_abs() * 2 + (self.x().signum() - 1).unsigned_abs() / 2) as u64;
let y = (self.y().unsigned_abs() * 2 + (self.y().signum() - 1).unsigned_abs() / 2) as u64;
/* szudziks function */
let hash_val = if x >= y { x * x + x + y } else { x + y * y };
state.write_u64(hash_val);
}
}
impl std::fmt::Display for Point2d {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "({}, {})", self.x(), self.y())
}
}