aboutsummaryrefslogtreecommitdiffstats
path: root/day18/main.hs
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2021-12-18 17:50:01 +0200
committerJan Tuomi <jans.tuomi@gmail.com>2021-12-19 16:39:03 +0200
commit247d7534c247f19be84c0984581279ef5545b1e6 (patch)
treec336cac1d5bb8e585abeb672d7f0f0c22d320278 /day18/main.hs
parent63c1f74fe9a20984f6a19c3c2344793b1ed10072 (diff)
Day 18
Diffstat (limited to 'day18/main.hs')
-rw-r--r--day18/main.hs181
1 files changed, 181 insertions, 0 deletions
diff --git a/day18/main.hs b/day18/main.hs
new file mode 100644
index 0000000..29cf312
--- /dev/null
+++ b/day18/main.hs
@@ -0,0 +1,181 @@
+module Main where
+
+import Data.Char (isDigit)
+import Data.Foldable (Foldable (foldl'))
+import Data.List (foldl1')
+import qualified Data.List as L
+import Data.Maybe (fromMaybe)
+import Debug.Trace (trace, traceShow, traceShowId)
+import Utils
+
+-- Snailfish number element
+data SFFlatE = SFFlatRegular Int | SFFlatStart | SFFlatEnd | SFFlatSep deriving (Eq)
+
+data SFTreeE = SFTreeRegular Int | SFTreePair (SFTreeE, SFTreeE) deriving (Eq)
+
+-- Snailfish number
+newtype SFFlatN = SFFlatN [SFFlatE] deriving (Eq)
+
+newtype SFTreeN = SFTreeN SFTreeE deriving (Eq)
+
+instance Num SFTreeN where
+ a + b = sfnSum a b
+ abs (SFTreeN a) = SFTreeN $ SFTreeRegular $ sfeMagn a
+
+instance Show SFTreeN where
+ show (SFTreeN sfe) = show sfe
+
+instance Show SFTreeE where
+ show (SFTreeRegular int) = show int
+ show (SFTreePair (a, b)) = "[" ++ show a ++ "," ++ show b ++ "]"
+
+instance Show SFFlatN where
+ show (SFFlatN sfes) = concatMap showSFFlatE sfes
+
+instance Show SFFlatE where
+ show = showSFFlatE
+
+showSFFlatE (SFFlatRegular int) = show int
+showSFFlatE SFFlatStart = "["
+showSFFlatE SFFlatEnd = "]"
+showSFFlatE SFFlatSep = ","
+
+expectSFFlatE :: SFFlatE -> [SFFlatE] -> [SFFlatE]
+expectSFFlatE expectedE (e : rest) =
+ if expectedE == e
+ then rest
+ else error $ "unexpected flat element: " ++ show e ++ ", expected: " ++ show expectedE
+expectSFFlatE _ [] = error "expectSFFlatE: empty []"
+
+flatToTree' :: [SFFlatE] -> (SFTreeE, [SFFlatE])
+flatToTree' (SFFlatStart : rest) =
+ let (result1, rest1) = flatToTree' rest
+ rest2 = expectSFFlatE SFFlatSep rest1
+ (result2, rest3) = flatToTree' rest2
+ rest4 = expectSFFlatE SFFlatEnd rest3
+ in (SFTreePair (result1, result2), rest4)
+flatToTree' ((SFFlatRegular int) : rest) =
+ (SFTreeRegular int, rest)
+flatToTree' other = error $ "weird flatToTree: " ++ show (SFFlatN other)
+
+flatToTree :: SFFlatN -> SFTreeN
+flatToTree (SFFlatN sfes) =
+ let (result, rest) = flatToTree' sfes
+ in if null rest
+ then SFTreeN result
+ else error $ "flatToTree incomplete: " ++ show (SFFlatN rest)
+
+treeToFlat' :: SFTreeE -> [SFFlatE]
+treeToFlat' (SFTreeRegular int) = [SFFlatRegular int]
+treeToFlat' (SFTreePair (a, b)) = concat [[SFFlatStart], treeToFlat' a, [SFFlatSep], treeToFlat' b, [SFFlatEnd]]
+
+treeToFlat :: SFTreeN -> SFFlatN
+treeToFlat (SFTreeN sfe) = SFFlatN $ treeToFlat' sfe
+
+sfnSum :: SFTreeN -> SFTreeN -> SFTreeN
+sfnSum (SFTreeN a) (SFTreeN b) =
+ let sum = SFTreeN $ SFTreePair (a, b)
+ in sfnReduce $ sum
+
+sfnReduceM :: SFTreeN -> Either SFTreeN SFTreeN
+sfnReduceM sfn =
+ let explodeM tree =
+ let exploded = sfnExplode tree
+ in if exploded /= tree then Left exploded else Right exploded
+ splitM tree =
+ let splitted = sfnSplit tree
+ in if splitted /= tree then Left splitted else Right splitted
+ in do
+ result1 <- explodeM sfn
+ result2 <- splitM sfn
+ return sfn
+
+sfnReduce sfn =
+ let afterReduce = sfnReduceM sfn
+ in case afterReduce of
+ Left sfn' -> sfnReduce sfn'
+ Right sfn' -> sfn'
+
+sfeMagn :: SFTreeE -> Int
+sfeMagn (SFTreeRegular int) = int
+sfeMagn (SFTreePair (a, b)) = 3 * sfeMagn a + 2 * sfeMagn b
+
+sfeSplitM :: SFTreeE -> Either SFTreeE SFTreeE
+sfeSplitM sfe@(SFTreeRegular int)
+ | int >= 10 = Left $ SFTreePair (SFTreeRegular $ int `div` 2, SFTreeRegular $ int - int `div` 2)
+ | otherwise = Right sfe
+sfeSplitM (SFTreePair (a, b)) = do
+ lhs <- case sfeSplitM a of
+ Left a' -> Left $ SFTreePair (a', b)
+ Right a' -> Right a'
+ rhs <- case sfeSplitM b of
+ Left b' -> Left $ SFTreePair (a, b')
+ Right b' -> Right b'
+ Right $ SFTreePair (lhs, rhs)
+
+sfnSplit :: SFTreeN -> SFTreeN
+sfnSplit (SFTreeN sfe) = sfe $> sfeSplitM .> fromSymEither .> SFTreeN
+
+sfnExplodeAtDepth :: Int -> Int -> SFFlatN -> Maybe ([SFFlatE], Int, SFFlatE, SFFlatE)
+sfnExplodeAtDepth idx 0 (SFFlatN (SFFlatStart : rest1)) =
+ let (leftValue : rest2) = rest1
+ (SFFlatSep : rest3) = rest2
+ (rightValue : rest4) = rest3
+ (SFFlatEnd : rest5) = rest4
+ in Just (SFFlatRegular 0 : rest5, idx, leftValue, rightValue)
+sfnExplodeAtDepth idx depth (SFFlatN (sfe : rest)) =
+ let depth' = case sfe of
+ SFFlatStart -> depth - 1
+ SFFlatEnd -> depth + 1
+ _ -> depth
+ in sfnExplodeAtDepth (idx + 1) depth' (SFFlatN rest) $> fmap (\(rest, b, c, d) -> (sfe : rest, b, c, d))
+sfnExplodeAtDepth idx depth (SFFlatN []) = Nothing
+
+replaceLeftRegularIfExists :: Int -> SFFlatE -> SFFlatN -> SFFlatN
+replaceLeftRegularIfExists idx replacement sfn@(SFFlatN sfes) =
+ let elemAtIdx = sfes !! idx
+ (SFFlatRegular rInt) = replacement
+ in case elemAtIdx of
+ SFFlatRegular int -> SFFlatN $ take idx sfes ++ [SFFlatRegular (rInt + int)] ++ drop (idx + 1) sfes
+ other -> if idx > 0 then replaceLeftRegularIfExists (idx - 1) replacement sfn else sfn
+
+replaceRightRegularIfExists :: Int -> SFFlatE -> SFFlatN -> SFFlatN
+replaceRightRegularIfExists idx replacement sfn@(SFFlatN sfes) =
+ let elemAtIdx = sfes !! idx
+ (SFFlatRegular rInt) = replacement
+ in case elemAtIdx of
+ SFFlatRegular int -> SFFlatN $ take idx sfes ++ [SFFlatRegular (rInt + int)] ++ drop (idx + 1) sfes
+ other -> if idx < length sfes - 1 then replaceRightRegularIfExists (idx + 1) replacement sfn else sfn
+
+sfnExplodeM :: SFTreeN -> Maybe SFTreeN
+sfnExplodeM tree = do
+ let flat1 = treeToFlat tree
+ (flat2', idx, leftValue, rightValue) <- sfnExplodeAtDepth 0 4 flat1
+ let flat2 = SFFlatN flat2'
+ let flat3 = replaceLeftRegularIfExists (idx - 1) leftValue flat2
+ let flat4 = replaceRightRegularIfExists (idx + 1) rightValue flat3
+ return (flatToTree flat4)
+
+sfnExplode :: SFTreeN -> SFTreeN
+sfnExplode tree = fromMaybe tree $ sfnExplodeM tree
+
+parseStringToSFFlatN ('[' : rest) = SFFlatStart : parseStringToSFFlatN rest
+parseStringToSFFlatN (']' : rest) = SFFlatEnd : parseStringToSFFlatN rest
+parseStringToSFFlatN (',' : rest) = SFFlatSep : parseStringToSFFlatN rest
+parseStringToSFFlatN (' ' : rest) = parseStringToSFFlatN rest
+parseStringToSFFlatN "" = []
+parseStringToSFFlatN other = SFFlatRegular (read $ takeWhile isDigit other) : parseStringToSFFlatN (dropWhile isDigit other)
+
+main :: IO ()
+main = do
+ contents <- getContents
+ let flats = contents $> lines .> map (parseStringToSFFlatN .> SFFlatN)
+ let trees = flats $> map flatToTree
+ let reduceResult = foldl1' (+) trees
+ print reduceResult
+ let magnResult = abs reduceResult
+ print magnResult
+
+ let allPairs = [(a, b) | a <- trees, b <- trees, a /= b]
+ let biggestMagn = allPairs $> map (uncurry (+) .> abs .> (\(SFTreeN (SFTreeRegular mag)) -> mag)) .> maximum
+ print biggestMagn