advent_of_code/year2022/
day04.rs

1use crate::input::Input;
2
3fn parse_intervals(line: &str) -> Option<((u8, u8), (u8, u8))> {
4    fn parse_range(range: &str) -> Option<(u8, u8)> {
5        let (s1, s2) = range.split_once('-')?;
6        Some((s1.parse().ok()?, s2.parse().ok()?))
7    }
8
9    let (a, b) = line.split_once(',')?;
10    Some((parse_range(a)?, parse_range(b)?))
11}
12
13const fn contains(a: (u8, u8), b: (u8, u8)) -> bool {
14    b.0 >= a.0 && b.1 <= a.1 || a.0 >= b.0 && a.1 <= b.1
15}
16
17const fn overlaps(a: (u8, u8), b: (u8, u8)) -> bool {
18    b.0 <= a.1 && b.1 >= a.0
19}
20
21pub fn solve(input: &Input) -> Result<usize, String> {
22    let condition: fn((u8, u8), (u8, u8)) -> bool = input.part_values(contains, overlaps);
23
24    input
25        .text
26        .lines()
27        .enumerate()
28        .map(|(line_idx, line)| {
29            let intervals = parse_intervals(line).ok_or_else(|| {
30                format!("Line {line_idx}: Invalid input - expected 'u8-u8,u8-u8'")
31            })?;
32            Ok(usize::from(condition(intervals.0, intervals.1)))
33        })
34        .sum()
35}
36
37#[test]
38pub fn tests() {
39    use crate::input::{
40        test_part_one_error, test_part_one_no_allocations, test_part_two_no_allocations,
41    };
42
43    let test_input = "2-4,6-8
442-3,4-5
455-7,7-9
462-8,3-7
476-6,4-6
482-6,4-8";
49    test_part_one_no_allocations!(test_input => 2);
50    test_part_two_no_allocations!(test_input => 4);
51
52    let real_input = include_str!("day04_input.txt");
53    test_part_one_no_allocations!(real_input => 569);
54    test_part_two_no_allocations!(real_input => 936);
55
56    for input in ["1-2,3-4\nfoo", "1-2,3-4\n300-400,1-2", "1-2,3-4\n-1-2,3-4"] {
57        test_part_one_error!(input => "Line 1: Invalid input - expected 'u8-u8,u8-u8'");
58    }
59}