 # Mars Rover Kata in Haskell

Posted on September 16, 2019 by Riccardo.

### Mars Rover Kata in Haskell (Series)

Mars Rover Kata in Haskell Refactoring the Mars Rover Kata in Haskell

Today we will TDD the Mars Rover Kata with the T in TDD meaning Type!

## Red

The first step is to define all the types we need for our program to do it’s job:

``````type Coord =
(Int, Int)

type CoordDir =
(Coord, Dir)

data Turn
= L
| R

data Dir
= N
| E
| S
| W

data Cmd
= Turn Turn
| Move

parsePlateauMax :: Text -> Maybe Coord

parseStartingCoordDir :: Text -> Maybe CoordDir

parseCommands :: Text -> Maybe [Cmd]

runCommands :: [Cmd] -> Coord -> CoordDir -> CoordDir

run :: Text -> Maybe CoordDir``````

Ideally we would use the functions above as follows:

``````main :: IO ()
main = do
print \$ parsePlateauMax "5 5"
-- Just (5,5)

print \$ parseStartingCoordDir "1 2 N"
-- Just ((1,2),N)

print \$ parseCommands "LMLMLMLMM"
-- Just [Turn L,Move,Turn L,Move,Turn L,Move,Turn L,Move,Move]

let commands = "5 5\n1 2 N\nLMLMLMLMM"
maybe (print "ERR!") print \$ run commands
-- ((1,3),N)``````

## Green

With the types in place it’s enough to make it compile :D

``````{-# LANGUAGE OverloadedStrings #-}

module Main where

import Data.Text (Text)
import qualified Data.Text as T (lines, words, unpack)

type Coord =
(Int, Int)

type CoordDir =
(Coord, Dir)

data Turn
= L
| R

data Dir
= N
| E
| S
| W

data Cmd
= Turn Turn
| Move
deriving (Show)

parsePlateauMax :: Text -> Maybe Coord
parsePlateauMax txt =
case T.words txt of
_      -> Nothing

parseStartingCoordDir :: Text -> Maybe CoordDir
parseStartingCoordDir txt = do
(x, y, d) <- case T.words txt of
_      -> Nothing
Just ((x, y), d)

parseCommands :: Text -> Maybe [Cmd]
parseCommands txt = toCmd `traverse` T.unpack txt
where
toCmd :: Char -> Maybe Cmd
toCmd 'L' = Just (Turn L)
toCmd 'R' = Just (Turn R)
toCmd 'M' = Just Move
toCmd _   = Nothing

runCommands :: [Cmd] -> Coord -> CoordDir -> CoordDir
runCommands []       _   coordDir             = coordDir
runCommands (c:cmds) max coordDir = runCommands cmds max \$ newCoordDir c
where
newCoordDir (Turn L) = turnL coordDir
newCoordDir (Turn R) = turnR coordDir
newCoordDir Move     = move coordDir

turnL (c, N) = (c, W)
turnL (c, E) = (c, N)
turnL (c, S) = (c, E)
turnL (c, W) = (c, S)

turnR (c, N) = (c, E)
turnR (c, E) = (c, S)
turnR (c, S) = (c, W)
turnR (c, W) = (c, N)

move ((x, y), N) = (wrap max (x, y+1), N)
move ((x, y), E) = (wrap max (x+1, y), E)
move ((x, y), S) = (wrap max (x, y-1), S)
move ((x, y), W) = (wrap max (x-1, y), W)

wrap (maxX, maxY) (x, y) = (wrap' maxX x, wrap' maxY y)

wrap' max x | x < 0       = max + 1 + x
wrap' max x | x > max     = x `rem` max
wrap' max x | otherwise   = x

run :: Text -> Maybe CoordDir
run txt = do
(max, coordDir, cmds) <- go \$ T.lines txt
Just \$ runCommands cmds max coordDir
where
go [f, s, t] = (,,) <\$> parsePlateauMax f <*> parseStartingCoordDir s <*> parseCommands t
go _         = Nothing

main :: IO ()
main = do
let commands = "5 5\n1 2 N\nLMLMLMLMM"
print \$ parsePlateauMax "5 5"
print \$ parseStartingCoordDir "1 2 N"
print \$ parseCommands "LMLMLMLMM"
maybe (print "ERR!") print \$ run commands``````

## Refactor

I don’t have any more time for today. We will be refactoring in a future post!