-
Notifications
You must be signed in to change notification settings - Fork 0
/
day09.hs
64 lines (53 loc) · 2.17 KB
/
day09.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
module Main where
import Control.Monad (guard)
import Data.Char (digitToInt)
import Data.Either
import Data.Foldable (foldr, toList)
import Data.Maybe (fromMaybe)
import Data.Sequence (Seq, ViewL (..), ViewR (..), viewl, viewr, (<|), (><), (|>))
import Data.Sequence qualified as Seq
import Data.Set qualified as S
main = interact (unlines . sequence [part1, part2] . (!! 0) . map (map digitToInt) . lines)
part1, part2 :: [Int] -> String
part1 = ("Part 1: " ++) . show . checksum . unpack . moveBlock . pack
part2 = ("Part 2: " ++) . show . checksum . unpack . moveFile . pack
checksum = sum . zipWith (*) [0 ..]
pack :: [Int] -> Seq (Either Int (Int, Int))
pack = block 0
where
block i n = case n of
[] -> Seq.empty
(x : xs) -> Right (x, i) <| space i xs
space i n = case n of
[] -> Seq.empty
(x : xs) -> Left x <| block (i + 1) xs
unpack :: Seq (Either Int (Int, Int)) -> [Int]
unpack = concatMap (either (`replicate` 0) (uncurry replicate))
moveBlock :: Seq (Either Int (Int, Int)) -> Seq (Either Int (Int, Int))
moveBlock drive = case viewl drive of
EmptyL -> Seq.empty
(Right b :< xs) -> Right b <| moveBlock xs
(Left x :< xs) -> let (blocks, xs') = popBack xs x in blocks >< moveBlock xs'
moveFile :: Seq (Either Int (Int, Int)) -> Seq (Either Int (Int, Int))
moveFile drive = foldr (\block -> fromMaybe <*> move block) drive blocks
where
blocks = rights (toList drive)
space n = if n > 0 then Seq.singleton (Left n) else Seq.empty
move block@(x, i) xs = do
from <- Seq.findIndexR (== Right block) xs
let xs' = replaceAt from (space x) xs
to <- Seq.findIndexL ((>= x) . fromLeft 0) xs'
guard (from > to)
(Left x') <- xs Seq.!? to
return $ replaceAt to (Right block <| space (x' - x)) xs'
replaceAt :: Int -> Seq a -> Seq a -> Seq a
replaceAt i new xs = Seq.take i xs >< new >< Seq.drop (i + 1) xs
popBack b = go (Seq.empty, b)
where
go (b, rb) n = case viewr rb of
EmptyR -> (b, rb)
_ | n == 0 -> (b, rb)
xs :> Right (x, i) -> case x - n of
x' | x' > 0 -> (b |> Right (n, i), xs |> Right (x', i))
x' -> go (b |> Right (x, i), xs) (n - x)
xs :> x -> (|> x) <$> go (b, xs) n