# [Year 2015 Day 2](https://adventofcode.com/2015/day/2) This day provides us our first little taste of effectful parsing ```idris 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 ```idris 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 ```idris (.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. ```idris 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. ```idris paperNeeded : Box -> Integer paperNeeded x = x.area + x.slack ``` ### Box parsing Parse a box from an input string of form `lengthxwidthxheight`. ```idris 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. ```idris 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. ```idris 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 ```idris 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. ```idris part2 : (boxes : List Box) -> Eff (PartEff String) Integer part2 boxes = pure . sum . map totalRibbon $ boxes ```