1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use std::fmt::Display;

use super::Direction;
use advent_of_code::errors::{Error, Result};

pub struct Grid {
    grid: Vec<Vec<u8>>,
    height: usize,
    width: usize,
}

impl Grid {
    pub fn from_lines<S: AsRef<str>, L: Iterator<Item = S>>(lines: L) -> Result<Self> {
        let grid: Vec<Vec<u8>> = lines
            .map(|line| {
                line.as_ref()
                    .trim()
                    .chars()
                    .map(|c| {
                        u8::try_from((c as u32).checked_sub(48).ok_or("Invalid grid character")?)
                            .map_err(|e| Error::InvalidParseError(format!("{:?}", e)))
                    })
                    .collect::<Result<Vec<u8>>>()
            })
            .filter(|row| row.as_ref().map(|r| !r.is_empty()).unwrap_or(true))
            .collect::<Result<_>>()?;
        let height = grid.len();
        let width = grid
            .get(0)
            .map(|r| r.len())
            .ok_or("Grid must have at least one row")?;

        Ok(Grid {
            grid,
            height,
            width,
        })
    }

    pub fn dims(&self) -> (usize, usize) {
        (self.height, self.width)
    }

    /// Doesn't do any bounds checking because I am too lazy today ;)
    pub fn is_visible(&self, start: (usize, usize), direction: Direction) -> bool {
        let height = self.grid[start.0][start.1];

        for (r, c) in direction.iter(start, (self.height, self.width)) {
            if self.grid[r][c] >= height {
                return false;
            }
        }
        true
    }

    pub fn viewing_distance(&self, start: (usize, usize), direction: Direction) -> usize {
        let height = self.grid[start.0][start.1];
        let mut dist = 0;
        for (r, c) in direction.iter(start, (self.height, self.width)) {
            dist += 1;
            if self.grid[r][c] >= height {
                break;
            }
        }
        dist
    }
}

impl Display for Grid {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for row in self.grid.iter() {
            writeln!(f, "{:?}", row)?;
        }
        Ok(())
    }
}