use std::ops::{Index, IndexMut};
use crate::cell::Cell;
use crate::wall::Wall;
const GRID_SIZE: usize = 16;
pub type Cells = [Cell; GRID_SIZE];
#[derive(Clone, Copy)]
enum GridIterTarget {
Rows,
Columns,
}
pub struct GridIter<'a> {
grid: &'a [Cells; GRID_SIZE],
target: GridIterTarget,
current: usize,
end: usize,
}
impl<'a> GridIter<'a> {
fn new(grid: &'a [Cells; GRID_SIZE], target: GridIterTarget) -> Self {
Self {
grid,
target,
current: 0,
end: match target {
GridIterTarget::Rows => grid.len(),
GridIterTarget::Columns => grid[0].len(),
},
}
}
}
impl<'a> Iterator for GridIter<'a> {
type Item = GridCellIter<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}
let iter = GridCellIter::new(self.grid, self.target, self.current);
self.current += 1;
Some(iter)
}
}
impl<'a> DoubleEndedIterator for GridIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}
let iter = GridCellIter::new(self.grid, self.target, self.end - 1);
self.end -= 1;
Some(iter)
}
}
pub struct GridCellIter<'a> {
grid: &'a [Cells; GRID_SIZE],
target: GridIterTarget,
index: usize,
current: usize,
end: usize,
}
impl<'a> GridCellIter<'a> {
fn new(grid: &'a [Cells; GRID_SIZE], target: GridIterTarget, index: usize) -> Self {
Self {
grid,
target,
index,
current: 0,
end: match target {
GridIterTarget::Rows => grid[0].len(),
GridIterTarget::Columns => grid.len(),
},
}
}
fn get(&self, current: usize) -> &'a Cell {
match self.target {
GridIterTarget::Rows => &self.grid[self.index][current],
GridIterTarget::Columns => &self.grid[current][self.index],
}
}
}
impl<'a> Iterator for GridCellIter<'a> {
type Item = &'a Cell;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}
let cell = self.get(self.current);
self.current += 1;
Some(cell)
}
}
impl<'a> DoubleEndedIterator for GridCellIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.current >= self.end {
return None;
}
let cell = self.get(self.end - 1);
self.end -= 1;
Some(cell)
}
}
#[derive(Debug)]
pub struct Grid([Cells; GRID_SIZE]);
impl Index<usize> for Grid {
type Output = Cells;
fn index(&self, index: usize) -> &Self::Output {
self.0.index(index)
}
}
impl IndexMut<usize> for Grid {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.0.index_mut(index)
}
}
impl Grid {
pub fn new() -> Grid {
Grid([Cells::default(); GRID_SIZE])
}
pub fn is_open_between(&self, (row1, col1): (usize, usize), (row2, col2): (usize, usize)) -> bool {
match (row2 as isize - row1 as isize, col2 as isize - col1 as isize) {
(-1, 0) => {
assert_eq!(self[row1][col1].north, self[row2][col2].south);
self[row1][col1].north == Wall::Open
}
(0, 1) => {
assert_eq!(self[row1][col1].east, self[row2][col2].west);
self[row1][col1].east == Wall::Open
}
(1, 0) => {
assert_eq!(self[row1][col1].south, self[row2][col2].north);
self[row1][col1].south == Wall::Open
}
(0, -1) => {
assert_eq!(self[row1][col1].west, self[row2][col2].east);
self[row1][col1].west == Wall::Open
}
_ => unreachable!("Cells were not adjacent"),
}
}
pub fn open_between(&mut self, (row1, col1): (usize, usize), (row2, col2): (usize, usize)) {
match (row2 as isize - row1 as isize, col2 as isize - col1 as isize) {
(-1, 0) => {
self[row1][col1].north = Wall::Open;
self[row2][col2].south = Wall::Open;
}
(0, 1) => {
self[row1][col1].east = Wall::Open;
self[row2][col2].west = Wall::Open;
}
(1, 0) => {
self[row1][col1].south = Wall::Open;
self[row2][col2].north = Wall::Open;
}
(0, -1) => {
self[row1][col1].west = Wall::Open;
self[row2][col2].east = Wall::Open;
}
_ => unreachable!("Cells were not adjacent"),
}
}
pub fn adjacent_cells(&self, (row, col): (usize, usize)) -> Vec<(usize, usize)> {
let mut positions = Vec::new();
if row > 0 {
positions.push((row - 1, col));
}
if col < self.row_size() - 1 {
positions.push((row, col + 1));
}
if row < self.col_size() - 1 {
positions.push((row + 1, col));
}
if col > 0 {
positions.push((row, col - 1));
}
positions
}
pub fn get(&self, (row, col): (usize, usize)) -> &Cell {
&self[row][col]
}
pub fn row_size(&self) -> usize {
self.0[0].len()
}
pub fn col_size(&self) -> usize {
self.0.len()
}
pub fn first_row(&self) -> GridCellIter<'_> {
self.rows().next().unwrap()
}
pub fn last_col(&self) -> GridCellIter<'_> {
self.cols().next_back().unwrap()
}
pub fn rows(&self) -> GridIter<'_> {
GridIter::new(&self.0, GridIterTarget::Rows)
}
pub fn cols(&self) -> GridIter<'_> {
GridIter::new(&self.0, GridIterTarget::Columns)
}
}