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