path_finding/
grid.rs

1pub enum Direction {
2    Up,
3    Down,
4    Left,
5    Right,
6    UpLeft,
7    UpRight,
8    DownLeft,
9    DownRight,
10}
11
12impl Direction {
13    pub fn attempt_move(&self, coords: (usize, usize)) -> (usize, usize) {
14        let opt_coord = match self {
15            Direction::Up => (coords.0.checked_sub(1), Some(coords.1)),
16            Direction::Down => (coords.0.checked_add(1), Some(coords.1)),
17            Direction::Left => (Some(coords.0), coords.1.checked_sub(1)),
18            Direction::Right => (Some(coords.0), coords.1.checked_add(1)),
19            Direction::UpLeft => (coords.0.checked_sub(1), coords.1.checked_sub(1)),
20            Direction::UpRight => (coords.0.checked_sub(1), coords.1.checked_add(1)),
21            Direction::DownLeft => (coords.0.checked_add(1), coords.1.checked_sub(1)),
22            Direction::DownRight => (coords.0.checked_add(1), coords.1.checked_add(1)),
23        };
24
25
26        if opt_coord.0.and(opt_coord.1).is_some() {
27            return (opt_coord.0.unwrap(), opt_coord.1.unwrap());
28        }
29
30        return coords;
31    }
32}
33
34pub struct Grid {
35    pub width: usize,
36    pub height: usize,
37    pub costs: Vec<Vec<f32>>,
38    pub size: usize,
39}
40
41impl Grid {
42    pub fn from(grid: &[&[f32]]) -> Grid {
43        if grid.is_empty() || grid[0].is_empty() {
44            panic!("Given grid should not be empty")
45        }
46
47        let (height, width) = (grid.len(), grid[0].len());
48        let mut costs = vec![vec![0.0; width]; height];
49
50        for (row, row_value) in grid.iter().enumerate() {
51            for (col, col_value) in row_value.iter().enumerate() {
52                costs[row][col] = col_value.clone();
53            }
54        }
55
56        return Grid {
57            width,
58            height,
59            costs,
60            size: width * height,
61        };
62    }
63
64    pub fn outside(&self, coord: (usize, usize)) -> bool {
65        return !self.within(coord);
66    }
67
68    pub fn within(&self, coord: (usize, usize)) -> bool {
69        return coord.0 < self.costs.len() && coord.1 < self.costs[coord.0].len();
70    }
71
72    pub fn node_id(&self, coord: (usize, usize)) -> usize {
73        if self.outside(coord) {
74            panic!("Coordinate is outside of matrix");
75        }
76
77        return self.costs[coord.0].len() * coord.0 + coord.1;
78    }
79
80    pub fn coords(&self, node_id: usize) -> (usize, usize) {
81        if self.size <= node_id {
82            panic!("Node id exceeds grid size");
83        }
84
85        return (node_id / self.width, node_id % self.width);
86    }
87
88    pub fn cost(&self, node_id: usize) -> f32 {
89        let (row, col) = self.coords(node_id);
90        return self.costs[row][col];
91    }
92}
93
94
95// Testing
96#[test]
97fn subtract_below_zero_should_be_max() {
98    let coord = Direction::UpLeft.attempt_move((0, 0));
99
100    assert_eq!(0, coord.0);
101    assert_eq!(0, coord.1)
102}
103
104// Testing
105#[test]
106fn add_above_max_should_remain() {
107    let coord = Direction::DownRight.attempt_move((usize::MAX, usize::MAX));
108
109    assert_eq!(usize::MAX, coord.0);
110    assert_eq!(usize::MAX, coord.1)
111}
112
113#[test]
114fn up_direction_should_move_coordinate() {
115    let coord = Direction::Up.attempt_move((1, 1));
116
117    assert_eq!(0, coord.0);
118    assert_eq!(1, coord.1)
119}
120
121#[test]
122fn down_direction_should_move_coordinate() {
123    let coord = Direction::Down.attempt_move((1, 1));
124
125    assert_eq!(2, coord.0);
126    assert_eq!(1, coord.1)
127}
128
129#[test]
130fn left_direction_should_move_coordinate() {
131    let coord = Direction::Left.attempt_move((1, 1));
132
133    assert_eq!(1, coord.0);
134    assert_eq!(0, coord.1)
135}
136
137#[test]
138fn right_direction_should_move_coordinate() {
139    let coord = Direction::Right.attempt_move((1, 1));
140
141    assert_eq!(1, coord.0);
142    assert_eq!(2, coord.1)
143}
144
145#[test]
146fn up_left_direction_should_move_coordinate() {
147    let coord = Direction::UpLeft.attempt_move((1, 1));
148
149    assert_eq!(0, coord.0);
150    assert_eq!(0, coord.1)
151}
152
153#[test]
154fn up_right_direction_should_move_coordinate() {
155    let coord = Direction::UpRight.attempt_move((1, 1));
156
157    assert_eq!(0, coord.0);
158    assert_eq!(2, coord.1)
159}
160
161#[test]
162fn down_left_direction_should_move_coordinate() {
163    let coord = Direction::DownLeft.attempt_move((1, 1));
164
165    assert_eq!(2, coord.0);
166    assert_eq!(0, coord.1)
167}
168
169#[test]
170fn down_right_direction_should_move_coordinate() {
171    let coord = Direction::DownRight.attempt_move((1, 1));
172
173    assert_eq!(2, coord.0);
174    assert_eq!(2, coord.1)
175}
176
177#[test]
178fn get_cost_with_node_id_left_upper() {
179    let grid = Grid::from(&[
180        &[4.0, 2.0, 1.0],
181        &[2.0, 1.0, 0.0],
182        &[3.0, 4.0, 7.0]
183    ]);
184
185
186    assert_eq!(4.0, grid.cost(0));
187    assert_eq!(9, grid.size);
188}
189
190#[test]
191fn get_cost_with_node_id_center() {
192    let grid = Grid::from(&[
193        &[4.0, 2.0, 1.0],
194        &[2.0, 1.0, 0.0],
195        &[3.0, 4.0, 7.0]
196    ]);
197
198
199    assert_eq!(1.0, grid.cost(4));
200}
201
202
203#[test]
204fn get_cost_with_node_id_right_lower() {
205    let grid = Grid::from(&[
206        &[4.0, 2.0, 1.0],
207        &[2.0, 1.0, 0.0],
208        &[3.0, 4.0, 7.0]
209    ]);
210
211
212    assert_eq!(7.0, grid.cost(8));
213}
214
215#[test]
216#[should_panic(expected = "Node id exceeds grid size")]
217fn get_cost_with_node_id_should_panic() {
218    let grid = Grid::from(&[
219        &[4.0, 2.0, 1.0],
220        &[2.0, 1.0, 0.0],
221        &[3.0, 4.0, 7.0]
222    ]);
223
224
225    grid.cost(9);
226}
227
228#[test]
229fn node_id_should_return_center_node_id() {
230    let grid = Grid::from(&[
231        &[4.0, 2.0, 1.0],
232        &[2.0, 1.0, 0.0],
233        &[3.0, 4.0, 7.0]
234    ]);
235
236
237    assert_eq!(4, grid.node_id((1, 1)));
238}
239
240#[test]
241fn node_id_should_return_left_upper_node_id() {
242    let grid = Grid::from(&[
243        &[4.0, 2.0, 1.0],
244        &[2.0, 1.0, 0.0],
245        &[3.0, 4.0, 7.0]
246    ]);
247
248
249    assert_eq!(0, grid.node_id((0, 0)));
250}
251
252#[test]
253fn node_id_should_return_right_lower_node_id() {
254    let grid = Grid::from(&[
255        &[4.0, 2.0, 1.0],
256        &[2.0, 1.0, 0.0],
257        &[3.0, 4.0, 7.0]
258    ]);
259
260
261    assert_eq!(8, grid.node_id((2, 2)));
262}
263
264#[test]
265#[should_panic(expected = "Coordinate is outside of matrix")]
266fn node_id_should_panic() {
267    let grid = Grid::from(&[
268        &[4.0, 2.0, 1.0],
269        &[2.0, 1.0, 0.0],
270        &[3.0, 4.0, 7.0]
271    ]);
272
273
274    assert_eq!(8, grid.node_id((2, 3)));
275}
276
277#[test]
278fn coord_should_be_within() {
279    let coord: (usize, usize) = (0, 0);
280
281    let grid = Grid::from(&[
282        &[4.0, 2.0, 1.0],
283        &[2.0, 1.0, 0.0]
284    ]);
285
286    assert!(grid.within(coord));
287    assert!(!grid.outside(coord));
288}
289
290#[test]
291fn coord_row_should_be_outside() {
292    let coord: (usize, usize) = (2, 0);
293
294    let grid = Grid::from(&[
295        &[4.0, 2.0, 1.0],
296        &[2.0, 1.0, 0.0]
297    ]);
298
299    assert!(grid.outside(coord));
300}
301
302#[test]
303fn coord_col_should_be_outside() {
304    let coord: (usize, usize) = (0, 3);
305
306    let grid = Grid::from(&[
307        &[4.0, 2.0, 1.0],
308        &[2.0, 1.0, 0.0]
309    ]);
310
311    assert!(grid.outside(coord));
312}
313
314#[test]
315fn from_should_create_grid() {
316    let grid_matrix: &[&[f32]] = &[
317        &[0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.0, 0.0],
318        &[4.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 11.0, 0.0],
319        &[0.0, 8.0, 0.0, 7.0, 0.0, 4.0, 0.0, 0.0, 2.0],
320        &[0.0, 0.0, 7.0, 0.0, 9.0, 14.0, 0.0, 0.0, 0.0],
321        &[0.0, 0.0, 0.0, 9.0, 0.0, 10.0, 0.0, 0.0, 0.0],
322        &[0.0, 0.0, 4.0, 14.0, 10.0, 0.0, 2.0, 0.0, 0.0],
323        &[0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 1.0, 6.0],
324        &[8.0, 11.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 7.0],
325        &[0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 6.0, 7.0, 0.0]
326    ];
327
328    let grid = Grid::from(grid_matrix);
329    assert_eq!(9, grid.height);
330    assert_eq!(9, grid.width);
331    assert_eq!(7.0, grid.costs[8][7]);
332    assert_eq!(81, grid.size);
333}
334
335#[test]
336#[should_panic(expected = "Given grid should not be empty")]
337fn from_with_no_rows_should_panic() {
338    Grid::from(&[]);
339}
340
341#[test]
342#[should_panic(expected = "Given grid should not be empty")]
343fn from_with_no_columns_should_panic() {
344    Grid::from(&[&[]]);
345}