I already have a function that moves 2 paddles in a ping pong game in haskell. I want to change so it uses MVars now.
I know that i need to change wHeld, sHeld, downHeld and upHeld to MVars but any ideas on how to change movePaddle to deal with MVars?
Also when i declare wHeld an MVars it shows a error on deriving show (Non instance for (Show MVar Bool))
data PongGame = Game
{ ballLoc :: (Float, Float) -- ^ Pong ball (x, y) location.
, ballVel :: (Float, Float) -- ^ Pong ball (x, y) velocity.
, player1 :: Float -- ^ Left player paddle height.
-- Zero is the middle of the screen.
, player2 :: Float -- ^ Right player paddle height.
, playerRPos :: Float -- posicao do player right
, playerLPos :: Float --- posicao do player left
, ghci :: Bool -- pausar
, showMenu :: Bool -- mostrar o menu
, wHeld :: MVar Bool -- segura o w
, sHeld :: Bool -- segura os
, downHeld :: Bool -- segura down
, upHeld :: Bool -- segura para cima
, playerLScore :: Int -- score do jogador left
, playerRScore :: Int -- score do jogador right
, paused :: Bool
} deriving Show
movePaddle :: PongGame -> PongGame
movePaddle = moveLeftPaddle . moveRightPaddle
moveLeftPaddle game
| (wHeld game) = game {playerLPos = paddleUp (playerLPos game)}
| (sHeld game) = game {playerLPos = paddleDn (playerLPos game)}
| otherwise = game
moveRightPaddle game
| (upHeld game) = game {playerRPos = paddleUp (playerRPos game)}
| (downHeld game) = game {playerRPos = paddleDn (playerRPos game)}
| otherwise = game
paddleUp pos = min (pos + 10) paddleMax
paddleDn pos = max (pos - 10) paddleMin
The way MVars work is that a value of type
MVar Bool
is an opaque "token" referencing a storage location for aBool
. You create such a token and read and modify the contents of its associated storage location using IO actions.The token itself (the
MVar Bool
value) has noShow
instance by default. For debugging purposes, you could add one:This will allow you to derive a
Show
instance forPongGame
without getting an error message. However, the values stored in theMVars
can't be displayed by theShow
instance without some egregious IO abuse, so you might want to consider writing a functiondumpGame :: PongGame -> IO ()
that pretty-prints the current game state with all the MVar values, letting you skip theShow
instance entirely.Anyway, to rewrite your program to use MVars in key
PongGame
fields:you'll want to rewrite your
movePaddle
and its subfunctions to run in the IO monad:In
movePaddle
, you can replace the.
operator with<=<
fromControl.Monad
, which is the monadic equivalent of function composition:This is basically shorthand for:
Then, you'll need to rewrite
moveLeftPaddle
andmoveRightPaddle
to access the contents of the MVars by executing IO actions:with
moveRightPaddle
defined similarly.To be clear, the function call
wHeld game
is a simple, pure function call that retrieves the token of typeMVar Bool
associated with thewHeld
field. We then execute the IO actionreadMVar <this_token>
to actually retrieve theBool
value, which we can then act on in acase
statement to update the game state.Elsewhere in your program, you'll need a
setup
routine that creates these MVars:and you'll presumably have some thread that's running a function like: