doc: switch to literate idris
Switch entire project over to literate markdown files
This commit is contained in:
parent
0ba73aa1fd
commit
94bbe93db9
11 changed files with 712 additions and 319 deletions
|
@ -1,3 +1,4 @@
|
|||
```idris
|
||||
module Years.Y2015
|
||||
|
||||
import Structures.Dependent.FreshList
|
||||
|
@ -6,10 +7,25 @@ import Runner
|
|||
|
||||
import Years.Y2015.Day1
|
||||
import Years.Y2015.Day2
|
||||
```
|
||||
|
||||
# Days
|
||||
|
||||
```idris
|
||||
export
|
||||
y2015 : Year
|
||||
y2015 = MkYear 2015 [
|
||||
```
|
||||
|
||||
## [Day 1](./Day1.md)
|
||||
|
||||
```idris
|
||||
day1
|
||||
```
|
||||
|
||||
## [Day 2](./Day2.md)
|
||||
|
||||
```idris
|
||||
, day2
|
||||
]
|
||||
```
|
|
@ -1,3 +1,8 @@
|
|||
# Day 1
|
||||
|
||||
Pretty simple, basic warmup problem, nothing really novel is on display here except the effectful part computations.
|
||||
|
||||
<!-- idris
|
||||
module Years.Y2015.Day1
|
||||
|
||||
import Control.Eff
|
||||
|
@ -5,13 +10,25 @@ import Control.Eff
|
|||
import Runner
|
||||
|
||||
%default total
|
||||
-->
|
||||
|
||||
## Solver Functions
|
||||
|
||||
This one implements the entirety of the logic for part 1, in a simple tail recursive manner, pattern matching on each character in the input string.
|
||||
|
||||
We include a case for a non-paren character for totality's sake, but since we kinda trust the input here we just don't do anything in that branch.
|
||||
|
||||
```idris
|
||||
trackFloor : (start : Integer) -> (xs : List Char) -> Integer
|
||||
trackFloor start [] = start
|
||||
trackFloor start ('(' :: xs) = trackFloor (start + 1) xs
|
||||
trackFloor start (')' :: xs) = trackFloor (start - 1) xs
|
||||
trackFloor start (x :: xs) = trackFloor start xs
|
||||
```
|
||||
|
||||
This one is slightly more complicated, ultimately very similar to the above, but with two accumulators, one for the position in the input string in addition to the one for the current floor.
|
||||
|
||||
```idris
|
||||
findBasement : (position : Nat) -> (currentFloor : Integer) -> (xs : List Char)
|
||||
-> Nat
|
||||
findBasement position currentFloor [] = position
|
||||
|
@ -23,18 +40,37 @@ findBasement position currentFloor (')' :: xs) =
|
|||
else findBasement (position + 1) (currentFloor - 1) xs
|
||||
findBasement position currentFloor (x :: xs) =
|
||||
findBasement (position + 1) currentFloor xs
|
||||
```
|
||||
|
||||
## Part Functions
|
||||
|
||||
Both this parts are simple application of one of our solver functions
|
||||
|
||||
### Part 1
|
||||
|
||||
Very uneventful, the only thing novel here is pulling the input out of the `ReaderL "input" String`, which will become very boring very quickly.
|
||||
|
||||
```idris
|
||||
part1 : Eff (PartEff String) (Integer, ())
|
||||
part1 = do
|
||||
input <- map unpack $ askAt "input"
|
||||
let output = trackFloor 0 input
|
||||
pure (output, ())
|
||||
```
|
||||
|
||||
### Part 2
|
||||
|
||||
We have to be careful to start the position accumulator at 1, since the problem specifies that the input string is 1-index.
|
||||
|
||||
```idris
|
||||
part2 : () -> Eff (PartEff String) Nat
|
||||
part2 x = do
|
||||
input <- map unpack $ askAt "input"
|
||||
pure $ findBasement 1 0 input
|
||||
```
|
||||
|
||||
<!-- idris
|
||||
public export
|
||||
day1 : Day
|
||||
day1 = Both 1 part1 part2
|
||||
-->
|
|
@ -1,60 +0,0 @@
|
|||
module Years.Y2015.Day2
|
||||
|
||||
import Data.List
|
||||
import Data.List1
|
||||
import Data.String
|
||||
|
||||
import Control.Eff
|
||||
|
||||
import Runner
|
||||
|
||||
%default total
|
||||
|
||||
record Box where
|
||||
constructor MkBox
|
||||
length, width, height : Integer
|
||||
|
||||
(.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
|
||||
|
||||
totalRibbon : Box -> Integer
|
||||
totalRibbon x = x.ribbon + x.bow
|
||||
|
||||
paperNeeded : Box -> Integer
|
||||
paperNeeded x = x.area + x.slack
|
||||
|
||||
parseBox : Has (Except String) fs =>
|
||||
String -> Eff fs Box
|
||||
parseBox str = do
|
||||
l ::: [w, h] <- pure $ split (== 'x') str
|
||||
| xs => throw "Box did not have exactly 3 components: \{show xs}"
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
part2 : (boxes : List Box) -> Eff (PartEff String) Integer
|
||||
part2 boxes = pure . sum . map totalRibbon $ boxes
|
||||
|
||||
public export
|
||||
day2 : Day
|
||||
day2 = Both 2 part1 part2
|
126
src/Years/Y2015/Day2.md
Normal file
126
src/Years/Y2015/Day2.md
Normal file
|
@ -0,0 +1,126 @@
|
|||
# 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
|
||||
-->
|
Loading…
Add table
Add a link
Reference in a new issue