我如何使用MVars在我的乒乓haskell游戏中移动球拍?

时间:2019-06-24 18:38:03

标签: multithreading haskell concurrency io-monad program-transformation

我已经具有在Haskell的乒乓球游戏中移动2个桨的功能。我要更改,因此现在使用MVars。

我知道我需要将wHeld,sHeld,downHeld和upHeld更改为MVars,但是关于如何更改movePaddle以处理MVars的任何想法?

另外,当我声明将MVars举为节目时,也会显示错误((Show MVar Bool的非实例)

None
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 

1 个答案:

答案 0 :(得分:3)

MVars的工作方式是类型MVar Bool的值是一个不透明的“令牌”,它引用了Bool的存储位置。您可以创建此类令牌,​​并使用IO操作读取和修改其关联存储位置的内容。

默认情况下,令牌本身(MVar Bool值)没有Show实例。为了进行调试,您可以添加一个:

instance Show (MVar a) where show _ = "<MVar>"

这将允许您派生Show的{​​{1}}实例而不会收到错误消息。但是,PongGame实例在没有严重的IO滥用的情况下无法显示MVars中存储的值,因此您可能需要考虑编写一个功能漂亮的Show函数具有所有MVar值的当前游戏状态,可让您完全跳过dumpGame :: PongGame -> IO ()实例。

无论如何,要重写程序以在键Show字段中使用MVar:

PongGame

您将要重写data PongGame = Game { ... , wHeld :: MVar Bool -- segura o w , sHeld :: MVar Bool -- segura os , downHeld :: MVar Bool -- segura down , upHeld :: MVar Bool -- segura para cima ... } 及其子功能以在IO monad中运行:

movePaddle

movePaddle :: PongGame -> IO PongGame moveLeftPaddle :: PongGame -> IO PongGame moveRightPaddle :: PongGame -> IO PongGame 中,您可以将movePaddle中的.替换为<=<的{​​{1}}运算符,这等效于函数组合的单子形式:

Control.Monad

这基本上是以下简称:

movePaddle = moveLeftPaddle <=< moveRightPaddle

然后,您需要通过执行IO操作来重写movePaddle game = do game1 <- moveRightPaddle game game2 <- moveLeftPaddle game1 return game2 moveLeftPaddle来访问MVar的内容:

moveRightPaddle

moveLeftPaddle game = do up <- readMVar (wHeld game) dn <- readMVar (sHeld game) case (up, dn) of (True, False) -> return $ game {playerLPos = paddleUp (playerLPos game)} (False, True) -> return $ game {playerLPos = paddleDn (playerLPos game)} _ -> return game 的定义类似。

要清楚,函数调用moveRightPaddle是一个简单的纯函数调用,它检索与wHeld game字段关联的类型MVar Bool令牌。然后,我们执行IO操作wHeld以实际获取readMVar <this_token>的值,然后可以在Bool语句中对其进行操作以更新游戏状态。

在程序的其他地方,您将需要一个case例程来创建这些MVar:

setup

您可能会有一些线程正在运行类似这样的函数:

initGame :: IO PongGame
initGame = do
  ...
  wHeld <- newMVar False
  sHeld <- newMVar False
  ...
  return $ Game ... wHeld sHeld ...