2024 AoC Day 2 – Red-Nosed Reports

This is a solution to Advent of Code 2024 day 2, written in Raku.

https://adventofcode.com/2024/day/2

Part One

Analyze the unusual data from the engineers. How many reports are safe?

Raku

The Raku [<] and [>] reduction meta-operators are very handy way to performa a boolean check for list order. With a few more boolean meta-operators and a hyper operator to compute a diference list we have a solution.

my @lines = 'input-2.txt'.IO.lines>>.split(/\s+/);

say [+] @lines.map(
    -> @levels {
        ([<] @levels or [>] @levels) and         # in increasing/decreasing order
        [&&] (@levels <<-<< @levels[1..*]).map(  # difference between successive pairs
            -> $n { 1 <= abs($n) <= 3 }          # $n is in the valid range
        )
    });
213

Rust

use std::error::Error;
use std::fs;

fn ascending(v: &Vec<i32>) -> bool {
    let mut ordered = true;
    for i in 0..v.len()-1 { ordered = ordered && (v[i] < v[i+1]); }
    ordered
}

fn descending(v: &Vec<i32>) -> bool {
    let mut ordered = true;
    for i in 0..v.len()-1 { ordered = ordered && (v[i] > v[i+1]); }
    ordered
}

fn in_range(v: &Vec<i32>) -> bool {
    let mut valid = true;
    for i in 0..v.len()-1 {
        let diff = (v[i] - v[i+1]).abs();
        valid = valid && 1 <= diff && diff <= 3;
    }
    valid
}

fn main() -> Result<(), Box<dyn Error>> {
    let input = fs::read_to_string("/Users/donaldh/git/advent-of-code/2024/input-2.txt")?;
    let safe: Vec<i32> = input.lines()
        .map(|x| {
            let values: Vec<i32> = x.split(" ").map(
                |x| x.parse::<i32>().unwrap()
            ).collect();
            if (ascending(&values) || descending(&values)) && in_range(&values) {
                1
            } else {
                0
            }
        })
        .collect();
    let total: i32  = safe.iter().sum();
    println!("{:#?}", total);

    Ok(())
}
213

Part Two

Update your analysis by handling situations where the Problem Dampener can remove a single level from unsafe reports. How many reports are now safe?

Part two has a bit of refactoring to let us try successive sublists, along with another boolean meta-operator.

my @lines = 'input-2.txt'.IO.lines>>.split(/\s+/);

sub is-safe(@levels) {
    ([<] @levels or [>] @levels) and         # in increasing/decreasing order
    [&&] (@levels <<-<< @levels[1..*]).map(  # difference between successive pairs
        -> $n { 1 <= abs($n) <= 3 }          # $n is in the valid range
    )
}

say [+] @lines.map(
    -> @levels {
        is-safe(@levels) or   # is original list safe
        [||] (^+@levels).map( # is any sub-list safe
            -> $pos {
                # sub-list omitting $pos
                is-safe(@levels[0..^$pos, $pos+1..*].flat)
            }
        )
    });
285
raku