Hassan Ali
Hassan Ali

Reputation: 149

Haskell - How do I iterate and compare?

I have an issue. I am currently learning Haskell, and trying to build a dungeon game.

Currently I am trying to implement a movement function for the player. Before moving to a new position,I must check if the player's new position will collide with the position of an object on the map of the game.

The code I have in my head (in Java/C) is fairly simple, yet I cannot think how it would cross over to Haskell. I'm pretty sure there's an alternative approach using Haskell code but this is the rough idea (Java/C):-

Let's assume the player object has x and y coordinates. Let's also assume we store a list of other objects that the player may collide with in an array (this would be stored in a list for Haskell, I assume). Also, lets assume each object has an x and y co-ordinate. The Boolean function returns true if a collision will occur, or false if not.

   Boolean detectCollision(Player p, Object[] o)
   {
       for(int i=0; i < o.length; i++){
           if(p.x==o[i].x && p.y==o[i].y){
               return true;
           }
       } return false; 
   }

If someone could help me figure this out I'd be very grateful.

Upvotes: 3

Views: 668

Answers (1)

bheklilr
bheklilr

Reputation: 54058

Even if this were Java/C, I would recommend that you write a function that detects if a player and a single object would collide, so let's do that here:

collidesWith :: Player -> Object -> Bool
collidesWith player object =
    playerX player == objectX object && playerY player == objectY object

This is a little wordy, you can shorten this using the lens library to actually make it look like

collidesWith player object
    = player^.x == object^.x && player^.y == object^.y

But this is outside the scope of this question, just know that it's fully possible in Haskell.

For "looping" over a list in Haskell you can use recursion

detectCollision player [] = False  -- No objects, no collisions
detectCollision player (o:objects)
    = player `collidesWith` o || detectCollision player objects

Since Haskell is lazy, the first time player `collidesWith` o evaluates to True it will stop checking. But this actually already exists as a standard built in function named any:

any :: (a -> Bool) -> [a] -> Bool

And can be used as

detectCollision player objects = any (collidesWith player) objects

Haskell even lets you drop the objects argument through eta reduction, so it can be written simply as

detectCollision :: Player -> [Object] -> Bool
detectCollision player = any (collidesWith player)

And that's it!

Note: This is assuming that Player and Object are defined as

data Player = Player
    { playerX :: Int
    , playerY :: Int
    } deriving (Eq, Show)

data Object = Object
    { objectX :: Int
    , objectY :: Int
    } deriving (Eq, Show)

Upvotes: 7

Related Questions