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
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
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!