multithreading - 我如何使用 MVars 在我的 pingpong haskell 游戏中移动球拍?
问题描述
我已经有一个在haskell的乒乓球比赛中移动2个球拍的功能。我想改变,所以它现在使用 MVars。
我知道我需要将 wHeld、sHeld、downHeld 和 upHeld 更改为 MVar,但是关于如何更改 movePaddle 以处理 MVar 的任何想法?
此外,当我声明 wHeld 一个 MVars 时,它在派生节目时显示错误(非实例(显示 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
解决方案
MVar 的工作方式是 type 的值MVar Bool
是一个不透明的“令牌”,它引用Bool
. 您创建这样的令牌并使用 IO 操作读取和修改其关联存储位置的内容。
默认情况下,令牌本身(MVar Bool
值)没有Show
实例。出于调试目的,您可以添加一个:
instance Show (MVar a) where show _ = "<MVar>"
这将允许您派生Show
实例而PongGame
不会收到错误消息。但是,如果没有一些严重的 IO 滥用,实例MVars
无法显示存储在 中的值Show
,因此您可能需要考虑编写一个函数dumpGame :: PongGame -> IO ()
,使用所有 MVar 值漂亮地打印当前游戏状态,让您Show
完全跳过实例.
无论如何,要重写您的程序以在关键字PongGame
段中使用 MVar:
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
...
}
您需要重写您的movePaddle
及其子函数以在 IO monad 中运行:
movePaddle :: PongGame -> IO PongGame
moveLeftPaddle :: PongGame -> IO PongGame
moveRightPaddle :: PongGame -> IO PongGame
在movePaddle
中,您可以将.
运算符替换为<=<
from Control.Monad
,它是函数组合的一元等效项:
movePaddle = moveLeftPaddle <=< moveRightPaddle
这基本上是以下的简写:
movePaddle game = do
game1 <- moveRightPaddle game
game2 <- moveLeftPaddle game1
return game2
然后,您需要通过执行 IO 操作来重写moveLeftPaddle
和moveRightPaddle
访问 MVar 的内容:
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
是一个简单的纯函数调用,它检索与字段关联的类型的标记。然后我们执行 IO 操作以实际检索值,然后我们可以在语句中对其进行操作以更新游戏状态。MVar Bool
wHeld
readMVar <this_token>
Bool
case
在您的程序的其他地方,您将需要一个setup
创建这些 MVar 的例程:
initGame :: IO PongGame
initGame = do
...
wHeld <- newMVar False
sHeld <- newMVar False
...
return $ Game ... wHeld sHeld ...
并且您可能会有一些线程正在运行以下函数:
processKeyEvent :: KeyEvent -> PongGame -> IO ()
processKeyEvent event game = do
...
case event of
...
KeyDown 'W' -> void $ swapMVar (wHeld game) True
KeyUp 'W' -> void $ swapMVar (wHeld game) False
...
推荐阅读
- javascript - 在 Highchart 中解析数据
- sql - SQL Server UPDATE 来自另一个表的列值的列值
- xlwings - xlwings 处理多个 python 文件
- unity3d - 触发碰撞时移动相机位置 y
- c# - 如何避免角色随背景图像移动?
- web-services - 空手道自动设置 Content-Type 标头
- python-3.x - Tkinter 对话框打开文件或目录
- amazon-web-services - 如何将自定义响应标头添加到 AWS 网关中发布的 API
- python - python中的sum函数如何处理这种情况(添加列表的元组)
- r - group_by 并使用条件将代码应用于组中的每个元素