Advent of Code Day 1, 2025

Posted on December 31, 2025 by Jarvis Cochrane

Amidst all my other projects and pastimes, I’ve decided to make a serious effort to learn Haskell.

I’m still working my way through Learn You A Haskell For Great Good, but it’s time to practice writing some actual programs.

It’s time for Advent of Code!

This is a correct solution for part 1 of the puzzle for Day 1, 2025. It’s the most complex Haskell program I’ve written to date, and I think I might even almost understand the bind operator (>>=)!

{--
    Haskell solution for Advent Of Code 2025 day 1 (a).

    Given:

      * A virtual combination lock whose dial can freely rotate through 
        positions numbered 0 to 99 so that rotating right (clockwise) one 
        step from position 99 goes to position 0 and rotating left 
        (anticlockwise) one step from position 0 goes to position 99.

      * An initial dial position of 50.

      * A file `input.txt` containing a list of rotations, one per line,
        of the form Lx or Rx (regex: "[LR]\d+") denoting rotations by
        x steps left or right. E.g:

            L68
            L30
            R48
            L5

    Find:

      * The number of times the dial is at position 0 after a rotation.

    Run with:

      runghc aoc-2025-01a.hs

    December 31, 2025 by Jarvis Cochrane
    Copyright (c) 2025 Jarvis Cochrane
--}

initial_position = 50 :: Int

-- Convert a code to a positive (right) or negative (left) integer

readCode :: String -> Int
readCode ('L':xs) = - (read xs :: Int)
readCode ('R':xs) = (read xs :: Int)

-- Convert list of codes to list of +/- integers

readCodes :: [String] -> [Int]
readCodes xs = map readCode xs

-- Replace each code with the result of rotating the dial as specified
-- rotateDial 50 (readCodes ["L5", "R10"]) -> [45, 55]

rotateDial ::  Int -> [Int] -> [Int]
rotateDial dialPosition [] = []
rotateDial dialPosition (x:xs) =
    let newDialPosition = (dialPosition + x) `mod` 100
    in newDialPosition : rotateDial newDialPosition xs

-- Return the number of zeros found in the list

countZeros :: [Int] -> Int
countZeros xs = length $ filter (\x -> x == 0) xs

-- Return the number of zeroes found by applying the code sequence to
-- the initial position

solve :: [String] -> Int
solve xs = countZeros $ rotateDial initial_position $ readCodes xs

-- Read input file, solve, and output result

main = readFile "input.txt" >>= (putStrLn . show . solve . lines)