Year 2015 Day 14 Part 1
This commit is contained in:
parent
f675677a76
commit
5aa490acae
5 changed files with 150 additions and 1 deletions
|
@ -136,6 +136,10 @@ solution.
|
|||
|
||||
Naive ring buffer and `parameters` blocks[^2]
|
||||
|
||||
- [Day 14](src/Years/Y2015/Day14.md)
|
||||
|
||||
Introduction to streams, infinite collections of data
|
||||
|
||||
## References
|
||||
|
||||
[^1]: Idris 2 Manual:
|
||||
|
|
|
@ -41,7 +41,7 @@ main = Main
|
|||
|
||||
-- name of executable
|
||||
executable = "advent"
|
||||
-- opts =
|
||||
opts = "--directive lazy=weakMemo"
|
||||
sourcedir = "src"
|
||||
-- builddir =
|
||||
-- outputdir =
|
||||
|
|
|
@ -35,3 +35,4 @@
|
|||
- [Day 11 - Refinement Types](Years/Y2015/Day11.md)
|
||||
- [Day 12 - Custom Parser Effect and DLists](Years/Y2015/Day12.md)
|
||||
- [Day 13 - Naive Ring Buffer and parameters blocks](Years/Y2015/Day13.md)
|
||||
- [Day 14 - Introduction to Streams](Years/Y2015/Day14.md)
|
||||
|
|
|
@ -20,6 +20,7 @@ import Years.Y2015.Day10
|
|||
import Years.Y2015.Day11
|
||||
import Years.Y2015.Day12
|
||||
import Years.Y2015.Day13
|
||||
import Years.Y2015.Day14
|
||||
```
|
||||
|
||||
# Days
|
||||
|
@ -108,6 +109,12 @@ y2015 = MkYear 2015 [
|
|||
, day13
|
||||
```
|
||||
|
||||
## [Day 14](Y2015/Day14.md)
|
||||
|
||||
```idris
|
||||
, day14
|
||||
```
|
||||
|
||||
```idris
|
||||
]
|
||||
```
|
||||
|
|
137
src/Years/Y2015/Day14.md
Normal file
137
src/Years/Y2015/Day14.md
Normal file
|
@ -0,0 +1,137 @@
|
|||
# [Year 2015 Day 14](https://adventofcode.com/2015/day/14)
|
||||
|
||||
This day provides us an introduction to streams, infinite, lazily generated,
|
||||
collections of data.
|
||||
|
||||
```idris hide
|
||||
module Years.Y2015.Day14
|
||||
|
||||
import Data.Primitives.Interpolation
|
||||
|
||||
import Control.Eff
|
||||
|
||||
import Runner
|
||||
```
|
||||
|
||||
```idris
|
||||
import Data.String
|
||||
import Data.List1
|
||||
import Data.Vect
|
||||
import Data.Stream
|
||||
import Decidable.Equality
|
||||
|
||||
import Util
|
||||
```
|
||||
|
||||
## Parsing And Datastructures
|
||||
|
||||
Collect the aspects defining a reindeer into a record
|
||||
|
||||
```idris
|
||||
record Reindeer where
|
||||
constructor MkReindeer
|
||||
name : String
|
||||
speed : Nat
|
||||
duration, rest : Nat
|
||||
```
|
||||
|
||||
```idris hide
|
||||
Show Reindeer where
|
||||
show (MkReindeer name speed duration rest) =
|
||||
"MkReindeer \{name} \{speed} \{duration} \{rest}"
|
||||
```
|
||||
|
||||
This time around, since the lines describing a reindeer contain a lot of cruft,
|
||||
we'll handle the parsing by converting the input, after splitting it on space
|
||||
characters, to a `Vect`, and indexing into that `Vect`.
|
||||
|
||||
```idris
|
||||
parseReindeer : Has (Except String) fs =>
|
||||
(input : String) -> Eff fs Reindeer
|
||||
parseReindeer input = do
|
||||
parts <- note "Input has wrong size: \{input}" $
|
||||
toVect 15 . forget . split (== ' ') . trim $ input
|
||||
let name = index 0 parts
|
||||
speed <- note "" $
|
||||
parsePositive $ index 3 parts
|
||||
duration <- note "" $
|
||||
parsePositive $ index 6 parts
|
||||
rest <- note "" $
|
||||
parsePositive $ index 13 parts
|
||||
pure $ MkReindeer name speed duration rest
|
||||
```
|
||||
|
||||
### Solver Functions
|
||||
|
||||
A stream is an infinite analog of a list, storing an infinite collection of
|
||||
(lazily generated) values.
|
||||
|
||||
Streams are defined like:
|
||||
|
||||
```idris
|
||||
data Stream' : Type -> Type where
|
||||
(::) : a -> Inf (Stream' a) -> Stream' a
|
||||
```
|
||||
|
||||
Streams are a member of a family of concepts analogous to iterators in
|
||||
imperative languages, the different flavors of colist.
|
||||
|
||||
Colists are the codata duals of lists, we'll dig more into to this later, but to
|
||||
provide a high level summary, where data is defined by how it is constructed,
|
||||
codata is defined by how it is destructed. While a list is defined by how you
|
||||
can cons an element `x` onto a list `xs` to produce a new list `x :: xs`, a
|
||||
colist is defined by how you can break down a colist `x :: xs` into a head `x`
|
||||
and a tail `xs`.
|
||||
|
||||
Streams are a particular type of colist that has no empty case, breaking down a
|
||||
`Stream` will always produce an element and another stream, resulting in streams
|
||||
always being infinite in length.
|
||||
|
||||
Destructing a `Stream` by pattern matching is semantically equivalent to calling
|
||||
the `next` method on an iterator in a language like rust, it produces the
|
||||
element at the head of a stream, and a new stream producing future elements.
|
||||
|
||||
We will model are reindeer's future history of locations as a stream, with each
|
||||
element being the position at the time given by the index into the stream,
|
||||
generating it with a pair of mutually recursive functions. The `run` function
|
||||
adds the speed to current position to produce the next one, and the `rest`
|
||||
function doesn't modify the position whill still consuming a time step.
|
||||
|
||||
```idris
|
||||
distances : Reindeer -> Stream Nat
|
||||
distances x = run x x.duration 0
|
||||
where
|
||||
run : (deer : Reindeer) -> (left : Nat) -> (position : Nat)
|
||||
-> Stream Nat
|
||||
rest : (deer : Reindeer) -> (left : Nat) -> (position : Nat)
|
||||
-> Stream Nat
|
||||
run deer 0 position = rest deer deer.rest position
|
||||
run deer (S k) position = position :: run deer k (position + deer.speed)
|
||||
rest deer 0 position = run deer deer.duration position
|
||||
rest deer (S k) position = position :: rest deer k position
|
||||
```
|
||||
|
||||
## Part Functions
|
||||
|
||||
### Part 1
|
||||
|
||||
Parse the input, generate the position `Stream`s for each reindeer, then index
|
||||
the finish position in each stream.
|
||||
|
||||
```idris
|
||||
part1 : Eff (PartEff String) (Nat, ())
|
||||
part1 = do
|
||||
lines <- map (lines) $ askAt "input"
|
||||
reindeer <- traverse parseReindeer lines
|
||||
debug $ show reindeer
|
||||
let dists = map distances reindeer
|
||||
let dists_end = map (index 2503) dists
|
||||
let max = maxBy compare 0 dists_end
|
||||
pure (max, ())
|
||||
```
|
||||
|
||||
```idris hide
|
||||
public export
|
||||
day14 : Day
|
||||
day14 = First 14 part1
|
||||
```
|
Loading…
Add table
Reference in a new issue