| 
 | 1 | +use std::collections::HashSet;  | 
 | 2 | + | 
 | 3 | +use crate::grid::{Cell, Grid, Point};  | 
 | 4 | + | 
 | 5 | +pub fn get_free_cells(grid: &Grid, walkable: Cell) -> (HashSet<Point>, HashSet<Point>) {  | 
 | 6 | +    let mut free_cells: HashSet<Point> = HashSet::new();  | 
 | 7 | +    let mut one_way_cells: HashSet<Point> = HashSet::new();  | 
 | 8 | +    let mut open_list: HashSet<Point> = HashSet::new();  | 
 | 9 | + | 
 | 10 | +    for x in 0..(grid.width as i8) {  | 
 | 11 | +        open_list.insert(Point { x, y: 0 });  | 
 | 12 | +        open_list.insert(Point {  | 
 | 13 | +            x,  | 
 | 14 | +            y: (grid.height as i8) - 1,  | 
 | 15 | +        });  | 
 | 16 | +    }  | 
 | 17 | +    for y in 0..(grid.height as i8) {  | 
 | 18 | +        open_list.insert(Point { x: 0, y });  | 
 | 19 | +        open_list.insert(Point {  | 
 | 20 | +            x: (grid.width as i8) - 1,  | 
 | 21 | +            y,  | 
 | 22 | +        });  | 
 | 23 | +    }  | 
 | 24 | +    open_list.retain(|p| grid.get_cell(&p) <= walkable);  | 
 | 25 | + | 
 | 26 | +    let directions = [  | 
 | 27 | +        Point { x: 1, y: 0 },  | 
 | 28 | +        Point { x: -1, y: 0 },  | 
 | 29 | +        Point { x: 0, y: 1 },  | 
 | 30 | +        Point { x: 0, y: -1 },  | 
 | 31 | +    ];  | 
 | 32 | + | 
 | 33 | +    while let Some(p) = open_list.iter().next().cloned() {  | 
 | 34 | +        open_list.remove(&p);  | 
 | 35 | + | 
 | 36 | +        let has_enough_free_exits = {  | 
 | 37 | +            let mut exit_count = 0;  | 
 | 38 | +            let mut visited: HashSet<Point> = HashSet::new();  | 
 | 39 | + | 
 | 40 | +            for dir in directions {  | 
 | 41 | +                let neighbour = Point {  | 
 | 42 | +                    x: p.x + dir.x,  | 
 | 43 | +                    y: p.y + dir.y,  | 
 | 44 | +                };  | 
 | 45 | + | 
 | 46 | +                if !visited.contains(&neighbour)  | 
 | 47 | +                    && (free_cells.contains(&neighbour) || !grid.is_inside(&neighbour))  | 
 | 48 | +                {  | 
 | 49 | +                    visited.insert(neighbour);  | 
 | 50 | +                    exit_count += 1;  | 
 | 51 | +                }  | 
 | 52 | + | 
 | 53 | +                if grid.is_inside(&neighbour) && grid.get_cell(&neighbour) <= walkable {  | 
 | 54 | +                    for alt in [-1, 1] {  | 
 | 55 | +                        let corner = {  | 
 | 56 | +                            if neighbour.x != 0 {  | 
 | 57 | +                                Point {  | 
 | 58 | +                                    x: neighbour.x,  | 
 | 59 | +                                    y: neighbour.y + alt,  | 
 | 60 | +                                }  | 
 | 61 | +                            } else {  | 
 | 62 | +                                Point {  | 
 | 63 | +                                    x: neighbour.x + alt,  | 
 | 64 | +                                    y: neighbour.y,  | 
 | 65 | +                                }  | 
 | 66 | +                            }  | 
 | 67 | +                        };  | 
 | 68 | + | 
 | 69 | +                        if !visited.contains(&neighbour)  | 
 | 70 | +                            && !visited.contains(&corner)  | 
 | 71 | +                            && (free_cells.contains(&corner) || !grid.is_inside(&corner))  | 
 | 72 | +                        {  | 
 | 73 | +                            visited.insert(neighbour);  | 
 | 74 | +                            visited.insert(corner);  | 
 | 75 | +                            exit_count += 1;  | 
 | 76 | +                        }  | 
 | 77 | +                    }  | 
 | 78 | +                }  | 
 | 79 | +            }  | 
 | 80 | + | 
 | 81 | +            exit_count >= 2  | 
 | 82 | +        };  | 
 | 83 | + | 
 | 84 | +        if has_enough_free_exits {  | 
 | 85 | +            free_cells.insert(p);  | 
 | 86 | + | 
 | 87 | +            for dir in directions {  | 
 | 88 | +                let neighbour = Point {  | 
 | 89 | +                    x: p.x + dir.x,  | 
 | 90 | +                    y: p.y + dir.y,  | 
 | 91 | +                };  | 
 | 92 | + | 
 | 93 | +                if !free_cells.contains(&neighbour)  | 
 | 94 | +                    && grid.is_inside(&neighbour)  | 
 | 95 | +                    && grid.get_cell(&neighbour) <= walkable  | 
 | 96 | +                {  | 
 | 97 | +                    open_list.insert(neighbour);  | 
 | 98 | +                }  | 
 | 99 | +            }  | 
 | 100 | +        } else {  | 
 | 101 | +            one_way_cells.insert(p);  | 
 | 102 | +        }  | 
 | 103 | +    }  | 
 | 104 | + | 
 | 105 | +    one_way_cells.retain(|p| !free_cells.contains(&p));  | 
 | 106 | + | 
 | 107 | +    (free_cells, one_way_cells)  | 
 | 108 | +}  | 
 | 109 | + | 
 | 110 | +#[test]  | 
 | 111 | +fn it_should_collect_free_cell() {  | 
 | 112 | +    let mut grid = Grid::create_empty(2, 2);  | 
 | 113 | + | 
 | 114 | +    grid.set_cell(&Point { x: 1, y: 1 }, Cell::Color2);  | 
 | 115 | + | 
 | 116 | +    let (free_cells, _) = get_free_cells(&grid, Cell::Color1);  | 
 | 117 | + | 
 | 118 | +    assert_eq!(  | 
 | 119 | +        free_cells,  | 
 | 120 | +        HashSet::from([  | 
 | 121 | +            //  | 
 | 122 | +            Point { x: 0, y: 0 },  | 
 | 123 | +            Point { x: 0, y: 1 },  | 
 | 124 | +            Point { x: 1, y: 0 },  | 
 | 125 | +        ])  | 
 | 126 | +    );  | 
 | 127 | +}  | 
0 commit comments