0

I'm trying to make a very simple snake-like game where, if you try to go to a x,y coordinate you have already visited, you lose the game.

This is code that's working so far (you can move player 1 with arrowkeys and player 2 with wasd):

import UI.NCurses

main :: IO ()    
main = runCurses $ do
    w <- defaultWindow
    updateWindow w $ do
        drawBorder Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
    render
    loop w [] 1 1 0 0 10 10 0 0

loop :: Window -> [(Integer, Integer)] -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer ->  Curses ()
loop w list p1x p1y p1oldDx p1oldDy p2x p2y p2oldDx p2oldDy = do
    e <- getEvent w (Just 100)

    let coordList = updateXY e p1oldDx p1oldDy p2oldDx p2oldDy
    let p1dX = coordList !! 0
    let p1dY = coordList !! 1
    let p2dX = coordList !! 2
    let p2dY = coordList !! 3

    updateWindow w $ do
        moveCursor (p1y+p1dY) (p1x+p1dX)
        drawString ("#")
        moveCursor (p2y+p2dY) (p2x+p2dX)
        drawString ("#")
    render

    let updatedList = list ++ [(p1y+p1dY, p1x+p1dX)] ++ [(p2y+p2dY, p2x+p2dX)]

    loop w updatedList (p1x+p1dX) (p1y+p1dY) p1dX p1dY (p2x+p2dX) (p2y+p2dY) p2dX p2dY

updateXY :: Maybe Event -> Integer -> Integer  -> Integer -> Integer -> [Integer]
updateXY e p1oldX p1oldY p2oldX p2oldY
    | e == Just (EventSpecialKey KeyLeftArrow) = [-1, 0, p2oldX, p2oldY]
    | e == Just (EventSpecialKey KeyRightArrow) = [1, 0, p2oldX, p2oldY]
    | e == Just (EventSpecialKey KeyDownArrow) = [0, 1, p2oldX, p2oldY]
    | e == Just (EventSpecialKey KeyUpArrow) = [0, -1, p2oldX, p2oldY]
    | e == Just (EventCharacter 'a') = [p1oldX, p1oldY, -1, 0]
    | e == Just (EventCharacter 'd') = [p1oldX, p1oldY, 1, 0]
    | e == Just (EventCharacter 's') = [p1oldX, p1oldY, 0, 1]
    | e == Just (EventCharacter 'w') = [p1oldX, p1oldY, 0, -1]
    | p1oldX /= 0 = [p1oldX, 0, p2oldX, p2oldY]
    | p1oldY /= 0 = [0, p1oldY, p2oldX, p2oldY]
    | p2oldX /= 0 = [p1oldX, p1oldY, p2oldX, 0]
    | p2oldY /= 0 = [p1oldX, p1oldY, 0, p2oldY]
    | otherwise = [1, 0, 1, 0] -- Starts moving in x-direction. Change to (0,0) for stand-still


So as you move, "#" characters are put down. The above code does nothing if you go to a coordinate which already has a "#" on it, so I tried changing the loop function by adding this just before the let updatedList...:

if length (filter (==(p1x, p1y)) list) > 0 then gameOver w "player one lost"
if length (filter (==(p2x, p2y)) list) > 0 then gameOver w "player two lost"

And adding a temporary gameOver function:

gameOver w player = 
    updateWindow w $ do
        moveCursor (10) (10)
        drawString (player)
    render

But when I try to load this file in GHCI I get the following error message: parse error on input 'if'

Will Ness
  • 70,110
  • 9
  • 98
  • 181
abc556
  • 37
  • 5

2 Answers2

6

ifs need elses. Period. You can use the when function for when you need the imperative style "if this, then do that, else do nothing," but there is no built-in syntax:

import Control.Monad

when (length (filter (==(p1x, p1y)) list) > 0) $ gameOver w "player one lost"
when (length (filter (==(p2x, p2y)) list) > 0) $ gameOver w "player two lost"

The definition of when is

when cond action = if cond then action else pure ()
HTNW
  • 27,182
  • 1
  • 32
  • 60
  • 1
    but the loop will continue, won't it? (unless `gameOver` throws an exception, but that would be bad style). explicit `if` will force us to place `loop ...` into just one, appropriate clause. – Will Ness May 20 '19 at 15:58
4

In Haskell, if expressions must have both then and else clauses.

Without the else, you get the error.

Both the consequent and the alternative clause must have the same type.

Will Ness
  • 70,110
  • 9
  • 98
  • 181