-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.hs
78 lines (61 loc) · 1.98 KB
/
run.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
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (guard)
import Data.List (inits, tails)
import Data.String (IsString)
import qualified Data.Set as Set
import Data.Void (Void)
import Text.Megaparsec
import Text.Megaparsec.Char
type Parser = Parsec Void String
newtype Atom = Atom String
deriving (Show, Eq, Ord, IsString)
newtype Molecule = Molecule { atoms :: [Atom] }
deriving (Show, Eq, Ord)
data Rule = Rule !Atom !Molecule deriving Show
azl :: [Char]
azl = ['a'..'z']
azu :: [Char]
azu = ['A'..'Z']
atom :: Parser Atom
atom = Atom <$> ((:) <$> oneOf azu <*> many (oneOf azl))
molecule :: Parser Molecule
molecule = Molecule <$> many atom
eAtom :: Parser Atom
eAtom = Atom <$> string "e"
rule :: Parser Rule
rule = Rule <$> (atom <|> eAtom) <*> (string " => " *> molecule)
unsafeRight (Right x) = x
parseAll :: String -> ([Rule], Molecule)
parseAll input = ( unsafeRight . traverse (parse rule "") $ r
, unsafeRight (parse molecule "" l)
)
where l = last . lines $ input
r = init . filter (not . null) . lines $ input
foci :: [a] -> [([a], a, [a])]
foci [] = []
foci xs =
map (\case (ys, z:zs) -> (ys, z, zs))
. init
$ zip (inits xs) (tails xs)
step :: [Rule] -> Molecule -> Set.Set Molecule
step rules input = Set.fromList do
Rule a (Molecule replacement) <- rules
(h, e, t) <- foci (atoms input)
guard $ a == e
pure $ Molecule $ h ++ replacement ++ t
part1 :: [Rule] -> Molecule -> Int
part1 rules = length . step rules
part2 :: Molecule -> Int
part2 (Molecule input) = symbols - lefts - rights - (2*ys) - 1
where symbols = length input
lefts = length $ filter (== "Rn") input
rights = length $ filter (== "Ar") input
ys = length $ filter (== "Y") input
main = do
(rules, input) <- parseAll <$> readFile "input.txt"
print (part1 rules input)
print (part2 input)