--- /dev/null
+-- Writeup at https://work.njae.me.uk/2024/12/25/advent-of-code-2024-day-23/
+
+import AoC
+
+import Data.List
+import Data.List.Split
+import Data.Char
+
+data Schematic = Lock [Int] | Key [Int]
+ deriving (Show, Eq, Ord)
+
+main :: IO ()
+main =
+ do dataFileName <- getDataFileName
+ text <- readFile dataFileName
+ let (locks, keys) = makeSchematics text
+ -- print locks
+ -- print keys
+ print $ part1 locks keys
+
+part1 :: [Schematic] -> [Schematic] -> Int
+part1 locks keys = length [(l, k) | l <- locks, k <- keys, compatible l k]
+
+compatible :: Schematic -> Schematic -> Bool
+compatible (Lock ls) (Key ks) = all (<= 5) $ zipWith (+) ls ks
+
+makeSchematics :: String -> ([Schematic], [Schematic])
+makeSchematics = partition isLock . fmap makeSchematic . splitTexts . lines
+ where isLock (Lock _) = True
+ isLock _ = False
+
+splitTexts :: [String] -> [[String]]
+splitTexts = splitOn [""]
+
+makeSchematic :: [String] -> Schematic
+makeSchematic ss
+ | isKeySchematic ss = Key $ heightsOf ss
+ | isLockSchematic ss = Lock $ heightsOf ss
+ | otherwise = error "Invalid schematic"
+
+isKeySchematic, isLockSchematic :: [String] -> Bool
+isKeySchematic [a,_,_,_,_,_,b] = a == "....." && b == "#####"
+isLockSchematic [a,_,_,_,_,_,b] = a == "#####" && b == "....."
+
+heightsOf :: [String] -> [Int]
+heightsOf = fmap ((-1 +) . length . filter (== '#')) . transpose
+
executable advent24
import: warnings, common-extensions, build-directives, common-modules
main-is: advent24/Main.hs
- build-depends: attoparsec, text, containers
\ No newline at end of file
+ build-depends: attoparsec, text, containers
+
+executable advent25
+ import: warnings, common-extensions, build-directives, common-modules
+ main-is: advent25/Main.hs
+ build-depends: split
\ No newline at end of file