use std::collections::{HashSet, VecDeque};

type Position = (i32, i32);
type Grid = Vec<Vec<char>>;

impl Solution {
    fn indices(grid: &Grid) -> impl Iterator<Item = Position> + '_ {
        (0..grid.len() as i32)
            .flat_map(move |y| (0..grid[y as usize].len() as i32).map(move |x| (y, x)))
    }

    fn interesting(grid: &Grid, visited: &HashSet<Position>, p: &Position) -> bool {
        let (max_y, max_x) = (grid.len() as i32, grid[0].len() as i32);
        let &(y, x) = p;

        y >= 0
            && y < max_y
            && x >= 0
            && x < max_x
            && grid[p.0 as usize][p.1 as usize] != '0'
            && !visited.contains(p)
    }

    fn bfs(grid: &Grid, visited: &mut HashSet<Position>, pos: Position) {
        let mut queue: VecDeque<Position> = VecDeque::new();
        queue.push_back(pos);
        visited.insert(pos);

        while let Some(p) = queue.pop_front() {
            for d in [-1, 1] {
                for adjacent in [(p.0 + d, p.1), (p.0, p.1 + d)] {
                    if Solution::interesting(grid, visited, &adjacent) {
                        queue.push_back(adjacent);
                        visited.insert(adjacent);
                    }
                }
            }
        }
    }

    pub fn num_islands(grid: Grid) -> i32 {
        let mut visited: HashSet<Position> = HashSet::new();

        let mut islands = 0;
        for (y, x) in Solution::indices(&grid) {
            if !Solution::interesting(&grid, &visited, &(y, x)) {
                continue;
            }

            Solution::bfs(&grid, &mut visited, (y, x));
            islands += 1;
        }

        islands
    }
}
struct Solution {}

fn main() {
    println!("Hello World!");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_example_1() {
        assert_eq!(
            Solution::num_islands(vec![
                vec!['1', '1', '1', '1', '0'],
                vec!['1', '1', '0', '1', '0'],
                vec!['1', '1', '0', '0', '0'],
                vec!['0', '0', '0', '0', '0']
            ]),
            1
        );
    }

    #[test]
    fn test_example_2() {
        assert_eq!(
            Solution::num_islands(vec![
                vec!['1', '1', '0', '0', '0'],
                vec!['1', '1', '0', '0', '0'],
                vec!['0', '0', '1', '0', '0'],
                vec!['0', '0', '0', '1', '1']
            ]),
            3
        );
    }
}