terrain_forge/
grid.rs

1//! Core grid and cell types for terrain generation
2
3use std::ops::{Index, IndexMut};
4
5/// Trait for grid cells
6pub trait Cell: Clone + Default {
7    fn is_passable(&self) -> bool;
8}
9
10/// Basic tile type for dungeon/terrain generation
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
12pub enum Tile {
13    #[default]
14    Wall,
15    Floor,
16}
17
18impl Tile {
19    pub fn is_wall(&self) -> bool {
20        matches!(self, Tile::Wall)
21    }
22    pub fn is_floor(&self) -> bool {
23        matches!(self, Tile::Floor)
24    }
25}
26
27impl Cell for Tile {
28    fn is_passable(&self) -> bool {
29        self.is_floor()
30    }
31}
32
33/// 2D grid of cells
34#[derive(Debug, Clone)]
35pub struct Grid<C: Cell = Tile> {
36    width: usize,
37    height: usize,
38    cells: Vec<C>,
39}
40
41impl<C: Cell> Grid<C> {
42    pub fn new(width: usize, height: usize) -> Self {
43        Self {
44            width,
45            height,
46            cells: vec![C::default(); width * height],
47        }
48    }
49
50    pub fn width(&self) -> usize {
51        self.width
52    }
53    pub fn height(&self) -> usize {
54        self.height
55    }
56
57    pub fn in_bounds(&self, x: i32, y: i32) -> bool {
58        x >= 0 && y >= 0 && (x as usize) < self.width && (y as usize) < self.height
59    }
60
61    pub fn get(&self, x: i32, y: i32) -> Option<&C> {
62        if self.in_bounds(x, y) {
63            Some(&self.cells[y as usize * self.width + x as usize])
64        } else {
65            None
66        }
67    }
68
69    pub fn get_mut(&mut self, x: i32, y: i32) -> Option<&mut C> {
70        if self.in_bounds(x, y) {
71            Some(&mut self.cells[y as usize * self.width + x as usize])
72        } else {
73            None
74        }
75    }
76
77    pub fn set(&mut self, x: i32, y: i32, cell: C) -> bool {
78        if self.in_bounds(x, y) {
79            self.cells[y as usize * self.width + x as usize] = cell;
80            true
81        } else {
82            false
83        }
84    }
85
86    pub fn fill(&mut self, cell: C) {
87        self.cells.fill(cell);
88    }
89
90    pub fn fill_rect(&mut self, x: i32, y: i32, w: usize, h: usize, cell: C) {
91        for dy in 0..h {
92            for dx in 0..w {
93                self.set(x + dx as i32, y + dy as i32, cell.clone());
94            }
95        }
96    }
97
98    pub fn count<F: Fn(&C) -> bool>(&self, predicate: F) -> usize {
99        self.cells.iter().filter(|c| predicate(c)).count()
100    }
101
102    pub fn iter(&self) -> impl Iterator<Item = (usize, usize, &C)> {
103        self.cells
104            .iter()
105            .enumerate()
106            .map(move |(i, c)| (i % self.width, i / self.width, c))
107    }
108}
109
110impl<C: Cell> Index<(usize, usize)> for Grid<C> {
111    type Output = C;
112    fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
113        &self.cells[y * self.width + x]
114    }
115}
116
117impl<C: Cell> IndexMut<(usize, usize)> for Grid<C> {
118    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output {
119        &mut self.cells[y * self.width + x]
120    }
121}
122
123impl<C: Cell + PartialEq> PartialEq for Grid<C> {
124    fn eq(&self, other: &Self) -> bool {
125        self.width == other.width && self.height == other.height && self.cells == other.cells
126    }
127}
128
129impl<C: Cell + Eq> Eq for Grid<C> {}