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 Boolis 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 Boolvalue) has noShowinstance by default. For debugging purposes, you could add one:This will allow you to derive a
Showinstance forPongGamewithout getting an error message. However, the values stored in theMVarscan't be displayed by theShowinstance 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 theShowinstance entirely.Anyway, to rewrite your program to use MVars in key
PongGamefields:you'll want to rewrite your
movePaddleand 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
moveLeftPaddleandmoveRightPaddleto access the contents of the MVars by executing IO actions:with
moveRightPaddledefined similarly.To be clear, the function call
wHeld gameis a simple, pure function call that retrieves the token of typeMVar Boolassociated with thewHeldfield. We then execute the IO actionreadMVar <this_token>to actually retrieve theBoolvalue, which we can then act on in acasestatement to update the game state.Elsewhere in your program, you'll need a
setuproutine that creates these MVars:and you'll presumably have some thread that's running a function like: