advent_of_code/year2020/
day10.rs

1use crate::input::Input;
2
3type JoltageAmount = u64;
4
5pub fn solve(input: &Input) -> Result<JoltageAmount, String> {
6    // "Any given adapter can take an input 1, 2, or 3 jolts lower than
7    // its rating and still produce its rated output joltage":
8    const MAX_DIFF: JoltageAmount = 3;
9
10    let mut joltages = std::iter::once(Ok(0))
11        .chain(input.text.lines().enumerate().map(|(line_idx, line)| {
12            line.parse::<JoltageAmount>().map_err(|parse_error| {
13                format!("Line {}: Invalid joltage - {}", line_idx + 1, parse_error)
14            })
15        }))
16        .collect::<Result<Vec<_>, _>>()?;
17
18    joltages.sort_unstable();
19
20    // "In addition, your device has a built-in joltage adapter rated
21    // for 3 jolts higher than the highest-rated adapter in your bag":
22    joltages.push(joltages[joltages.len() - 1] + MAX_DIFF);
23
24    if input.is_part_one() {
25        joltages
26            .windows(2)
27            .try_fold([0; MAX_DIFF as usize + 1], |mut diff_count, window| {
28                let diff = window[1] - window[0];
29                if diff > MAX_DIFF {
30                    Err(format!(
31                        "Too big difference between adapters - cannot go from {} to {}",
32                        window[0], window[1]
33                    ))
34                } else {
35                    diff_count[diff as usize] += 1;
36                    Ok(diff_count)
37                }
38            })
39            .map(|diff_counts| diff_counts[1] * diff_counts[3])
40    } else {
41        let mut distinct_ways_counts = vec![0; joltages.len()];
42        distinct_ways_counts[0] = 1;
43
44        for (idx, joltage) in joltages.iter().enumerate() {
45            let this_distinct_count = distinct_ways_counts[idx];
46
47            joltages[(idx + 1)..]
48                .iter()
49                .take_while(|&higher_joltage| higher_joltage - joltage <= MAX_DIFF)
50                .enumerate()
51                .for_each(|(offset, _)| {
52                    distinct_ways_counts[idx + offset + 1] += this_distinct_count;
53                });
54        }
55
56        Ok(distinct_ways_counts[distinct_ways_counts.len() - 1])
57    }
58}
59
60#[test]
61pub fn tests() {
62    use crate::input::{test_part_one, test_part_one_error, test_part_two};
63
64    let example = "16\n10\n15\n5\n1\n11\n7\n19\n6\n12\n4";
65    test_part_one!(example => 35);
66    test_part_two!(example => 8);
67
68    test_part_one_error!(" " => "Line 1: Invalid joltage - invalid digit found in string");
69    test_part_one_error!("100" => "Too big difference between adapters - cannot go from 0 to 100");
70    test_part_one!("1" => 1);
71    test_part_two!("1" => 1);
72
73    let real_input = include_str!("day10_input.txt");
74    test_part_one!(real_input => 2376);
75    test_part_two!(real_input => 129_586_085_429_248);
76}