--- /dev/null
+module Main(main) where
+
+import Data.List (last, intersperse, sortBy, intercalate, isInfixOf, init)
+import Data.List.Split (splitOn)
+import Data.Char (isLetter, ord, chr)
+import qualified Data.Map.Lazy as Map
+
+data Room = Room { name :: String
+ , sector :: Int
+ , checksum :: String
+ } deriving (Show)
+
+main :: IO ()
+main = do
+ instrText <- readFile "data/advent04.txt"
+ let rooms = map (parseLine) $ lines instrText
+ part1 rooms
+ part2 rooms
+
+
+part1 :: [Room] -> IO ()
+part1 rooms = do
+ print $ sum $ map (sector) validRooms
+ where
+ validChecksum room = (checksum room) == makeChecksum (name room)
+ validRooms = filter (validChecksum) rooms
+
+part2 :: [Room] -> IO ()
+part2 rooms = do
+ print $ fst $ head $ filter (\sn -> isInfixOf "north" (snd sn)) sectorNames
+ where
+ validChecksum room = (checksum room) == makeChecksum (name room)
+ validRooms = filter (validChecksum) rooms
+ sectorNames = [((sector r),
+ shiftWord (sector r) (name r)) | r <- validRooms]
+
+
+parseLine :: String -> Room
+parseLine line = Room {name=name, sector=sector, checksum=checksum}
+ where components = splitOn "-" line
+ name = intercalate "-" $ init components
+ sector = read $ head $ splitOn "[" $ last components
+ checksum = filter (isLetter) $ last components
+
+countedLetters :: String -> [(Char, Int)]
+countedLetters name = sortBy sortCLetter $ unsortedCountedLetters name
+ where unsortedCountedLetters name =
+ Map.toList $ Map.fromListWith (+) [(c, 1) | c <- filter (isLetter) name]
+
+sortCLetter :: (Char, Int) -> (Char, Int) -> Ordering
+sortCLetter (l1, n1) (l2, n2)
+ | n1 < n2 = GT
+ | n1 > n2 = LT
+ | n1 == n2 = compare l1 l2
+
+makeChecksum :: String -> String
+makeChecksum name = [l | (l, _) <- take 5 $ countedLetters name]
+
+
+shiftWord :: Int -> String -> String
+shiftWord shift letters = map (shiftLetter shift) letters
+
+shiftLetter :: Int -> Char -> Char
+shiftLetter shift letter
+ | isLetter letter = chr $ (ord letter - ord 'a' + shift) `mod` 26 + ord 'a'
+ | otherwise = ' '