-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path04.hs
78 lines (65 loc) · 2.64 KB
/
04.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import Data.Char (digitToInt)
import Data.List
import Text.ParserCombinators.Parsec
import qualified Data.Map.Strict as M
data Room = Room { name :: String
, sector :: Int
, checksum :: String } deriving (Show)
data CountedChar = CountedChar Int Char deriving (Eq, Show)
instance Ord CountedChar where
CountedChar n c `compare` CountedChar n' c' = if (n == n')
then c `compare` c'
else n' `compare` n
legalRoom :: Room -> Bool
legalRoom r = all id (zipWith matches cs (checksum r))
where matches (CountedChar _ c) c' = c == c'
cs = sort [CountedChar (length g) (g !! 0)
| g <- group (sort (name r))]
onlyLegalRooms :: [Room] -> [Room]
onlyLegalRooms = filter legalRoom
sumSectorIds :: [Room] -> Int
sumSectorIds = foldl (\acc r -> acc + (sector r)) 0
-- Part 2.
rotatedAlphabet :: Int -> M.Map Char Char
rotatedAlphabet n = M.fromList (zip alphabet (rot n alphabet))
where alphabet = ['a'..'z']
rot n = take 26 . drop n . cycle
data DecryptedRoom = DecryptedRoom { dName :: String
, dSector :: Int }
instance Show DecryptedRoom where
show (DecryptedRoom n s) = n ++ ": " ++ (show s)
decrypt :: Room -> DecryptedRoom
decrypt r = DecryptedRoom (map decode (name r)) (sector r)
where rA = rotatedAlphabet (sector r)
decode c = rA M.! c
-- Parser.
parseRoomName :: Parser String
parseRoomName = concat <$> (many1 $ do
part <- many1 lower
char '-'
return part)
parseDecimal :: Parser Int
parseDecimal = convertDecimal <$> many1 digit where
convertDecimal = foldl' (\a i -> a * 10 + digitToInt i) 0
parseRoom :: Parser Room
parseRoom = do
name <- parseRoomName
sector <- parseDecimal
char '['
checksum <- many1 lower
char ']'
return (Room name sector checksum)
parseRooms :: Parser [Room]
parseRooms = many1 $ do
room <- parseRoom
newline
return room
-- Main.
main = do
input <- getContents
let rooms = parse parseRooms "" input
let legalRooms = onlyLegalRooms <$> rooms
let result = sumSectorIds <$> legalRooms
putStrLn . show $ result
let decryptedRooms = (map decrypt) <$> legalRooms
putStrLn . show $ (map show) <$> decryptedRooms