Advent of Code 2021 day 2

The main questions for today's task are how to represent what's important. There are two things: the commands given to the submarine, and the current position. I decided to go with clarity over cleverness.

I could represent the position as a pair (or triple) of numbers. I could do something clever with complex numbers. I could use the Linear package to represent coordinates. I could use records. But in this case, some simple algebraic types are more than good enough.

type Course = [Command]
data Command =   Forward Int
               | Up Int
               | Down Int
               deriving (Eq, Show)

data Position = Position Int Int -- forward, depth
  deriving (Eq, Show)               

data AimedPosition = AimedPosition Int Int Int -- forward, depth, aim
  deriving (Eq, Show)   

This has the additional advantage from the "parse, don't validate" approach, in that I can't have an invalid Command.

It's back to attoparsec for parsing, which remains remarkably clear.

courseP = commandP `sepBy` endOfLine
commandP = forwardP <|> upP <|> downP

forwardP = Forward <$> ("forward " *> decimal)
upP = Up <$> ("up " *> decimal)
downP = Down <$> ("down " *> decimal)

A course is a list of commands separated by newlines. A command is either forward, up, or down. Each of those is the text string followed by a number, and we don't bother recording the text string.

Finding the final position requires writing down the rules as a function (using pattern matching for the different cases), then folding the commands into a final Position.

followCourse :: Course -> Position
followCourse = foldl courseStep (Position 0 0)

courseStep :: Position -> Command -> Position
courseStep (Position h d) (Forward n) = Position (h + n) d
courseStep (Position h d) (Up n)      = Position h (d - n)
courseStep (Position h d) (Down n)    = Position h (d + n)

followAimedCourse :: Course -> AimedPosition
followAimedCourse = foldl courseAimedStep (AimedPosition 0 0 0)

courseAimedStep :: AimedPosition -> Command -> AimedPosition
courseAimedStep (AimedPosition h d a) (Forward n) = AimedPosition (h + n) (d + a * n) a
courseAimedStep (AimedPosition h d a) (Up n)      = AimedPosition h d (a - n)
courseAimedStep (AimedPosition h d a) (Down n)    = AimedPosition h d (a + n)

The only wrinkle is to remember to fold from the left, so commands are applied from first to last!

Code

You can get the code from my locally-hosed Git repo, or from Gitlab.