Compare commits
2 Commits
38eb10ed45
...
1e25a65c09
Author | SHA1 | Date |
---|---|---|
Nathan McCarty | 1e25a65c09 | |
Nathan McCarty | 5d0e711569 |
|
@ -0,0 +1,239 @@
|
|||
import Data.List
|
||||
import Data.List1
|
||||
import Data.Vect
|
||||
import Data.String
|
||||
import System.File.ReadWrite
|
||||
|
||||
%default total
|
||||
|
||||
simple = """
|
||||
$ cd /
|
||||
$ ls
|
||||
dir a
|
||||
14848514 b.txt
|
||||
8504156 c.dat
|
||||
dir d
|
||||
$ cd a
|
||||
$ ls
|
||||
dir e
|
||||
29116 f
|
||||
2557 g
|
||||
62596 h.lst
|
||||
$ cd e
|
||||
$ ls
|
||||
584 i
|
||||
$ cd ..
|
||||
$ cd ..
|
||||
$ cd d
|
||||
$ ls
|
||||
4060174 j
|
||||
8033020 d.log
|
||||
5626152 d.ext
|
||||
7214296 k
|
||||
"""
|
||||
|
||||
data DirEntry = File String Nat | Directory String
|
||||
|
||||
Show DirEntry where
|
||||
show (File name len) = name ++ " (" ++ show len ++ ")"
|
||||
show (Directory name) = name
|
||||
|
||||
parseDirEntry : String -> Maybe DirEntry
|
||||
parseDirEntry entry =
|
||||
let worded = words entry
|
||||
in do fst <- head' worded
|
||||
lst <- last' worded
|
||||
if fst == "dir"
|
||||
then Just (Directory lst)
|
||||
else do len <- parsePositive fst
|
||||
Just (File lst len)
|
||||
|
||||
data Command : Type where
|
||||
Cd : String -> Command
|
||||
Ls : List DirEntry -> Command
|
||||
|
||||
Show Command where
|
||||
show (Cd str) = "cd " ++ str
|
||||
show (Ls dirs) = "ls: " ++ (unwords . map show $ dirs)
|
||||
|
||||
-- Parse one command from a list of lines
|
||||
parseCmd : List String -> Maybe (Command, List String)
|
||||
parseCmd [] = Nothing
|
||||
parseCmd (cmd :: lines) =
|
||||
if isPrefixOf "$ cd" cmd
|
||||
then case last' . words $ cmd of
|
||||
Just dir => Just (Cd dir, lines)
|
||||
Nothing => Nothing
|
||||
else if isPrefixOf "$ ls" cmd
|
||||
then let ours = takeWhile (not . isPrefixOf "$") lines
|
||||
theirs = dropWhile (not . isPrefixOf "$") lines
|
||||
in do children <- traverse parseDirEntry ours
|
||||
Just (Ls children, theirs)
|
||||
else Nothing
|
||||
|
||||
-- Parse all the commands in a list
|
||||
parseCmds : List String -> Maybe (List Command)
|
||||
parseCmds lines = map reverse $ helper [] lines
|
||||
where helper : List Command -> List String -> Maybe (List Command)
|
||||
helper [] [] = Nothing
|
||||
helper cmds [] = Just cmds
|
||||
helper cmds lines =
|
||||
do (command, rest) <- parseCmd lines
|
||||
helper (command :: cmds) (assert_smaller lines rest)
|
||||
|
||||
data Map : Nat -> Type -> Type -> Type where
|
||||
Nil : Map 0 k v
|
||||
(::) : (k, v) -> Map n k v -> Map (S n) k v
|
||||
%name Map map, map2, map3
|
||||
|
||||
get : Eq k => k -> Map n k v -> Maybe v
|
||||
get x [] = Nothing
|
||||
get x ((key, value) :: map) =
|
||||
if key == x
|
||||
then Just value
|
||||
else get x map
|
||||
|
||||
values : Map n k v -> Vect n v
|
||||
values [] = []
|
||||
values ((x, y) :: map) = y :: values map
|
||||
|
||||
mapToList : Map n k v -> List (k, v)
|
||||
mapToList [] = []
|
||||
mapToList (x :: map) = x :: mapToList map
|
||||
|
||||
insert : k -> v -> Map n k v -> Map (S n) k v
|
||||
insert key value map = (key, value) :: map
|
||||
|
||||
replace : Eq k => k -> v -> Map n k v -> Maybe (Map n k v)
|
||||
replace key value [] = Nothing
|
||||
replace key value ((x, y) :: map) =
|
||||
if key == x
|
||||
then Just $ (key, value) :: map
|
||||
else do rest <- replace key value map
|
||||
Just $ (x, y) :: rest
|
||||
|
||||
insertPair : k -> v -> (n: Nat ** Map n k v) -> (m: Nat ** Map m k v)
|
||||
insertPair key value ((fst ** snd)) = (S fst ** insert key value snd)
|
||||
|
||||
record Node where
|
||||
constructor MkDir
|
||||
name : String
|
||||
files : (n: Nat ** Map n String (Nat, String))
|
||||
children : (m : Nat ** Map m String Node)
|
||||
|
||||
size : Node -> Nat
|
||||
size self@(MkDir nodename (fileCount ** files) (childCount ** children)) =
|
||||
let fileTotal = sum . map fst . values $ files
|
||||
dirTotal = sum . map (\x => size (assert_smaller self x)) . values $ children
|
||||
in fileTotal + dirTotal
|
||||
|
||||
showDepth : Nat -> Node -> List String
|
||||
showDepth depth self@(MkDir name (fileCount ** files) (childCount ** children)) =
|
||||
let fileLines = toList . map (\(size, name) => leader depth ++ name ++ " (" ++ show size ++ ")" ) . values $ files
|
||||
header = leader depth ++ name ++ ": (" ++ show (size self) ++ ")"
|
||||
childrenLines = foldMap (\x => showDepth (depth + 1) (assert_smaller self x)) . values $ children
|
||||
in header :: (fileLines ++ childrenLines)
|
||||
where leader : Nat -> String
|
||||
leader k = replicate k ' '
|
||||
|
||||
Show Node where
|
||||
show = unlines . showDepth 0
|
||||
|
||||
emptyNode : Node
|
||||
emptyNode = MkDir "Root" (0 ** Nil) (0 ** Nil)
|
||||
|
||||
directories : Node -> List Node
|
||||
directories self@(MkDir nodename files (childCount ** children)) =
|
||||
self :: foldMap (\x => directories (assert_smaller self x)) (values children)
|
||||
|
||||
insertFile : List String -> String -> Nat -> Node -> Maybe Node
|
||||
insertFile [] name size (MkDir nodename files children) = Just $ MkDir nodename (insertPair name (size, name) files) children
|
||||
insertFile (x :: xs) name size (MkDir nodename files (childCount ** children)) =
|
||||
case get x children of
|
||||
Nothing => Nothing
|
||||
(Just y) => do result <- insertFile xs name size y
|
||||
replaced <- replace x result children
|
||||
Just $ MkDir nodename files (childCount ** replaced)
|
||||
|
||||
insertDir : List String -> String -> Node -> Maybe Node
|
||||
insertDir [] name (MkDir nodename files children) = Just $ MkDir nodename files (insertPair name (MkDir name (0 ** Nil) (0 ** Nil)) children)
|
||||
insertDir (x :: xs) name (MkDir nodename files (childCount ** children)) =
|
||||
case get x children of
|
||||
Nothing => Nothing
|
||||
(Just y) => do result <- insertDir xs name y
|
||||
replaced <- replace x result children
|
||||
Just $ MkDir nodename files (childCount ** replaced)
|
||||
|
||||
insertEntry : List String -> Node -> DirEntry -> Maybe Node
|
||||
insertEntry path node (File name size) = insertFile path name size node
|
||||
insertEntry path node (Directory name) = insertDir path name node
|
||||
|
||||
data PathStack = Stk (List String)
|
||||
|
||||
current : PathStack -> List String
|
||||
current (Stk strs) = reverse strs
|
||||
|
||||
pop : PathStack -> PathStack
|
||||
pop (Stk []) = Stk []
|
||||
pop (Stk (x :: xs)) = Stk xs
|
||||
|
||||
push : String -> PathStack -> PathStack
|
||||
push "/" stack = stack
|
||||
push ".." stack = pop stack
|
||||
push path (Stk strs) = Stk (path :: strs)
|
||||
|
||||
applyCommands : List Command -> Node -> Maybe Node
|
||||
applyCommands xs x = helper (Stk []) xs x
|
||||
where helper : PathStack -> List Command -> Node -> Maybe Node
|
||||
helper stack [] node = Just node
|
||||
helper stack ((Cd name) :: ys) node =
|
||||
helper (push name stack) ys node
|
||||
helper stack ((Ls entries) :: ys) node =
|
||||
do result <- foldlM (insertEntry (current stack)) node entries
|
||||
helper stack ys result
|
||||
|
||||
part1 : String -> IO ()
|
||||
part1 input =
|
||||
case parseCmds (lines input) of
|
||||
Nothing => putStrLn "Failed to parse commands"
|
||||
(Just commands) =>
|
||||
case applyCommands commands emptyNode of
|
||||
Nothing => putStrLn "Failed to apply commands"
|
||||
(Just node) =>
|
||||
let wanted = sum . filter (<= 100000) . map size . directories $ node
|
||||
in putStrLn $ "Part 1: " ++ show wanted
|
||||
|
||||
minBy : Ord b => (a -> b) -> a -> a -> a
|
||||
minBy f x y =
|
||||
let i = f x
|
||||
j = f y
|
||||
in if i < j then x else y
|
||||
|
||||
part2 : String -> IO ()
|
||||
part2 input =
|
||||
case parseCmds (lines input) of
|
||||
Nothing => putStrLn "Failed to parse commands"
|
||||
(Just commands) =>
|
||||
case applyCommands commands emptyNode of
|
||||
Nothing => putStrLn "Failed to apply commands"
|
||||
(Just node) =>
|
||||
let totalSpace = 70000000
|
||||
neededSpace = 30000000
|
||||
freeSpace = minus totalSpace (size node)
|
||||
deficit = minus neededSpace freeSpace
|
||||
canidates = filter ((>= deficit) . size) (directories node)
|
||||
in case Data.List1.fromList canidates of
|
||||
Nothing => putStrLn "Only one canidate?"
|
||||
(Just canidates) =>
|
||||
let smallest = foldl1 (minBy size) canidates
|
||||
in putStrLn $ "Part 2: " ++ (show (size smallest))
|
||||
|
||||
|
||||
partial main : IO ()
|
||||
main =
|
||||
do file <- readFile "input"
|
||||
case file of
|
||||
(Right contents) =>
|
||||
do part1 contents
|
||||
part2 contents
|
||||
(Left err) => printLn err
|
Loading…
Reference in New Issue