Reputation: 11
I need to do a project in Haskell, which does a snake program, but I'm stuck the last part.
These two test case should give True
, but I dont have an idea how should I do that:
doInstruction Move (North, [(5, 7), (5, 6)], 2) == (North, [(5, 8), (5, 7)], 2)
doInstruction Move (East, [(2, 1), (1, 1), (1, 0)], 3) == (East, [(3, 1), (2, 1), (1, 1)], 3)
My code until that part
data Dir = West | North | East | South deriving (Eq, Show)
type Position = (Int, Int)
type Snake = (Dir, [Position], Int)
data Instruction = Move | Turn Dir deriving (Eq, Show)
isOppositeDir :: Dir -> Dir -> Bool
isOppositeDir West East = True
isOppositeDir East West = True
isOppositeDir North South = True
isOppositeDir South North = True
isOppositeDir _ _ = False
oppositeDir :: Dir -> Dir
oppositeDir West = East
oppositeDir East = West
oppositeDir North = South
oppositeDir South = North
nextPos :: Dir -> Position -> Position
nextPos x (a,b)
| x == West = (a - 1 , b)
| x == East = (a + 1 , b)
| x == North = (a , b + 1)
| x == South = (a , b - 1)
| otherwise = (a , b)
doInstruction :: Instruction -> Snake -> Snake
doInstruction (Turn dir) (x, p, y) = if isOppositeDir dir x then (x,p,y) else (dir,p,y)
Upvotes: 1
Views: 104
Reputation: 233307
The following is in no way a complete implementation of doInstruction
, but should enable you to move forward. It passes your two test cases.
Okay, here's a partial implementation of doInstruction
:
doInstruction :: Instruction -> Snake -> Snake
doInstruction Move (currDir, p@(x:_), l) = (currDir, take l $ nextPos currDir x : p, l)
If you load it into GHCi, it'll pass the two test cases from the OP:
> doInstruction Move (North, [(5, 7), (5, 6)], 2) == (North, [(5, 8), (5, 7)], 2)
True
> doInstruction Move (East, [(2, 1), (1, 1), (1, 0)], 3) == (East, [(3, 1), (2, 1), (1, 1)], 3)
True
It'll still crash on lots of other input because it doesn't handle e.g. Turn
instructions.
doInstruction Move
handles the Move
instruction exclusively. (currentDir, p@(x:_), l)
pattern-matches the snake's current direction, it's positions, and it's length (I'm assuming that the last integer is the length).
The p@
syntax captures the entire position into the variable p
. The function also needs the head of the list, so it also uses pattern matching on the list. Here, it matches only the head and ignores the tail: (x:_)
. The underscore is the wildcard character that ignores the match, in this case the tail.
This pattern match is also partial, because it's not going to match an empty list. You probably don't allow snakes of zero length, but if you did, this wouldn't match.
The function uses nextPos
to calculate the next position of the head of the snake. It conses (uses :
) the resulting Position
value onto the p
list. This in itself creates a list that's too long, but then the function uses take l
to take only the first l
elements of that list.
You should add more cases to the doInstruction
function, e.g.
doInstruction :: Instruction -> Snake -> Snake
doInstruction Move (currDir, p@(x:_), l) = (currDir, take l $ nextPos currDir x : p, l)
doInstruction (Turn dir) snake = -- More code goes here...
Upvotes: 1