-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.hs
116 lines (94 loc) · 2.65 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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TypeApplications #-}
import AoC
import AoC.Grid
import Data.Bifunctor
import Data.Void
import Text.Megaparsec
import Text.Megaparsec.Char
-- TODO: Improve later
type Parser = Parsec Void String
type N = Int
-- Two main representations, one is better suited for "explode" and
-- "split".
type ListNum = [(Int, N)]
-- ... and one is easier to use for "magnitude" and printing
data TreeNum = Regular Int
| Pair TreeNum TreeNum
deriving (Eq, Show)
num :: Parser ListNum
num = val 0
val :: Int -> Parser ListNum
val n = pair n <|> lit n
lit :: Int -> Parser ListNum
lit n = do
v <- read <$> many digitChar
pure [(n, v)]
pair :: Int -> Parser ListNum
pair n = between (char '[') (char ']') do
l <- val (n + 1)
char ','
r <- val (n + 1)
pure $ l ++ r
add :: ListNum -> ListNum -> ListNum
add l r = reduce . map (first (+1)) $ l ++ r
reduce :: ListNum -> ListNum
reduce = fixpoint (split . fixpoint explode)
where explode = \case
[] -> []
(dl, vl):(d1, v1):(d2, v2):[]
| d1 == 5 && d2 == 5 ->
(dl, vl + v1):(4, 0):[]
(d1, v1):(d2, v2):(dr, vr):rest
| d1 == 5 && d2 == 5 ->
(4, 0):(dr, vr + v2):rest
(dl, vl):(d1, v1):(d2, v2):(dr, vr):rest
| d1 == 5 && d2 == 5 ->
(dl, vl + v1):(4, 0):(dr, v2 + vr):rest
x:rest -> x:explode rest
split = \case
[] -> []
(d, v):rest
| v < 10 -> (d, v):split rest
| otherwise ->
let (q, r) = v `divMod` 2
in (d + 1, q):(d + 1, q + r):rest
pp :: TreeNum -> String
pp = go
where go = \case
Regular v -> show v
Pair v1 v2 -> "[" ++ go v1 ++ "," ++ go v2 ++ "]"
tree :: ListNum -> TreeNum
tree = fst . val 0
where pair n xs =
let (l, xs') = val n xs
(r, xs'') = val n xs'
in (Pair l r, xs'')
val n = \case
xs@((d, v):rest)
| d > n -> pair (n + 1) xs
| d == n -> (Regular v, rest)
magnitude :: TreeNum -> Int
magnitude = go
where go = \case
Regular v -> v
Pair v1 v2 -> 3 * go v1 + 2 * go v2
parseAll =
map (\case Right v -> v)
. map (parse num "")
. lines
part1 = magnitude . tree . foldl1 add
part2 input =
maximum
. map (magnitude . tree)
$ [ x1 `add` x2 | x1 <- input
, x2 <- input
, x1 /= x2 ]
main = main' "input.txt"
exampleMain = main' "example.txt"
main' file = do
input <- parseAll <$> readFile file
print (part1 input)
print (part2 input)