在这个游戏中,我需要每隔半秒左右向前迈出一步,同时偶尔获得改变方向的输入。这些似乎无法用haskell做的事情有办法吗?目前我有一个mv胎面失速例外。
答案 0 :(得分:2)
更新:找到hWaitForInput
中的System.IO
功能,该功能与waitFor
基本相同。
以下是一些主要基于this answer的代码。
我做的主要区别是等待按键的线程不直接执行getChar
。 MVar
中传达的结果表示超时或已发生按键操作。实际获得角色是主线程的责任。这可以防止在获取角色并将其放入MVar
之间杀死char读取线程时可能出现的竞争条件。
import Control.Concurrent
import Control.Monad
import Data.Maybe
import System.IO
import Control.Exception
data Event = CharReady | TimedOut
withRawStdin :: IO a -> IO a
withRawStdin = bracket uncook restore . const
where
uncook = do
oldBuffering <- hGetBuffering stdin
oldEcho <- hGetEcho stdin
hSetBuffering stdin NoBuffering
hSetEcho stdin False
return (oldBuffering, oldEcho)
restore (oldBuffering, oldEcho) = do
hSetBuffering stdin oldBuffering
hSetEcho stdin oldEcho
waitFor :: Int -> IO Event
waitFor delay = do
done <- newEmptyMVar
withRawStdin . bracket (start done) cleanUp $ \_ -> takeMVar done
where
start done = do
t1 <- forkIO $ hLookAhead stdin >> putMVar done CharReady
t2 <- forkIO $ threadDelay delay >> putMVar done TimedOut
return (t1, t2)
cleanUp (t1, t2) = do
killThread t1
killThread t2
loop state = do
if state <= 0
then putStrLn "Game over."
else do putStrLn $ "Rounds to go: " ++ show state
e <- waitFor 3000000
case e of
TimedOut -> do putStrLn "Too late!"; loop state
CharReady -> do c <- getChar -- should not block
if c == 'x'
then do putStrLn "Good job!"; loop (state-1)
else do putStrLn "Wrong key"; loop state
main = loop 3