advent_of_code/year2024/
day02.rs1use 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}