summaryrefslogtreecommitdiff
path: root/2025/09/rust/src/main.rs
blob: 32847e31281174e53c12f56704c1de86b476e0d8 (plain)
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
use std::io;

#[derive(Debug)]
struct Floor {
    x_comp: Vec<usize>,
    y_comp: Vec<usize>,
}

impl Floor {
    fn new(pts: Vec<(usize, usize)>) -> (Self, Vec<(usize, usize)>) {
        let (mut xs, mut ys): (Vec<_>, Vec<_>) = pts.clone().into_iter().unzip();
        xs.sort_unstable();
        xs.dedup();
        ys.sort_unstable();
        ys.dedup();

        let pts = pts
            .into_iter()
            .map(|(x, y)| (xs.binary_search(&x).unwrap(), ys.binary_search(&y).unwrap()))
            .collect();

        (
            Self {
                x_comp: xs,
                y_comp: ys,
            },
            pts,
        )
    }

    fn uncomp(&self, (x, y): (usize, usize)) -> (usize, usize) {
        (self.x_comp[x], self.y_comp[y])
    }

    fn area(&self, (p1, p2): ((usize, usize), (usize, usize))) -> usize {
        let (x1, y1) = self.uncomp(p1);
        let (x2, y2) = self.uncomp(p2);
        (x1.abs_diff(x2) + 1) * (y1.abs_diff(y2) + 1)
    }
}

fn main() {
    let pts = io::stdin()
        .lines()
        .flatten()
        .map(|line| {
            let (x, y) = line.split_once(',').unwrap();
            (x.parse().unwrap(), y.parse().unwrap())
        })
        .collect::<Vec<(usize, usize)>>();

    let (floor, pts) = Floor::new(pts);

    let xmax = *pts.iter().map(|(x, _)| x).max().unwrap();
    let ymax = *pts.iter().map(|(_, y)| y).max().unwrap();

    // some padding so that flood fill works correctly
    let mut grid: Vec<Vec<Option<bool>>> = vec![vec![None; xmax + 3]; ymax + 3];

    // draw green/red tiles
    for pair in [vec![*pts.last().unwrap()], pts.clone()]
        .concat()
        .windows(2)
    {
        let (x1, y1) = pair[0];
        let (x2, y2) = pair[1];
        (x1.min(x2)..=x1.max(x2)).for_each(|x| {
            (y1.min(y2)..=(y1.max(y2))).for_each(|y| grid[y + 1][x + 1] = Some(true))
        });
    }

    // flood fill from top left
    let mut q = vec![(0, 0)];
    while let Some((x, y)) = q.pop() {
        grid[y][x] = Some(false);
        let ns = vec![
            (x.saturating_sub(1), y),
            ((x + 1).min(xmax + 2), y),
            (x, y.saturating_sub(1)),
            (x, (y + 1).min(ymax + 2)),
        ];
        q.extend(ns.into_iter().filter(|&(x, y)| grid[y][x].is_none()));
    }

    // replace inside cells with Some(true) and undo the padding
    let grid: Vec<Vec<bool>> = grid
        .into_iter()
        .skip(1)
        .map(|r| r.into_iter().skip(1).map(|c| c.unwrap_or(true)).collect())
        .collect();

    let mut rectangles = pts
        .iter()
        .enumerate()
        .flat_map(|(idx, p1)| pts.iter().take(idx).map(move |p2| (*p1, *p2)))
        .collect::<Vec<_>>();
    rectangles.sort_unstable_by_key(|&r| std::cmp::Reverse(floor.area(r)));

    let silver: usize = floor.area(*rectangles.first().unwrap());
    let gold: usize = floor.area(
        *rectangles
            .iter()
            .filter(|&&((x1, y1), (x2, y2))| {
                (x1.min(x2)..=x1.max(x2))
                    .flat_map(|x| (y1.min(y2)..=y1.max(y2)).map(move |y| (x, y)))
                    .all(|(x, y)| grid[y][x])
            })
            .next()
            .unwrap(),
    );
    println!("silver: {silver}");
    println!("gold: {gold}");
}