137 lines
3.3 KiB
Markdown
137 lines
3.3 KiB
Markdown
# [Year 2015 Day 2](https://adventofcode.com/2015/day/2)
|
|
|
|
This day provides us our first little taste of effectful parsing
|
|
|
|
<!-- idris
|
|
module Years.Y2015.Day2
|
|
|
|
import Control.Eff
|
|
|
|
import Runner
|
|
-->
|
|
|
|
```idris
|
|
import Data.List
|
|
import Data.List1
|
|
import Data.String
|
|
```
|
|
|
|
<!-- idris
|
|
%default total
|
|
-->
|
|
|
|
## 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
|
|
```
|
|
|
|
<!-- idris
|
|
public export
|
|
day2 : Day
|
|
day2 = Both 2 part1 part2
|
|
-->
|