Как я могу использовать MVars для перемещения весла в моей игре в пингпонг на Haskell?
У меня уже есть функция, которая перемещает 2 весла в игре в пинг-понг в Haskell. Я хочу изменить, чтобы он теперь использовал MVars.
Я знаю, что мне нужно изменить wHeld, sHeld, downHeld и upHeld на MVars, но есть идеи, как изменить movePaddle для работы с MVars?
Также, когда я объявляю wHeld MVars, он показывает ошибку при создании show (Не экземпляр для (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
1 ответ
MVars работает так, что значение типа MVar Bool
является непрозрачным "токеном", ссылающимся на место хранения для Bool
, Вы создаете такой токен и читаете и изменяете содержимое связанного с ним места хранения, используя действия ввода-вывода.
Сам токен (MVar Bool
значение) не имеет Show
экземпляр по умолчанию. В целях отладки вы можете добавить один:
instance Show (MVar a) where show _ = "<MVar>"
Это позволит вам получить Show
экземпляр для PongGame
без получения сообщения об ошибке. Однако значения, хранящиеся в MVars
не может быть отображено Show
экземпляр без каких-либо вопиющих злоупотреблений ввода-вывода, поэтому вы можете рассмотреть возможность написания функции dumpGame :: PongGame -> IO ()
симпатично печатает текущее состояние игры со всеми значениями MVar, позволяя вам пропустить Show
пример целиком.
Во всяком случае, переписать вашу программу, чтобы использовать MVars в ключе 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
...
}
Вы захотите переписать свой movePaddle
и его подфункции для запуска в монаде IO:
movePaddle :: PongGame -> IO PongGame
moveLeftPaddle :: PongGame -> IO PongGame
moveRightPaddle :: PongGame -> IO PongGame
В movePaddle
Вы можете заменить .
оператор с <=<
из Control.Monad
, который является монадическим эквивалентом состава функции:
movePaddle = moveLeftPaddle <=< moveRightPaddle
Это в основном сокращение для:
movePaddle game = do
game1 <- moveRightPaddle game
game2 <- moveLeftPaddle game1
return game2
Затем вам нужно будет переписать moveLeftPaddle
а также moveRightPaddle
чтобы получить доступ к содержимому MVars, выполнив действия ввода-вывода:
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
связано с wHeld
поле. Затем мы выполняем действие IO readMVar <this_token>
на самом деле получить Bool
значение, которое мы можем затем действовать в case
Заявление об обновлении игрового состояния.
В другом месте вашей программы вам понадобится setup
рутина, которая создает эти MVars:
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
...