fb2839ece8accc5aadc20d24d091f5ce39bb9e18
[advent-of-code-16.git] / advent04.hs
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
6 data Room = Room { name :: String
7 , sector :: Int
8 , checksum :: String
9 } deriving (Show)
10
11 main :: IO ()
12 main = do
13 instrText <- readFile "advent04.txt"
14 let rooms = map (parseLine) $ lines instrText
15 part1 rooms
16 part2 rooms
17
18
19 part1 :: [Room] -> IO ()
20 part1 rooms = do
21 print $ sum $ map (sector) validRooms
22 where
23 validChecksum room = (checksum room) == makeChecksum (name room)
24 validRooms = filter (validChecksum) rooms
25
26 part2 :: [Room] -> IO ()
27 part2 rooms = do
28 print $ fst $ head $ filter (\sn -> isInfixOf "north" (snd sn)) sectorNames
29 where
30 validChecksum room = (checksum room) == makeChecksum (name room)
31 validRooms = filter (validChecksum) rooms
32 sectorNames = [((sector r),
33 shiftWord (sector r) (name r)) | r <- validRooms]
34
35
36 parseLine :: String -> Room
37 parseLine line = Room {name=name, sector=sector, checksum=checksum}
38 where components = splitOn "-" line
39 name = intercalate "-" $ reverse $ tail $ reverse components
40 sector = read $ head $ splitOn "[" $ last components
41 checksum = filter (isLetter) $ last components
42
43 countedLetters :: String -> [(Char, Int)]
44 countedLetters name = sortBy sortCLetter $ unsortedCountedLetters name
45 where unsortedCountedLetters name = Map.toList $ Map.fromListWith (+) [(c, 1) | c <- filter (isLetter) name]
46
47 sortCLetter :: (Char, Int) -> (Char, Int) -> Ordering
48 sortCLetter (l1, n1) (l2, n2)
49 | n1 < n2 = GT
50 | n1 > n2 = LT
51 | n1 == n2 = compare l1 l2
52
53 makeChecksum :: String -> String
54 makeChecksum name = [l | (l, _) <- take 5 $ countedLetters name]
55
56
57 shiftWord :: Int -> String -> String
58 shiftWord shift letters = map (shiftLetter shift) letters
59
60 shiftLetter :: Int -> Char -> Char
61 shiftLetter shift letter
62 | isLetter letter = chr $ (ord letter - ord 'a' + shift) `mod` 26 + ord 'a'
63 | otherwise = ' '