Day 7 Part 1
This commit is contained in:
97
src/Day7.hs
Normal file
97
src/Day7.hs
Normal file
@@ -0,0 +1,97 @@
|
||||
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 [] = []
|
||||
|
||||
sizeThreshold :: Int
|
||||
sizeThreshold = 100000
|
||||
|
||||
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
|
||||
|
||||
day7 :: IO ()
|
||||
day7 = do
|
||||
input <- readFile "ressources/day07-input"
|
||||
putStrLn "Day7"
|
||||
let parsed = forceRight $ parseTerminalLines input
|
||||
let tree = buildTree parsed
|
||||
let filtered = filterDirectories (<= 100000) tree
|
||||
let summed = sumUp filtered
|
||||
putStrLn ("Sum of those directories is " ++ show summed)
|
||||
68
src/Day7/Parser.hs
Normal file
68
src/Day7/Parser.hs
Normal file
@@ -0,0 +1,68 @@
|
||||
module Day7.Parser (
|
||||
parseTerminalLines,
|
||||
TerminalCommand (..),
|
||||
ListingEntry (..),
|
||||
DirName,
|
||||
FileName,
|
||||
Size
|
||||
) where
|
||||
|
||||
import Text.ParserCombinators.Parsec
|
||||
|
||||
type DirName = String
|
||||
type FileName = String
|
||||
type Size = Int
|
||||
|
||||
data ListingEntry = DirListing DirName | FileListing FileName Size
|
||||
deriving (Show, Eq)
|
||||
|
||||
data TerminalCommand = In DirName | Out | Root | Listing [ListingEntry]
|
||||
deriving (Show, Eq)
|
||||
|
||||
parseTerminalLines :: String -> Either ParseError [TerminalCommand]
|
||||
parseTerminalLines = parse terminal "(error)"
|
||||
|
||||
terminal :: GenParser Char st [TerminalCommand]
|
||||
terminal = many command
|
||||
|
||||
command :: GenParser Char st TerminalCommand
|
||||
command = do
|
||||
_ <- string "$ "
|
||||
cd <|> ls
|
||||
|
||||
ls :: GenParser Char st TerminalCommand
|
||||
ls = do
|
||||
_ <- string "ls\n"
|
||||
entries <- many listingEntry
|
||||
return $ Listing entries
|
||||
|
||||
listingEntry :: GenParser Char st ListingEntry
|
||||
listingEntry = dirListing <|> fileListing
|
||||
|
||||
dirListing :: GenParser Char st ListingEntry
|
||||
dirListing = do
|
||||
_ <- string "dir "
|
||||
dirName <- many $ noneOf "\n"
|
||||
_ <- char '\n'
|
||||
return $ DirListing dirName
|
||||
|
||||
fileListing :: GenParser Char st ListingEntry
|
||||
fileListing = do
|
||||
fileSize <- many1 digit
|
||||
_ <- char ' '
|
||||
fileName <- many $ noneOf "\n"
|
||||
_ <- char '\n'
|
||||
return $ FileListing fileName (read fileSize)
|
||||
|
||||
cd :: GenParser Char st TerminalCommand
|
||||
cd = do
|
||||
_ <- string "cd "
|
||||
name <- many $ noneOf "\n"
|
||||
_ <- char '\n'
|
||||
return (nameToDir name)
|
||||
|
||||
nameToDir :: DirName -> TerminalCommand
|
||||
nameToDir "/" = Root
|
||||
nameToDir ".." = Out
|
||||
nameToDir ds = In ds
|
||||
|
||||
@@ -8,6 +8,7 @@ import Day3
|
||||
import Day4
|
||||
import Day5
|
||||
import Day6
|
||||
import Day7
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = do
|
||||
@@ -22,5 +23,7 @@ someFunc = do
|
||||
day5
|
||||
putStrLn "-----------"
|
||||
day6
|
||||
putStrLn "-----------"
|
||||
day7
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user