Year 2015 Day 2
This day provides us our first little taste of effectful parsing
import Data.List
import Data.List1
import Data.String
Box structure
A record to hold the parameters of a box and methods to operate on it
record Box where
constructor MkBox
length, width, height : Integer
Box methods
.area
provides the surface area of the box, .slack
provides the surface area
of the smallest face, .ribbon
provides the smallest perimeter of a face, and
.bow
provides the volume of the box.
Names are as described in the problem
(.area) : Box -> Integer
(.area) (MkBox length width height) =
2 * length * width + 2 * width * height + 2 * length * height
(.slack) : Box -> Integer
(.slack) (MkBox length width height) =
foldl1 min [length * width, width * height, length * height]
(.ribbon) : Box -> Integer
(.ribbon) (MkBox length width height) =
foldl1 min [2 * (length + width), 2 * (length + height), 2 * (width + height)]
(.bow) : Box -> Integer
(.bow) (MkBox length width height) = length * width * height
Provide the total amount of ribbon needed, by adding the ribbon (smallest perimeter) and the bow (volume) values together.
totalRibbon : Box -> Integer
totalRibbon x = x.ribbon + x.bow
Provide the total amount of paper needed by adding the surface area and the slack (smallest side surface area) together.
paperNeeded : Box -> Integer
paperNeeded x = x.area + x.slack
Box parsing
Parse a box from an input string of form lengthxwidthxheight
.
parseBox : Has (Except String) fs =>
String -> Eff fs Box
parseBox str = do
First, we split the string into 3 parts by pattern matching on the result of
split
, using pure
to lift the computation into the effect, and throwing an
error if the pattern match fails.
l ::: [w, h] <- pure $ split (== 'x') str
| xs => throw "Box did not have exactly 3 components: \{show xs}"
Then try to parse each of the three integers, throwing an error if parsing fails, then construct and return our box.
length <- note "Failed parsing length: \{show l}" $ parsePositive l
width <- note "Failed parsing width: \{show w}" $ parsePositive w
height <- note "Failed parsing height: \{show h}" $ parsePositive h
pure $ MkBox length width height
Part Functions
Part 1
Split the input into lines, effectfully traverse our box parser over the resulting list of lines, then sum the amounts of paper needed for each box.
We return the list of parsed boxes as the context here to avoid needing to parse again in part 2
part1 : Eff (PartEff String) (Integer, List Box)
part1 = do
input <- map lines $ askAt "input"
boxes <- traverse parseBox input
let output = sum . map paperNeeded $ boxes
pure (output, boxes)
Part 2
Much the same as part 1, except with the amount of ribbon needed instead of the amount of paper, and without the parsing, since we received the already parsed list of boxes from part 1 as our context value.
part2 : (boxes : List Box) -> Eff (PartEff String) Integer
part2 boxes = pure . sum . map totalRibbon $ boxes