/
Day7.hs
132 lines (106 loc) · 4.3 KB
/
Day7.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
{-# LANGUAGE OverloadedStrings #-}
module Day7 where
import Control.Monad (foldM)
import Control.Monad.State (StateT, MonadState (get, put))
import Control.Monad.Logger (MonadLogger, runStdoutLoggingT)
import Data.Maybe (fromMaybe)
import Data.List (tails, sort, find)
import qualified Data.Map as M
import Text.Megaparsec (ParsecT, sepEndBy1, (<|>), some, MonadParsec (try))
import Text.Megaparsec.Char (eol, string, letterChar, char)
import Data.Void (Void)
import Data.Text (Text, pack, unpack)
import Utils (parseFile, parsePositiveNumber, OccMapBig, addKey)
dayNum :: Int
dayNum = 7
-------------------- PUTTING IT TOGETHER --------------------
solveEasy :: FilePath -> IO (Maybe Integer)
solveEasy fp = runStdoutLoggingT $ do
input <- parseFile parseInput fp
result <- processInputEasy input
findEasySolution result
solveHard :: FilePath -> IO (Maybe Integer)
solveHard fp = runStdoutLoggingT $ do
input <- parseFile parseInput fp
result <- processInputEasy input
findHardSolution result
-------------------- PARSING --------------------
type InputType = [LineType]
type LineType = Command
data Command =
ChangeDirectoryCommand String |
ListDirectoryCommand |
ListedDirectory String |
ListedFile Integer String
deriving (Show)
parseInput :: (MonadLogger m) => ParsecT Void Text m InputType
parseInput = sepEndBy1 parseLine eol
parseLine :: (MonadLogger m) => ParsecT Void Text m LineType
parseLine = parseCD <|> parseLS <|> parseDir <|> parseFile
where
parseCD = do
string "$ cd "
dir <- (unpack <$> string "..") <|> (unpack <$> string "/") <|> some letterChar
return $ ChangeDirectoryCommand dir
parseLS = string "$ ls" >> return ListDirectoryCommand
parseDir = do
string "dir "
dir <- some letterChar
return $ ListedDirectory dir
parseFile = do
fileSize <- fromIntegral <$> parsePositiveNumber
char ' '
fileName <- some (letterChar <|> char '.')
return $ ListedFile fileSize fileName
-------------------- SOLVING EASY --------------------
type EasySolutionType = OccMapBig [String]
processInputEasy :: (MonadLogger m) => InputType -> m EasySolutionType
processInputEasy inputs = directoryMap <$> solveFold inputs
findEasySolution :: (MonadLogger m) => EasySolutionType -> m (Maybe Integer)
findEasySolution dirMap = do
let largePairs = filter (<= 100000) (M.elems dirMap)
return $ Just $ sum largePairs
-------------------- SOLVING HARD --------------------
type HardSolutionType = EasySolutionType
processInputHard :: (MonadLogger m) => InputType -> m HardSolutionType
processInputHard _ = undefined
findHardSolution :: (MonadLogger m) => HardSolutionType -> m (Maybe Integer)
findHardSolution dirMap = do
let allDirSizes = sort (M.elems dirMap)
let usedSpace = last allDirSizes
let currentUnusedSpace = 70000000 - usedSpace
return $ find (\i -> currentUnusedSpace + i >= 30000000) allDirSizes
-------------------- SOLUTION PATTERNS --------------------
solveFold :: (MonadLogger m) => [LineType] -> m FSState
solveFold = foldM foldLine initialFoldV
data FSState = FSState
{ currentDirectory :: [String]
, directoryMap :: OccMapBig [String]
} deriving (Show)
initialFoldV :: FSState
initialFoldV = FSState [] M.empty
foldLine :: (MonadLogger m) => FSState -> LineType -> m FSState
foldLine prevState command = case command of
ChangeDirectoryCommand dir -> if dir == ".."
then return $ prevState { currentDirectory = tail (currentDirectory prevState)}
else if dir == "/"
then return $ prevState { currentDirectory = ["/"]}
else return $ prevState { currentDirectory = dir : currentDirectory prevState}
ListedFile size _ -> do
let allDirs = currentDirectory prevState
let newDirMap = foldl (\mp d -> addKey mp d size) (directoryMap prevState) (init $ tails allDirs)
return $ prevState { directoryMap = newDirMap}
_ -> return prevState
-------------------- BOILERPLATE --------------------
smallFile :: FilePath
smallFile = "inputs_2022/day_" <> show dayNum <> "_small.txt"
largeFile :: FilePath
largeFile = "inputs_2022/day_" <> show dayNum <> "_large.txt"
easySmall :: IO (Maybe Integer)
easySmall = solveEasy smallFile
easyLarge :: IO (Maybe Integer)
easyLarge = solveEasy largeFile
hardSmall :: IO (Maybe Integer)
hardSmall = solveHard smallFile
hardLarge :: IO (Maybe Integer)
hardLarge = solveHard largeFile