day 7
parent
6fe209eac9
commit
07ade6f13d
@ -0,0 +1,23 @@
|
||||
$ 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
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,5 @@
|
||||
import
|
||||
|
||||
main = do
|
||||
|
||||
return ()
|
@ -0,0 +1,169 @@
|
||||
module Day7Lib
|
||||
( Node (..),
|
||||
Session (..),
|
||||
newSession,
|
||||
cd,
|
||||
-- mkdir,
|
||||
touch,
|
||||
--
|
||||
processInput1,
|
||||
processInput2,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad
|
||||
import Data.List
|
||||
import Debug.Trace
|
||||
import Text.Parsec
|
||||
import Text.Parsec.Combinator
|
||||
|
||||
data Node = File {name :: String, size :: Maybe Int} | Dir {name :: String, size :: Maybe Int, children :: [Node]} deriving (Show, Eq)
|
||||
|
||||
newDir :: String -> Node
|
||||
newDir name = Dir name Nothing []
|
||||
|
||||
newFile :: String -> Int -> Node
|
||||
newFile name size = File name (Just size)
|
||||
|
||||
data Session = Session
|
||||
{ pwd :: [String],
|
||||
filesystem :: Node
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
newSession :: Session
|
||||
newSession = Session {pwd = ["/"], filesystem = Dir "/" Nothing []}
|
||||
|
||||
mkdir :: Session -> String -> Session
|
||||
mkdir session dname = modifyNode session (\n -> n {children = newDir dname : children n})
|
||||
|
||||
touch :: Session -> String -> Int -> Session
|
||||
touch session name size = modifyNode session (\n -> n {children = newFile name size : children n})
|
||||
|
||||
getNode :: Node -> [String] -> Node
|
||||
getNode fs [] = fs
|
||||
getNode fs (p : wd) =
|
||||
let (cd, rest) = partition (\d -> (name d) == p) (children fs)
|
||||
in if null cd then error ("Directory not found" ++ show wd) else getNode (head cd) wd
|
||||
|
||||
modifyNode :: Session -> (Node -> Node) -> Session
|
||||
modifyNode session@(Session {pwd = wd, filesystem = fs}) f = session {filesystem = modifyNode' (tail wd) fs f}
|
||||
|
||||
modifyNode' :: [String] -> Node -> (Node -> Node) -> Node
|
||||
modifyNode' [] node f = f node
|
||||
modifyNode' (p : wd) node f =
|
||||
let (chd, rest) = case partition (\d -> (name d) == p) (children node) of
|
||||
([chd], rest) -> (chd, rest)
|
||||
(_, _) -> error ("Directory not found: " ++ show p)
|
||||
in node {children = (modifyNode' wd chd f) : rest}
|
||||
|
||||
cd :: Session -> String -> Session
|
||||
cd session@(Session {pwd = wd}) ".." = session {pwd = init wd}
|
||||
cd session@(Session {pwd = wd}) "/" = session {pwd = ["/"]}
|
||||
cd session@(Session {pwd = p : wd, filesystem = fs}) dname = case find (\x -> name x == dname) (children $ getNode fs wd) of
|
||||
Just _ -> session {pwd = (p : wd) ++ [dname]}
|
||||
Nothing -> error "directory not found"
|
||||
cd _ _ = error "invalid cd"
|
||||
|
||||
applyCommand :: Session -> Command -> Session
|
||||
applyCommand session (CD name) = cd session name
|
||||
applyCommand session (LS children) =
|
||||
foldl
|
||||
( \session child -> case child of
|
||||
(Nothing, name) -> mkdir session name
|
||||
(Just size, name) -> touch session name size
|
||||
)
|
||||
session
|
||||
children
|
||||
|
||||
showNode :: String -> Node -> String
|
||||
showNode pf (Dir {size = Just s, name = n, children = ch}) =
|
||||
pf
|
||||
++ "dir "
|
||||
++ (show s)
|
||||
++ " "
|
||||
++ n
|
||||
++ "\n"
|
||||
++ concatMap (showNode (' ' : pf)) ch
|
||||
showNode pf (File {size = Just s, name = n}) = pf ++ show s ++ " " ++ n ++ "\n"
|
||||
showNode _ _ = error "show node error"
|
||||
|
||||
----------------------------
|
||||
|
||||
computeSizes :: Node -> Node
|
||||
computeSizes node@(Dir {children = ch}) =
|
||||
let newCh = map computeSizes ch
|
||||
newSize = fmap sum . sequence $ map size newCh
|
||||
in node {size = newSize, children = newCh}
|
||||
computeSizes file@(File {}) = file
|
||||
|
||||
---------------------------
|
||||
|
||||
data Command = CD String | LS [(Maybe Int, String)] deriving (Show, Eq)
|
||||
|
||||
history = command `sepBy1` (optional (char '\n'))
|
||||
|
||||
command = do
|
||||
string "$ "
|
||||
cdCommand <|> lsCommand
|
||||
|
||||
dirName = many (oneOf ['a' .. 'z'])
|
||||
|
||||
cdCommand :: Parsec String () Command
|
||||
cdCommand = do
|
||||
string "cd "
|
||||
dirname <- string "/" <|> string ".." <|> dirName
|
||||
return $ CD dirname
|
||||
|
||||
lsCommand :: Parsec String () Command
|
||||
lsCommand = do
|
||||
string "ls\n"
|
||||
children <- many1 (dirLine <|> fileLine)
|
||||
return $ LS children
|
||||
|
||||
dirLine :: Parsec String () (Maybe Int, String)
|
||||
dirLine = do
|
||||
string "dir "
|
||||
name <- dirName
|
||||
try (char '\n')
|
||||
return (Nothing, name)
|
||||
|
||||
fileLine :: Parsec String () (Maybe Int, String)
|
||||
fileLine = do
|
||||
size <- many (oneOf ['0' .. '9'])
|
||||
char ' '
|
||||
name <- many (oneOf ('.' : ['a' .. 'z']))
|
||||
try (char '\n')
|
||||
return (Just (read size), name)
|
||||
|
||||
-- ####################
|
||||
|
||||
processInput1 :: String -> Int
|
||||
processInput1 input =
|
||||
let h = case parse history "" input of
|
||||
Right h -> h
|
||||
Left e -> error (show e)
|
||||
session = foldl applyCommand newSession h
|
||||
sized = computeSizes . filesystem $ session
|
||||
in sumTooSmall sized
|
||||
|
||||
-- gintm = maybe 0 id
|
||||
|
||||
sumTooSmall :: Node -> Int
|
||||
sumTooSmall dir@(Dir {size = Just s, children = ch}) = (if s <= 100000 then s else 0) + (sum . (map sumTooSmall) $ ch)
|
||||
sumTooSmall f@(File {size = Just s}) = 0
|
||||
sumTooSmall _ = error "sum error"
|
||||
|
||||
processInput2 :: String -> Int
|
||||
processInput2 input =
|
||||
let h = case parse history "" input of
|
||||
Right h -> h
|
||||
Left e -> error (show e)
|
||||
session = foldl applyCommand newSession h
|
||||
sized = computeSizes . filesystem $ session
|
||||
sizes = sort . getAllDirSizes $ sized
|
||||
in head . dropWhile (< 30000000 - (70000000 - last sizes)) $ sizes
|
||||
|
||||
getAllDirSizes node@(Dir {size = Just s, children = ch}) = s : (concatMap getAllDirSizes ch)
|
||||
getAllDirSizes File {} = []
|
||||
getAllDirSizes n = error ("allDirSizes error" ++ show n)
|
@ -0,0 +1,41 @@
|
||||
import Day7Lib (processInput1, processInput2)
|
||||
import System.IO
|
||||
import Test.HUnit
|
||||
|
||||
testCases1 =
|
||||
[ ("data/input070.txt", 95437),
|
||||
("data/input071.txt", 1084134)
|
||||
]
|
||||
|
||||
testCase1 (file, result) = do
|
||||
withFile
|
||||
file
|
||||
ReadMode
|
||||
( \handle -> do
|
||||
contents <- hGetContents handle
|
||||
assertEqual "input test" result $ processInput1 contents
|
||||
)
|
||||
|
||||
testCases2 =
|
||||
[ ("data/input070.txt", 24933642),
|
||||
("data/input071.txt", 6183184)
|
||||
]
|
||||
|
||||
testCase2 (file, result) = do
|
||||
withFile
|
||||
file
|
||||
ReadMode
|
||||
( \handle -> do
|
||||
contents <- hGetContents handle
|
||||
assertEqual ("input test: " ++ file) result $ processInput2 contents
|
||||
)
|
||||
|
||||
tests =
|
||||
TestList $
|
||||
[TestCase (testCase1 c) | c <- testCases1]
|
||||
++ [TestCase (testCase2 c) | c <- testCases2]
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
runTestTT tests
|
||||
return ()
|
@ -0,0 +1,37 @@
|
||||
import Day7Lib
|
||||
import Test.HUnit
|
||||
|
||||
tNewSession =
|
||||
TestCase $
|
||||
assertEqual
|
||||
"new session"
|
||||
(Session {pwd = ["/"], filesystem = Dir "/" Nothing []})
|
||||
(newSession)
|
||||
|
||||
tNewDir1 =
|
||||
TestCase $
|
||||
assertEqual
|
||||
"new dir 1"
|
||||
(Session {pwd = ["/", "a"], filesystem = Dir "/" Nothing [Dir "a" Nothing []]})
|
||||
(let s = newSession in cd s "a")
|
||||
|
||||
tNewFile1 =
|
||||
TestCase $
|
||||
assertEqual
|
||||
"new dir 1"
|
||||
(Session {pwd = ["/"], filesystem = Dir "/" Nothing [File "b" (Just 100)]})
|
||||
(let s = newSession in touch s "b" 100)
|
||||
|
||||
|
||||
tests =
|
||||
TestList $
|
||||
[
|
||||
tNewSession,
|
||||
tNewDir1,
|
||||
tNewFile1
|
||||
]
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
runTestTT tests
|
||||
return ()
|
Loading…
Reference in New Issue