advent-of-code 2025.5.0

Solutions to Advent of Code
Documentation
use crate::input::Input;

fn parse_digits(input_string: &str) -> Result<Vec<i32>, String> {
    let result = input_string
        .chars()
        .map(|b| {
            b.to_digit(10)
                .map(|b| b as i32)
                .ok_or_else(|| "Invalid input".to_string())
        })
        .collect::<Result<Vec<_>, String>>()?;
    if result.len() > 1000 {
        return Err("Too big input".to_string());
    }
    Ok(result)
}

pub fn solve(input: &Input) -> Result<String, String> {
    let mut digits = parse_digits(input.text)?;

    if input.is_part_one() {
        const PHASES: usize = 100;
        let mut new_digits = vec![0; digits.len()];
        for _ in 0..PHASES {
            for (index, digit) in new_digits.iter_mut().enumerate() {
                let positives: i32 = (index..digits.len())
                    .step_by((index + 1) * 4)
                    .flat_map(|i| digits.iter().skip(i).take(index + 1))
                    .sum();
                let negatives: i32 = ((index + ((index + 1) * 2))..digits.len())
                    .step_by((index + 1) * 4)
                    .flat_map(|i| digits.iter().skip(i).take(index + 1))
                    .sum();
                *digit = (positives - negatives).abs() % 10;
            }

            std::mem::swap(&mut digits, &mut new_digits);
        }

        Ok(digits
            .iter()
            .take(8)
            .map(|b| ((b + 48) as u8) as char)
            .collect())
    } else {
        let offset = input
            .text
            .get(0..7)
            .map(str::parse)
            .ok_or_else(|| "Invalid input".to_string())?
            .map_err(|_| "Invalid input".to_string())?;

        let times_to_repeat = 10000;
        let message_length = input.text.len() * times_to_repeat;
        let end_sequence_length = message_length.checked_sub(offset).ok_or("Too big offset")?;

        let mut end_sequence: Vec<i32> = digits
            .into_iter()
            .cycle()
            .skip(offset)
            .take(end_sequence_length)
            .collect();

        for _ in 0..100 {
            for i in 0..(end_sequence.len() - 1) {
                let index = end_sequence_length - i - 1;
                end_sequence[index - 1] = (end_sequence[index - 1] + end_sequence[index]) % 10;
            }
        }

        Ok(end_sequence
            .iter()
            .take(8)
            .map(|&b| ((b + 48) as u8) as char)
            .collect())
    }
}

#[test]
pub fn tests() {
    test_part_one!("80871224585914546619083218645595" => "24176176".to_string());

    let real_input = include_str!("day16_input.txt");
    test_part_one!(real_input => "37153056".to_string());
    test_part_two!(real_input => "60592199".to_string());
}