Как я могу использовать MVars для перемещения весла в моей игре в пингпонг на Haskell? - PullRequest
0 голосов
/ 24 июня 2019

У меня уже есть функция, которая перемещает 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 Ответ

3 голосов
/ 25 июня 2019

Способ работы 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 и его подфункции для запуска в монаде ввода-вывода:

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 поле.Затем мы выполняем действие ввода-вывода 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
     ...
...