advent_of_code/year2024/
day02.rs

1use crate::common::array_stack::ArrayStack;
2use crate::input::{on_error, Input};
3
4pub fn solve(input: &Input) -> Result<u32, String> {
5    let mut safe = 0;
6    for line in input.text.lines() {
7        let mut parts = ArrayStack::<16, i8>::new();
8        for s in line.split(" ") {
9            parts.push(s.parse().map_err(|_| on_error())?)?;
10        }
11        if input.is_part_one() {
12            if is_safe(parts.slice().iter().copied()) {
13                safe += 1;
14            }
15        } else {
16            for remove_idx in 0..parts.len() {
17                let removed = parts
18                    .slice()
19                    .iter()
20                    .enumerate()
21                    .filter_map(|(idx, &e)| (idx != remove_idx).then_some(e));
22                if is_safe(removed) {
23                    safe += 1;
24                    break;
25                }
26            }
27        }
28    }
29    Ok(safe)
30}
31
32fn is_safe<I: Iterator<Item = i8>>(iter: I) -> bool {
33    let mut last = 0;
34    let mut direction = 0;
35    iter.enumerate().all(|(idx, val)| {
36        if idx > 0 {
37            let diff = val.abs_diff(last);
38            if !(1..=3).contains(&diff) {
39                return false;
40            }
41            if idx == 1 {
42                direction = last - val;
43            } else if (last < val) != (direction < 0) {
44                return false;
45            }
46        }
47        last = val;
48        true
49    })
50}
51
52#[test]
53pub fn tests() {
54    use crate::input::{test_part_one_no_allocations, test_part_two_no_allocations};
55
56    let test_input = "7 6 4 2 1
571 2 7 8 9
589 7 6 2 1
591 3 2 4 5
608 6 4 4 1
611 3 6 7 9";
62    test_part_one_no_allocations!(test_input => 2);
63    test_part_two_no_allocations!(test_input => 4);
64
65    let real_input = include_str!("day02_input.txt");
66    test_part_one_no_allocations!(real_input => 479);
67    test_part_two_no_allocations!(real_input => 531);
68}