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#[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#[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}