-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.hs
108 lines (88 loc) · 2.68 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
{-# LANGUAGE LambdaCase #-}
import Data.List
import Data.List.Split
import Data.Maybe (mapMaybe)
data FileSystem = FsDir String [FileSystem]
| FsFile String Int
deriving Show
type FlatFs = [([String], String, Int)]
data Command = Cd String
| Ls [Entry]
deriving Show
data Entry = Dir String
| File String Int
deriving Show
parse :: String -> Maybe Command
parse input =
case lines input of
"ls":output -> pure . Ls $ map (f . words) output
where f = \case ["dir", name] -> Dir name
[size, name] -> File name (read size)
['c':'d':' ':target] -> pure $ Cd target
_ -> Nothing
parseAll :: String -> [Command]
parseAll =
mapMaybe parse
. filter (not . null)
. splitOn "\n$ "
exec :: [Command] -> FlatFs
exec = go [] []
where go cwd acc =
\case Ls es:rest -> go cwd (acc ++ concatMap (f cwd) es) rest
Cd s:rest -> go (nav cwd s) acc rest
[] -> acc
f cwd = \case File fname size -> [(reverse cwd, fname, size)]
_ -> []
nav :: [String] -> String -> [String]
nav (_:xs) ".." = xs
nav xs x = x:xs
fst3 :: (a, b, c) -> a
fst3 (x, _, _) = x
buildTree :: FlatFs -> FileSystem
buildTree = go "/" . reverse . sort
where go name entries =
let groups = groupBy (\x y -> take 1 (fst3 x) == take 1 (fst3 y)) entries
in
FsDir name (concatMap dirEntries groups)
dirEntries =
\case entries@((name:_, _ ,_ ):_) -> [go name (map dropDir entries)]
files -> map (\(_, fname, size) -> FsFile fname size) files
dropDir =
\case e@([],_,_) -> e
(_:xs, f, s) -> (xs, f, s)
size :: FileSystem -> Int
size = \case FsFile _ s -> s
FsDir _ es -> sum (map size es)
collect :: (Int -> Bool) -> FileSystem -> [(String, Int)]
collect p = go
where go =
\case FsDir name entries ->
let s = sum (map size entries)
in
if p s
then (name, s):concatMap go entries
else concatMap go entries
_ -> []
part1 :: FileSystem -> Int
part1 = sum . map snd . collect (< 10000)
part2 :: FileSystem -> Int
part2 fs =
let used = size fs
total = 70000000
unused = total - used
needToDelete = 30000000 - unused
in
head
. sort
. map snd
$ collect (>= needToDelete) fs
main :: IO ()
main = main' "input.txt"
exampleMain :: IO ()
exampleMain = main' "example.txt"
main' :: FilePath -> IO ()
main' file = do
input <- parseAll <$> readFile file
let tree = buildTree (exec input)
print (part1 tree)
print (part2 tree)