summaryrefslogtreecommitdiff
path: root/day09/day09.hs
blob: c878584ee7da4d82835fdad34d6040e05c368ba3 (plain)
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
import Data.Char
import Data.List
import Data.List.Split

type Id = Int
type Len = Int
data Block = Data Id Len | Free Len deriving (Show, Eq)
type HDD = [Block]

getDataFree :: HDD -> Id -> [Len] -> HDD
getDataFree acc id (d:[]) =
  acc ++ [Data id d]
getDataFree acc id (d:f:r) =
  getDataFree (acc ++ [Data id d, Free f]) (id + 1) r

consume :: String -> HDD
consume file =
  getDataFree [] 0 $ map digitToInt $ trim file
  where trim = dropWhileEnd isSpace . dropWhile isSpace

frag1 :: HDD -> HDD -> HDD -> HDD
frag1 acc dt (Free _:rest) =
  frag1 acc dt rest
frag1 acc (Data di dl:dr) p@(Data pi pl:_)
  | di == pi = (Data pi pl:acc)
  | otherwise = frag1 (Data di dl:acc) dr p
frag1 acc (Free f:rest) (Data id d:pack)
  | d < f = frag1 (Data id d:acc) (Free (f - d):rest) pack
  | d == f = frag1 (Data id d:acc) rest pack
  | d > f = frag1 (Data id f:acc) rest (Data id (d - f):pack)

frag :: HDD -> HDD
frag hdd = reverse $ frag1 [] hdd (reverse hdd)

replace :: Block -> HDD -> HDD
replace d@(Data _ len) hdd =
  let (fore:aft:[]) = splitOn [d] hdd in
    fore ++ [Free len] ++ aft

moveUp :: HDD -> HDD -> Block -> HDD
moveUp acc (h@(Data hid _):rest) d@(Data did _)
  | hid == did = (reverse acc) ++ (h:rest)
  | otherwise = moveUp (h:acc) rest d
moveUp acc (f@(Free fl):rest) d@(Data id dl)
  | fl == dl = remove (d:acc) rest d
  | fl > dl = remove (Free (fl - dl):d:acc) rest d
  | otherwise = moveUp (f:acc) rest d
  where
    remove acc rest d = (reverse acc) ++ (replace d rest)

defrag1 :: HDD -> HDD -> HDD
defrag1 hdd []            = hdd
defrag1 hdd (Free _:rest) = defrag1 hdd rest
defrag1 hdd (d:rest)      = defrag1 (moveUp [] hdd d) rest

defrag :: HDD -> HDD
defrag hdd = defrag1 hdd (reverse hdd)

checksum1 :: Int -> Int -> HDD -> Int
checksum1 acc _ [] =
  acc
checksum1 acc ix (Free len:rest) =
  checksum1 acc (ix + len) rest
checksum1 acc ix (Data id len:rest) =
  checksum1 (acc + (sum $ map (id*) [ix..ix+len-1])) (ix + len) rest

checksum :: HDD -> Int
checksum = checksum1 0 0

part1 :: HDD -> Int
part1 = checksum . frag

part2 :: HDD -> Int
part2 = checksum . defrag

main = do
  file <- readFile "day09.input"
  let hdd = consume file
  putStrLn ("Part 1: " ++ (show $ part1 hdd))
  putStrLn ("Part 2: " ++ (show $ part2 hdd))