diff --git a/aoc2022.cabal b/aoc2022.cabal index fb16dc9..2cb4c3c 100644 --- a/aoc2022.cabal +++ b/aoc2022.cabal @@ -45,8 +45,10 @@ library Day5.Shared Day6 Day7 + Day7.Interpreter Day7.Parser Lib + Shared other-modules: Paths_aoc2022 hs-source-dirs: diff --git a/src/Day7.hs b/src/Day7.hs index 7f3cb58..c27c542 100644 --- a/src/Day7.hs +++ b/src/Day7.hs @@ -1,86 +1,10 @@ module Day7 ( - buildTree, - buildTree', - mkdir, - Directory (..), - calculateSize, - filterDirectories, - sumUp, day7 ) where import Day7.Parser -import Data.Map (Map) -import qualified Data.Map as Map - -data Directory = Directory - { sub :: Map String Directory - , files :: Map String Int - , isRoot :: Bool -} deriving (Eq) - - -instance Show Directory where - show d = show (files d) ++ " - " ++ show (sub d) ++ "\n" - -mkdir :: Directory -mkdir = Directory { sub = Map.empty, files = Map.empty, isRoot = False } - -rootDirectory :: Directory -rootDirectory = Directory { sub = Map.empty, files = Map.empty, isRoot = True } - -buildTree :: [TerminalCommand] -> Directory -buildTree commands = fst (buildTree' rootDirectory commands) - -buildTree' :: Directory -> [TerminalCommand] -> (Directory, [TerminalCommand]) -buildTree' dir [] = (dir, []) -buildTree' dir (command:cs) = case command of - Listing entries -> - let asFiles = Map.fromList $ toFiles entries - newDir = dir { files = asFiles } - in buildTree' newDir cs - In dirName -> - let subFolders = sub dir - selectedDir = Map.findWithDefault mkdir dirName subFolders - (replacement, rest) = buildTree' selectedDir cs - updatedSub = Map.insert dirName replacement subFolders - in buildTree' dir { sub = updatedSub} rest - Out -> (dir, cs) - Root -> if isRoot dir - then buildTree' dir cs - else (dir, command:cs) - -toFiles :: [ListingEntry] -> [(String, Int)] -toFiles ((FileListing name size):rest) = (name, size): toFiles rest -toFiles (_:rest) = toFiles rest -toFiles [] = [] - -calculateSize :: Directory -> Int -calculateSize dir = sum (Map.elems (files dir)) + sum sizes - where - subFolders = Map.elems (sub dir) - sizes = map calculateSize subFolders - -flatten :: Directory -> [(String, Directory)] -flatten dir = flatten' ("/", dir) - where - flatten' (name, d) = (name, d) : concatMap flatten' (Map.toList (sub d)) - -sizeOfDirectories :: Directory -> [(String, Int)] -sizeOfDirectories dir = map withSize allDirectories - where - allDirectories = flatten dir - withSize (name, directory) = (name, calculateSize directory) - -filterDirectories :: (Int -> Bool) -> Directory -> [(String, Int)] -filterDirectories predicate dir = filter (\(_, size) -> predicate size) $ sizeOfDirectories dir - -sumUp :: [(String, Int)] -> Int -sumUp = sum . map snd - -forceRight :: Either a b -> b -forceRight (Left _) = error "forced Right but got Left" -forceRight (Right b) = b +import Day7.Interpreter +import Shared day7 :: IO () day7 = do diff --git a/src/Day7/Interpreter.hs b/src/Day7/Interpreter.hs new file mode 100644 index 0000000..3321dbe --- /dev/null +++ b/src/Day7/Interpreter.hs @@ -0,0 +1,80 @@ +module Day7.Interpreter ( + buildTree, + mkdir, + Directory (..), + calculateSize, + filterDirectories, + sizeOfDirectories, + sumUp, +) where + +import Day7.Parser +import Data.Map (Map) +import qualified Data.Map as Map + +data Directory = Directory + { sub :: Map String Directory + , files :: Map String Int + , isRoot :: Bool +} deriving (Eq) + + +instance Show Directory where + show d = "DIR " ++ show (Map.toList $ files d) ++ " - " ++ show (Map.toList $ sub d) ++ "\n" + +mkdir :: Directory +mkdir = Directory { sub = Map.empty, files = Map.empty, isRoot = False } + +rootDirectory :: Directory +rootDirectory = mkdir { isRoot = True } + +buildTree :: [TerminalCommand] -> Directory +buildTree commands = fst (buildTree' rootDirectory commands) + where + -- executes terminal commands and returns all remaining commands + buildTree' :: Directory -> [TerminalCommand] -> (Directory, [TerminalCommand]) + buildTree' dir [] = (dir, []) + buildTree' dir (command:cs) = case command of + Listing entries -> + let asFiles = Map.fromList $ toFiles entries + newDir = dir { files = asFiles } + in buildTree' newDir cs -- update current directory and return all remaining commands + In dirName -> + let subFolders = sub dir + selectedDir = Map.findWithDefault mkdir dirName subFolders -- select or create folder + (replacement, rest) = buildTree' selectedDir cs -- recurse down + updatedSub = Map.insert dirName replacement subFolders -- update folder the map of the current directory + in buildTree' dir { sub = updatedSub} rest -- continue with updated folder and rest of commands + Out -> (dir, cs) -- return to previous caller and return all remaining commands + Root -> if isRoot dir + then buildTree' dir cs -- return to previous caller as long is current directory is not root + else (dir, command:cs) -- keep the Root command because it is not yet completed + +-- convert to file-pairs and ignore directories +toFiles :: [ListingEntry] -> [(String, Int)] +toFiles ((FileListing name size):rest) = (name, size): toFiles rest +toFiles (_:rest) = toFiles rest +toFiles [] = [] + +calculateSize :: Directory -> Int +calculateSize dir = sum (Map.elems (files dir)) + sum sizes + where + subFolders = Map.elems (sub dir) + sizes = map calculateSize subFolders + +flatten :: Directory -> [(String, Directory)] +flatten dir = flatten' ("/", dir) + where + flatten' (name, d) = (name, d) : concatMap flatten' (Map.toList (sub d)) + +sizeOfDirectories :: Directory -> [(String, Int)] +sizeOfDirectories dir = map withSize allDirectories + where + allDirectories = flatten dir + withSize (name, directory) = (name, calculateSize directory) + +filterDirectories :: (Int -> Bool) -> Directory -> [(String, Int)] +filterDirectories predicate dir = filter (\(_, size) -> predicate size) $ sizeOfDirectories dir + +sumUp :: [(String, Int)] -> Int +sumUp = sum . map snd diff --git a/src/Shared.hs b/src/Shared.hs new file mode 100644 index 0000000..dcc0ff0 --- /dev/null +++ b/src/Shared.hs @@ -0,0 +1,6 @@ +module Shared(forceRight) where + +forceRight :: Either a b -> b +forceRight (Left _) = error "forced Right but got Left" +forceRight (Right b) = b + diff --git a/test/Day7Spec.hs b/test/Day7Spec.hs index 7efbdce..53741b6 100644 --- a/test/Day7Spec.hs +++ b/test/Day7Spec.hs @@ -4,8 +4,9 @@ module Day7Spec (spec) where import Test.Hspec import Text.Heredoc -import Day7 +import Shared import Day7.Parser +import Day7.Interpreter import qualified Data.Map as Map inputPart1 :: String @@ -72,10 +73,6 @@ cdOutParsed = [ Listing [FileListing "f" 23, DirListing "foo", DirListing "bar"] ] -forceRight :: Either a b -> b -forceRight (Left _) = error "forced Right but got Left" -forceRight (Right b) = b - spec :: Spec spec = describe "Day7" $ do