Advent of Code 2025

Day 5 and factoring out some utils

Changed files
+77 -30
src
+1 -6
src/Day2.hs
···
module Day2 where
import Debug.Trace
-
-
split :: Char -> String -> [String]
-
split c s = case dropWhile (== c) s of
-
"" -> []
-
s' -> w : split c s''
-
where (w, s'') = break (== c) s'
+
import Util
isPartOneCandidate :: Int -> Bool
isPartOneCandidate n =
+3 -22
src/Day4.hs
···
import qualified Data.Map as Map
import Data.Map (Map)
-
-
type Grid = Map (Int,Int) Char
-
-
enumerate :: [a] -> [(Int,a)]
-
enumerate l = zip [0..] l
-
-
addRow :: Int -> String -> Grid -> Grid
-
addRow y row grid =
-
foldl
-
(\g (x,c) -> Map.insert (x,y) c g)
-
grid
-
(enumerate row)
+
import Util
parse :: String -> Grid
-
parse s =
-
foldl
-
(\g (x,row) -> addRow x row g)
-
Map.empty
-
(enumerate $ lines s)
-
-
neighbors :: (Int,Int) -> [(Int,Int)]
-
neighbors (x,y) =
-
[(x-1,y-1),(x,y-1),(x+1,y-1),(x-1,y),(x+1,y),(x-1,y+1),(x,y+1),(x+1,y+1)]
+
parse = toGrid
isOccupied :: Grid -> (Int,Int) -> Bool
isOccupied grid p = Map.lookup p grid == Just '@'
isAccessible :: Grid -> (Int,Int) -> Char -> Bool
isAccessible grid p '@' =
-
length (filter (isOccupied grid) (neighbors p)) < 4
+
length (filter (isOccupied grid) (neighbors8 p)) < 4
isAccessible _ _ _ = False
part1 :: String -> String
+43 -2
src/Day5.hs
···
module Day5 where
+
import Debug.Trace
+
import Data.Char
+
import Data.List
+
import Util
+
+
type Range = (Int,Int)
+
+
parse :: String -> ([Range], [Int])
+
parse s = foldl addLine ([], []) $ map (map read) $ map (splitOn (not . isDigit)) $ lines s
+
where addLine (ranges, queries) [lhs, rhs] = ((lhs, rhs) : ranges, queries)
+
addLine (ranges, queries) [q] = (ranges, q : queries)
+
addLine (ranges, queries) [] = (ranges, queries)
+
+
inRange :: Range -> Int -> Bool
+
inRange (lo,hi) q = lo <= q && q <= hi
+
+
inRanges :: [Range] -> Int -> Bool
+
inRanges rs q = any (\r -> inRange r q) rs
+
part1 :: String -> String
-
part1 _ = "Day 5 part 1"
+
part1 input =
+
let (rs,qs) = parse input
+
in show $
+
length $
+
filter (inRanges rs) qs
+
+
rangeUnion :: [Range] -> [Range]
+
rangeUnion rs =
+
let mergeWith cur [] = [cur]
+
mergeWith (lo,hi) ((lo',hi') : rest) =
+
if lo' <= hi then
+
mergeWith (lo, max hi hi') rest
+
else
+
(lo, hi) : mergeWith (lo', hi') rest
+
+
merge [] = []
+
merge (r : rest) = mergeWith r rest
+
in merge $ sort rs
part2 :: String -> String
-
part2 _ = "Day 5 part 2"
+
part2 input =
+
let (rs, _) = parse input
+
in show $
+
sum $
+
map (\(a,b) -> b-a+1) $
+
rangeUnion rs
+30
src/Util.hs
···
+
module Util where
+
+
import qualified Data.Map as Map
+
import Data.Map (Map)
+
+
splitOn :: (Char -> Bool) -> String -> [String]
+
splitOn p s = case dropWhile p s of
+
"" -> []
+
s' -> w : splitOn p s''
+
where (w, s'') = break p s'
+
+
split :: Char -> String -> [String]
+
split c = splitOn (== c)
+
+
enumerate :: [a] -> [(Int,a)]
+
enumerate l = zip [0..] l
+
+
type Point2 = (Int,Int)
+
type Grid = Map Point2 Char
+
+
toGrid :: String -> Grid
+
toGrid s = Map.fromList [((x,y),c) | (y,r) <- enumerate (lines s), (x,c) <- enumerate r]
+
+
neighbors8 :: Point2 -> [Point2]
+
neighbors8 (x,y) =
+
[(x-1,y-1),(x,y-1),(x+1,y-1),(x-1,y),(x+1,y),(x-1,y+1),(x,y+1),(x+1,y+1)]
+
+
neighbors4 :: Point2 -> [Point2]
+
neighbors4 (x,y) =
+
[(x-1,y), (x+1,y), (x,y-1), (x,y+1)]