Year 2015 Day 3 Part 2

This commit is contained in:
Nathan McCarty 2025-01-06 14:32:48 -05:00
parent 30917830df
commit fdfa15daf8

View file

@ -1,5 +1,7 @@
# Day 3
This day provides a gentle introduction to `mutual` blocks and mutually recursive functions.
<!-- idris
module Years.Y2015.Day3
@ -10,10 +12,15 @@ import Runner
```idris
import Data.SortedSet
import Data.String
import Util
```
<!-- idris
%default total
-->
## Parsing and data structures
We'll do parsing a little more properly this time, turning the input into a list of movement commands
@ -61,7 +68,35 @@ visitedLocations xs = visitor xs empty (0, 0)
-> (location : (Integer, Integer)) -> SortedSet (Integer, Integer)
visitor [] set location = insert location set
visitor (x :: xs) set location =
visitor xs (insert location set) (location >+< (vector x))
visitor xs (insert location set) (location >+< vector x)
```
### Robo Santa
This one gets a bit more interesting, we'll adopt the same tail recursive approach, but instead use a `mutual` block and two mutually recursive functions to handle the alternation between santa and robo santa. The `visitSanta` function will pass control to `visitRobo` after executing its movement, and vise versa.
We'll want to insert both present deliverer's locations in the recursive base case, this may result in a duplicate location, but that's okay because `SortedSet` will only hold at most one of each item inserted into it.
In idris, there is a general requirement that values be defined before their use, a common feature of dependently typed languages, resulting from the fact that just having the type signature of a function/value alone is not always enough to perform type checking, as functions can appear as part of types, requiring evaluation of the function and making automatic dependency analysis effectively impossible.
Inside a `mutual` block, elaboration behaves differently, elaborating types first and then values in separate passes. This restricts what you can do a little, but enables mutually recursive functions.
```idris
visitedLocations' : List Movement -> SortedSet (Integer, Integer)
visitedLocations' xs = visitSanta xs empty (0, 0) (0, 0)
where
mutual
visitSanta : (moves : List Movement) -> (set : SortedSet (Integer, Integer))
-> (santa, robo : (Integer, Integer)) -> SortedSet (Integer, Integer)
visitSanta [] set santa robo = insert santa . insert robo $ set
visitSanta (x :: xs) set santa robo =
visitRobo xs (insert santa set) (santa >+< vector x) robo
visitRobo : (moves : List Movement) -> (set : SortedSet (Integer, Integer))
-> (santa, robo : (Integer, Integer)) -> SortedSet (Integer, Integer)
visitRobo [] set santa robo = insert santa . insert robo $ set
visitRobo (x :: xs) set santa robo =
visitSanta xs (insert robo set) santa (robo >+< vector x)
```
## Part Functions
@ -73,14 +108,25 @@ Similar to the previous day, we get our input, unpack it, and traverse our effec
```idris
part1 : Eff (PartEff String) (Nat, List Movement)
part1 = do
input <- map unpack $ askAt "input"
input <- map (unpack . trim) $ askAt "input"
movements <- traverse parseMovement input
let locations = visitedLocations movements
pure (length locations, movements)
```
### Part 2
Same as Part 1, but with a different solving function
```idris
part2 : (movements : List Movement) -> Eff (PartEff String) Nat
part2 movements = do
let locations = visitedLocations' movements
pure . length $ locations
```
<!-- idris
public export
day3 : Day
day3 = First 3 part1
day3 = Both 3 part1 part2
-->