1 import Data.List (last, intersperse, sortBy, intercalate, isInfixOf)
2 import Data.List.Split (splitOn)
3 import Data.Char (isLetter, ord, chr)
4 import qualified Data.Map.Lazy as Map
5 import Data.Tuple (swap)
7 data Room = Room { name :: String
14 instrText <- readFile "advent04.txt"
15 let rooms = map (parseLine) $ lines instrText
20 part1 :: [Room] -> IO ()
22 print $ sum $ map (sector) validRooms
24 validChecksum room = (checksum room) == makeChecksum (name room)
25 validRooms = filter (validChecksum) rooms
27 part2 :: [Room] -> IO ()
29 print $ filter (\sn -> isInfixOf "north" (snd sn)) sectorNames
31 validChecksum room = (checksum room) == makeChecksum (name room)
32 validRooms = filter (validChecksum) rooms
33 sectorNames = [((sector r),
34 shiftWord (sector r) (name r)) | r <- validRooms]
37 parseLine :: String -> Room
38 parseLine line = Room {name=name, sector=sector, checksum=checksum}
39 where components = splitOn "-" line
40 -- name = concat $ intersperse "-" $ reverse $ tail $ reverse components
41 name = intercalate "-" $ reverse $ tail $ reverse components
42 sector = read $ head $ splitOn "[" $ last components
43 checksum = filter (isLetter) $ last components
45 countedLetters :: String -> [(Char, Int)]
46 countedLetters name = sortBy sortCLetter $ unsortedCountedLetters name
47 where unsortedCountedLetters name = Map.toList $ Map.fromListWith (+) [(c, 1) | c <- filter (isLetter) name]
49 sortCLetter :: (Char, Int) -> (Char, Int) -> Ordering
50 sortCLetter (l1, n1) (l2, n2)
53 | n1 == n2 = compare l1 l2
55 makeChecksum :: String -> String
56 makeChecksum name = [l | (l, _) <- take 5 $ countedLetters name]
59 shiftWord :: Int -> String -> String
60 shiftWord shift letters = map (shiftLetter shift) letters
62 shiftLetter :: Int -> Char -> Char
63 shiftLetter shift letter
64 | isLetter letter = chr $ (ord letter - ord 'a' + shift) `mod` 26 + ord 'a'