advent_of_code/year2018/
day01.rs

1use crate::input::Input;
2use std::collections::HashSet;
3
4type Frequency = i32;
5
6fn parse_frequency_changes(
7    input_string: &str,
8) -> impl Iterator<Item = Result<Frequency, String>> + Clone + '_ {
9    input_string.lines().enumerate().map(|(line_index, line)| {
10        line.parse::<Frequency>()
11            .map_err(|error| format!("Invalid input on line {}: {}", line_index + 1, error))
12    })
13}
14
15pub fn solve(input: &Input) -> Result<Frequency, String> {
16    const MAX_ITERATIONS: usize = 1_000_000;
17    let change_iterator = parse_frequency_changes(input.text);
18
19    if input.is_part_one() {
20        change_iterator.sum::<Result<_, _>>()
21    } else {
22        let mut frequency: Frequency = 0;
23        let mut seen_frequencies = HashSet::from([frequency]);
24
25        let changes: Vec<Frequency> = change_iterator.collect::<Result<_, _>>()?;
26        for &change in changes.iter().cycle().take(MAX_ITERATIONS) {
27            frequency = frequency.checked_add(change).ok_or("Too high frequency")?;
28            if !seen_frequencies.insert(frequency) {
29                return Ok(frequency);
30            }
31        }
32
33        Err(format!(
34            "Frequency not repeated after {MAX_ITERATIONS} iterations"
35        ))
36    }
37}
38
39#[test]
40pub fn tests() {
41    use crate::input::{test_part_one, test_part_two};
42
43    test_part_one!("+1\n-2\n+3\n+1" => 3);
44    test_part_one!("+1\n+1\n+1" => 3);
45    test_part_one!("+1\n+1\n-2" => 0);
46    test_part_one!("-1\n-2\n-3" => -6);
47
48    test_part_two!("+1\n-1" => 0);
49    test_part_two!("+3\n+3\n+4\n-2\n-4" => 10);
50    test_part_two!("-6\n+3\n+8\n+5\n-6" => 5);
51    test_part_two!("+7\n+7\n-2\n-7\n-4" => 14);
52
53    let real_input = include_str!("day01_input.txt");
54    test_part_one!(real_input => 477);
55    test_part_two!(real_input => 390);
56}