Today’s puzzle is one of those typical “same instructions, differing interpretations” problems AoC is so good at. This post is a literate Haskell program, let’s get the imports out of the way.
import Data.Complex
import Data.List
Parsing is dead easy. Assuming line-based instructions, I just pass
the first character as-is and convert the number to a
Double
.
parseInstruction :: String -> (Char,Double)
:n) = (i,read n) parseInstruction (i
Why a double? I’m going to use Complex
numbers, and in
Haskell they only have a useful Num
instance if the
underlying type implements RealFloat
. So
Double
it is, even if the coordinates are only expected to
take integer values.
type C = Complex Double
In part 1, NESW
mean to move the ship by the specified
distance, whereas the LRF
series implement “turtle
graphics”. So my step function maintains the ship’s position and
direction.
go1 :: (C,C) -> (Char,Double) -> (C,C)
= case i of
go1 (pos,hdg) (i,n) 'N' -> (pos + (0 :+ n) ,hdg)
'S' -> (pos - (0 :+ n) ,hdg)
'W' -> (pos - (n :+ 0) ,hdg)
'E' -> (pos + (n :+ 0) ,hdg)
'F' -> (pos + (n :+ 0)*hdg,hdg)
'L' -> (pos ,hdg * (0 :+ 1 )^a)
'R' -> (pos ,hdg * (0 :+ (-1))^a)
where a | round n `mod` 90 == 0 = round n `div` 90
In part 2, a “waypoint” is introduced. Its use looks more like like a
speed vector than a
navigational
waypoint, but let’s play along. Here the only way to move is the
F
instruction; NESW
adjust the vector by
addition, LR
by rotation.
go2 :: (C,C) -> (Char,Double) -> (C,C)
= case i of
go2 (pos,wpt) (i,n) 'N' -> (pos ,wpt + (0 :+ n))
'S' -> (pos ,wpt - (0 :+ n))
'W' -> (pos ,wpt - (n :+ 0))
'E' -> (pos ,wpt + (n :+ 0))
'L' -> (pos ,wpt * (0 :+ 1 )^a)
'R' -> (pos ,wpt * (0 :+ (-1))^a)
'F' -> (pos + (n :+ 0)*wpt,wpt)
where a | round n `mod` 90 == 0 = round n `div` 90
All that’s left is to fold the instructions, pairing the appropriate
go
function with its starting vector.
main :: IO ()
= do
main <- map parseInstruction . lines <$> readFile "day12.in"
instrs let (destination1,_) = foldl' go1 (0 :+ 0, 1 :+ 0) instrs
print $ round (dist destination1)
let (destination2,_) = foldl' go2 (0 :+ 0,10 :+ 1) instrs
print $ round (dist destination2)
And measuring the distance to the origin by Manhattan distance, because for some reason that’s what makes sense during a storm.
dist :: C -> Double
:+ y) = abs x + abs y dist (x
This concludes today’s solution. See you soon!