aoc2022/day7/main.hs

123 lines
3.3 KiB
Haskell

import System.IO
import Data.List
import Data.List.Split
import Debug.Trace
data Node = File String Int | Dir String Int [Node] deriving (Show)
instance Eq Node where
Dir n1 _ _ == Dir n2 _ _ = n1 == n2
File n1 _ == File n2 _ = n1 == n2
instance Ord Node where
Dir _ s1 _ `compare` Dir _ s2 _ = s1 `compare` s2
File _ s1 `compare` File _ s2 = s1 `compare` s2
nodeSize :: Node -> Int
nodeSize (File _ s) = s
nodeSize (Dir _ s _) = s
nodeInfo :: Node -> String
nodeInfo (File n s) = n ++ "(file, size=" ++ show s ++ ")"
nodeInfo (Dir n s _) = n ++ "(dir, size=" ++ show s ++ ")"
printNode :: Int -> Node -> String
printNode lvl n = case n of
File n s -> sp ++ "- " ++ n ++ " (file, size=" ++ show s ++ ")\n"
Dir n s c -> sp ++ "- " ++ n ++ " (dir, size=" ++ show s ++ ")\n" ++ printNodes (lvl+1) c
where
sp = replicate (2*lvl) ' '
printNodes :: Int -> [Node] -> String
printNodes a = concat . map (printNode a)
--parseDir' :: [String] -> [Node]
--parseDir' (x:xs) =
dirInNodes :: String -> [Node] -> (Bool,Node,[Node])
dirInNodes name onodes = (exists, instanc, removed)
where
matches = filter (\node@(Dir n _ _) -> n == name) onodes
exists = (length matches) > 0
instanc = if exists then head matches else File "<nonexistent>" 0
removed = delete instanc onodes
parseDir :: [Node] -> [Node] -> [String] -> ([Node],[String])
parseDir onodes nodes (x:xs)
| "$ cd " `isPrefixOf` x = ((Dir (drop 5 x) (sum $ map nodeSize nodes) nodes:onodes), xs)
| "dir " `isPrefixOf` x = case dirInNodes (drop 4 x) onodes of
(True,node,restnodes) -> parseDir restnodes (node:nodes) xs
(False,_,_) -> parseDir onodes nodes xs
| otherwise = parseDir onodes (File fname fsize:nodes) xs
where
[fsize',fname] = splitOn " " x
fsize = read fsize' :: Int
parseCli :: [Node] -> [String] -> [String] -> [Node]
parseCli nodes [] [] = []
parseCli nodes stack [] = onodes ++ parseCli onodes reststack []
where (onodes,reststack) = parseDir nodes [] $ stack
parseCli nodes stack (x:xs)
| x == "$ cd .." = onodes ++ parseCli onodes reststack xs
| otherwise = parseCli nodes (x:stack) xs
where (onodes,reststack) = parseDir nodes [] $ stack
prepCli :: [String] -> [String]
prepCli = filter (/= "$ ls")
findNodes :: (Node -> Bool) -> Node -> [Node]
findNodes f file@(File _ _) = if (f file) then [file] else []
findNodes f dir@(Dir _ _ c) = (if (f dir) then [dir] else []) ++ (concat $ map (findNodes f) c)
dirSize :: (Int -> Bool) -> Node -> Bool
dirSize _ (File _ _) = False
dirSize f (Dir _ s _) = f s
findDeletable :: Int -> Int -> Node -> Node
findDeletable fss req root@(Dir _ used _) = smallest
where
tbf = req - (fss-used)
candidates = findNodes (dirSize (>= tbf)) root
smallest = head $ sort candidates
handler :: String -> String
handler s = (show sumUnder) ++ "\n" ++
(show savsize) ++ "\n"
where
rootNode = last $ parseCli [] [] $ prepCli $ lines s
under100k = findNodes (dirSize (<= 100000)) rootNode
sumUnder = sum $ map (\(Dir _ s _) -> s) under100k
(Dir _ savsize _) = findDeletable 70000000 30000000 rootNode
main :: IO ()
main = do
interact handler