summaryrefslogtreecommitdiff
path: root/2024_rust/src/bin/day4.rs
blob: 7eb7b7ab65684e25e3044222b55ed00ecab2b3fd (plain) (blame)
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
const XMAS: &str = "XMAS";
const MAS: &str = "MAS";

type Position = (usize, usize);
type Row = Vec<Position>;
type Rows = Vec<Row>;
type Move = (Position, (usize, isize));

struct Matrix {
    chars: Vec<Vec<char>>,
    limit: Position,
}

impl Matrix {
    fn new(text: &str) -> Self {
        let chars: Vec<Vec<char>> = text.lines().map(|row| row.chars().collect()).collect();
        let limit = (chars.len(), chars[0].len());
        Self { chars, limit }
    }

    fn check_iter<'a, I>(&self, it: I, needle: &str) -> bool
    where
        I: Iterator<Item = &'a Position>,
    {
        for (c, (i, j)) in needle.chars().zip(it) {
            if self.chars[*i][*j] != c {
                return false;
            }
        }

        true
    }

    fn check_row(&self, row: &Row, needle: &str) -> bool {
        self.check_iter(row.iter(), needle) || self.check_iter(row.iter().rev(), needle)
    }
}

struct Square<'a> {
    pos: Position,
    size: usize,
    matrix: &'a Matrix,
}

impl Square<'_> {
    fn rows<M>(&self, moves: M) -> Rows
    where
        M: Fn(&Square) -> Vec<Move>,
    {
        let mut rs: Rows = vec![];

        // Horizontal / vertical / diagonal
        for (p0, (di, dj)) in moves(self) {
            // Bounds check
            if p0.0 + di * self.size > self.matrix.limit.0
                || (dj > 0 && offplus(p0.1, dj * self.size as isize) > self.matrix.limit.1)
                || p0.1 >= self.matrix.limit.1
            {
                continue;
            }

            let mut p_acc = p0;
            let mut row: Row = vec![];
            for _idx in 0..self.size {
                row.push(p_acc);
                p_acc = (p_acc.0 + di, offplus(p_acc.1, dj));
            }
            rs.push(row);
        }

        rs
    }
}

fn offplus(x: usize, y: isize) -> usize {
    (x as isize + y) as usize
}

fn moves_xmas(Square { pos, size, .. }: &Square) -> Vec<Move> {
    let v = vec![
        (*pos, (0, 1)),                       // diag from pos, top-down and left-to-right
        (*pos, (1, 0)),                       // diag from pos, top-down and left-to-right
        (*pos, (1, 1)),                       // diag from pos, top-down and left-to-right
        ((pos.0, pos.1 + size - 1), (1, -1)), // the other diag, top-down and right-to-left
    ];
    v
}

fn p1(input: &str) -> String {
    let matrix: Matrix = Matrix::new(input);
    let mut result = 0;

    for i in 0..matrix.limit.0 {
        for j in 0..matrix.limit.1 {
            let sq = Square {
                pos: (i, j),
                size: XMAS.len(),
                matrix: &matrix,
            };
            for row in sq.rows(moves_xmas) {
                if matrix.check_row(&row, XMAS) {
                    result += 1;
                }
            }
        }
    }

    result.to_string()
}

fn moves_mas(Square { pos, size, .. }: &Square) -> Vec<Move> {
    let v = vec![
        (*pos, (1, 1)),                       // diag from pos, top-down and left-to-right
        ((pos.0, pos.1 + size - 1), (1, -1)), // the other diag, top-down and right-to-left
    ];
    v
}

fn p2(input: &str) -> String {
    let matrix: Matrix = Matrix::new(input);
    let mut result = 0;

    for i in 0..=matrix.limit.0 - MAS.len() {
        for j in 0..=matrix.limit.1 - MAS.len() {
            let sq = Square {
                pos: (i, j),
                size: MAS.len(),
                matrix: &matrix,
            };
            if sq.rows(moves_mas).iter().all(|r| matrix.check_row(r, MAS)) {
                result += 1;
            }
        }
    }

    result.to_string()
}

fn main() {
    aoc2024::run_day("4", p1, p2);
}