summaryrefslogtreecommitdiffstats
path: root/2023/rs/code/three/src/main.rs
blob: c72abe9d21f031de9a1fa6c0ef108432409a12a4 (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
#![feature(iter_map_windows)]

use std::collections::BTreeSet;

static INPUT: &[u8] = include_bytes!("input.txt");

#[derive(Copy, Clone, Debug)]
enum LineMaskEntry {
  Empty,
  Gear,
  Number{tag: u32, num: u32}
}

fn main() {
  let valueified = INPUT.split(|b| *b == b'\n').filter(|s| !s.is_empty());

  let mut num = 0;

  let (parts_number_sum, gears_mask) = [valueified.clone().next().unwrap()].into_iter().chain(valueified.clone()).chain([valueified.clone().last().unwrap()])
    .map_windows::<_, _, 3>(|[prev, curr, next]| {
      let mut numlen = 0;
      let mut numval = 0;

      let mut acc = 0;
      let mut linemask = vec![LineMaskEntry::Empty; curr.len()];

      for i in 0..curr.len() {
        if curr[i] == b'*' { linemask[i] = LineMaskEntry::Gear; }

        if !curr[i].is_ascii_digit() && numlen != 0 {
          let start = if i - numlen == 0 { i - numlen } else { i - numlen - 1 };
          let end = i;
          let window = || start..=end;
          if prev[window()].into_iter().chain(&curr[window()]).chain(&next[window()]).any(|b| !b.is_ascii_digit() && *b != b'.') {
            acc += numval;

            linemask[i - numlen .. i].fill(LineMaskEntry::Number{ tag: num, num: numval });
            num += 1;
          }

          numlen = 0;
          numval = 0;
        }
        else if curr[i].is_ascii_digit() {
          numlen += 1;
          numval *= 10;
          numval += (curr[i] - b'0') as u32;
        }
      }

      if numlen != 0 {
        let window = || curr.len() - numlen - 1..curr.len();
        if prev[window()].into_iter().chain(&curr[window()]).chain(&next[window()]).any(|b| !b.is_ascii_digit() && *b != b'.') {
          acc += numval;

          linemask[curr.len() - numlen .. curr.len()].fill(LineMaskEntry::Number{ tag: num, num: numval });
          num += 1;
        }
      }
      (acc, linemask)
    }).fold((0, Vec::new()), |(n, mut v), (a, l)| (n + a, {v.push(l); v}));
  println!("Sum of valid part numbers: {parts_number_sum}");


  let first = vec![LineMaskEntry::Empty; gears_mask[0].len()];
  let last = first.clone();
  let gear_ratio_sum = [first].into_iter().chain(gears_mask).chain([last])
    .map_windows::<_, _, 3>(|[prev, curr, next]| {
      let mut acc = 0;

      for i in 0..curr.len() {
        match curr[i] {
          LineMaskEntry::Gear => {
            let mut set = BTreeSet::new();
            for row in [prev, curr, next] {
              for n in [i - 1, i, i + 1] {
                if n == 0 || n == row.len() { continue; }
                if let LineMaskEntry::Number{tag, num} = row[n] {
                  set.insert((tag, num));
                }
              }
            }
            if set.len() == 2 {
              let mut iter = set.into_iter();
              let x = iter.next().unwrap().1;
              let y = iter.next().unwrap().1;
              acc += x * y
            }
          },
          _ => ()
        }
      }
      acc
    }).sum::<u32>();
  println!("Gear ratio sum: {gear_ratio_sum}");
}