basic_pathfinding/
grid.rs1use 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}