aoc/year2024/
day02.rs

1//! # Red-Nosed Reports
2//!
3//! Computes boths parts simultaneously. Each pair of levels is converted into deltas of
4//! either +1, -1 or 0. For example:
5//!
6//! * `1 3 6 7 9` => `+1 +1 +1 +1`
7//! * `9 7 6 2 1` => `-1 -1 0 -1`
8//!
9//! If the sum of all delta equals ±4, then we know that all levels are increasing or
10//! decreasing. Any other value indicates either mixed up and down transitions or levels that
11//! are too far apart.
12//!
13//! For part two we remove each pair of deltas (or single delta at each end) then replace with
14//! the sum of the delta from the new neighbors on either side.
15use crate::util::parse::*;
16
17type Input = (u32, u32);
18
19/// Minimize allocation to only a single `vec` reused for each report.
20pub fn parse(input: &str) -> Input {
21    let mut report = Vec::new();
22    let mut part_one = 0;
23    let mut part_two = 0;
24
25    for line in input.lines() {
26        report.extend(line.iter_signed::<i32>());
27
28        let (p1, p2) = check(&report);
29        part_one += p1;
30        part_two += p2;
31
32        report.clear();
33    }
34
35    (part_one, part_two)
36}
37
38pub fn part1(input: &Input) -> u32 {
39    input.0
40}
41
42pub fn part2(input: &Input) -> u32 {
43    input.1
44}
45
46fn check(report: &[i32]) -> (u32, u32) {
47    let size = report.len();
48    let score: i32 = (1..size).map(|i| delta(report[i - 1], report[i])).sum();
49
50    if score.abs() == (size - 1) as i32 {
51        return (1, 1);
52    }
53
54    for i in 0..size {
55        let mut score = score;
56
57        // Snip out each level and replace with new level computed from neighbors to either side.
58        if i > 0 {
59            score -= delta(report[i - 1], report[i]);
60        }
61        if i < size - 1 {
62            score -= delta(report[i], report[i + 1]);
63        }
64        if i > 0 && i < size - 1 {
65            score += delta(report[i - 1], report[i + 1]);
66        }
67
68        if score.abs() == (size - 2) as i32 {
69            return (0, 1);
70        }
71    }
72
73    (0, 0)
74}
75
76/// Convert each pair of levels to either +1 for increase, -1 for decrease or 0 for invalid range.
77fn delta(a: i32, b: i32) -> i32 {
78    let diff = b - a;
79
80    if diff.abs() <= 3 { diff.signum() } else { 0 }
81}