basic_pathfinding/
grid.rs

1use crate::coord::Coord;
2use std::collections::HashMap;
3
4#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
5pub enum GridType {
6  Cardinal,
7  Hex,
8  Intercardinal,
9}
10
11impl Default for GridType {
12  fn default() -> GridType {
13    GridType::Cardinal
14  }
15}
16
17#[derive(Clone, Default, Serialize, Deserialize, Debug)]
18#[serde(rename_all = "camelCase")]
19pub struct Grid {
20  pub tiles: Vec<Vec<i32>>,
21  pub walkable_tiles: Vec<i32>,
22  pub unstoppable_tiles: Vec<i32>,
23  #[serde(default = "HashMap::new")]
24  pub costs: HashMap<i32, i32>,
25  #[serde(default = "HashMap::new")]
26  pub extra_costs: HashMap<i32, HashMap<i32, i32>>,
27  #[serde(default = "HashMap::new")]
28  pub unstoppable_coords: HashMap<i32, HashMap<i32, bool>>,
29  #[serde(default = "HashMap::new")]
30  pub unwalkable_coords: HashMap<i32, HashMap<i32, bool>>,
31  #[serde(default = "default_grid_type")]
32  pub grid_type: GridType,
33}
34
35fn default_grid_type() -> GridType {
36  GridType::Cardinal
37}
38
39impl Grid {
40  pub fn is_cardinal(&self) -> bool {
41    match self.grid_type {
42      GridType::Cardinal => true,
43      _ => false,
44    }
45  }
46
47  pub fn is_hex(&self) -> bool {
48    match self.grid_type {
49      GridType::Hex => true,
50      _ => false,
51    }
52  }
53
54  pub fn is_intercardinal(&self) -> bool {
55    match self.grid_type {
56      GridType::Intercardinal => true,
57      _ => false,
58    }
59  }
60
61  pub fn in_grid(&self, x: i32, y: i32) -> bool {
62    match self {
63      _grid if (x < 0) | (y < 0) => false,
64      grid if (y as usize) < grid.tiles.len() => (x as usize) < grid.tiles[y as usize].len(),
65      _ => false,
66    }
67  }
68
69  pub fn is_coord_stoppable(&self, x: i32, y: i32) -> bool {
70    let tile = self.tiles[y as usize][x as usize];
71    if self.unstoppable_tiles.contains(&tile) | get_nested_bool(&self.unstoppable_coords, x, y) {
72      false
73    } else {
74      self.is_coord_walkable(x, y)
75    }
76  }
77
78  pub fn is_coord_walkable(&self, x: i32, y: i32) -> bool {
79    if get_nested_bool(&self.unwalkable_coords, x, y) {
80      false
81    } else {
82      let tile = self.tiles[y as usize][x as usize];
83      self.walkable_tiles.contains(&tile)
84    }
85  }
86
87  pub fn get_coord_cost(&self, x: i32, y: i32) -> i32 {
88    match self.get_extra_cost(x, y) {
89      Some(extra) => extra,
90      _ => {
91        let tile = self.tiles[y as usize][x as usize];
92        match self.costs.get(&tile) {
93          None => 1,
94          Some(cost) => *cost,
95        }
96      }
97    }
98  }
99
100  pub fn get_adjacent(&self, coord: &Coord) -> Vec<Coord> {
101    let mut adjacent = vec![];
102    if self.in_grid(coord.x, coord.y - 1) {
103      adjacent.push(Coord::new(coord.x, coord.y - 1));
104    }
105    if !self.is_cardinal() & self.in_grid(coord.x + 1, coord.y - 1) {
106      adjacent.push(Coord::new(coord.x + 1, coord.y - 1));
107    }
108    if self.in_grid(coord.x + 1, coord.y) {
109      adjacent.push(Coord::new(coord.x + 1, coord.y));
110    }
111    if self.is_intercardinal() & self.in_grid(coord.x + 1, coord.y + 1) {
112      adjacent.push(Coord::new(coord.x + 1, coord.y + 1));
113    }
114    if self.in_grid(coord.x, coord.y + 1) {
115      adjacent.push(Coord::new(coord.x, coord.y + 1));
116    }
117    if !self.is_cardinal() & self.in_grid(coord.x - 1, coord.y + 1) {
118      adjacent.push(Coord::new(coord.x - 1, coord.y + 1));
119    }
120    if self.in_grid(coord.x - 1, coord.y) {
121      adjacent.push(Coord::new(coord.x - 1, coord.y));
122    }
123    if self.is_intercardinal() & self.in_grid(coord.x - 1, coord.y - 1) {
124      adjacent.push(Coord::new(coord.x - 1, coord.y - 1));
125    }
126    adjacent
127  }
128
129  fn get_extra_cost(&self, x: i32, y: i32) -> Option<i32> {
130    match self.extra_costs.get(&y) {
131      Some(inner_hash) => inner_hash.get(&x).cloned(),
132      _ => None,
133    }
134  }
135}
136
137fn get_nested_bool(map: &HashMap<i32, HashMap<i32, bool>>, x: i32, y: i32) -> bool {
138  match map.get(&y) {
139    Some(nested) => match nested.get(&x) {
140      Some(_) => true,
141      _ => false,
142    },
143    _ => false,
144  }
145}