Как я могу использовать 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
     ...
Другие вопросы по тегам