Advent of Code 2024 day 2

    Day 2 was another problem that suited Haskell. Most of the problem was tackled by my writing down the problem definition as a series of predicate functions.

    As with day 1, I read the input using a combination of lines, words, and read.

    Definitions

    A report is safe if the differences between adjacent terms are

    • all the same sign
    • all big enough
    • all small enough
    isSafe, allSameSign, bigEnough, smallEnough, safeWhenDamped :: [Int] -> Bool
    isSafe xs = allSameSign diffs && bigEnough diffs && smallEnough diffs
      where diffs = zipWith (-) xs (tail xs)

    Each of those tests has its own predicate.

    allSameSign xs = (all (>0) xs) || (all (<0) xs)
    bigEnough = all ((>= 1) . abs)
    smallEnough = all ((<= 3) . abs)

    From that, I count how many safe reports there are with length and filter.

    part1 reports = length $ filter isSafe reports

    Damping

    For part 2, I need to check the "damped" reports. The damped reports are one with one element missing.

    inits and tails split a list into all prefixes and suffixes; I combine them with (++) after I drop 1 element from the suffixes.

    damped :: [Int] -> [[Int]]
    damped line = zipWith (++) (inits line) (drop 1 $ tails line)

    A report is "safe when damped" if any of the damped reports is safe.

    safeWhenDamped = (any isSafe) . damped

    That gives the modified version of isSafe

    part2 reports = length $ filter isSafe' reports
        where isSafe' l = isSafe l || safeWhenDamped l

    Addendum

    Having that big conjunction of predicates in isSafe annoyed me slightly. Surely there's a better way to combine a set of predicates? If I put the predicates in a list, I can take advantage of a list being an Applicative Functor and use <*> to apply each function to the differences.

    isSafe xs = and $ [allSameSign, bigEnough, smallEnough] <*> (pure diffs)
      where diffs = zipWith (-) xs (tail xs)

    Possibly more elegant, but I doubt it's any easier to read.

    Code

    You can get the code from my locally-hosted Git repo, or from Codeberg.

    Neil Smith

    Read more posts by this author.