State Monad 在游戏中保存棋盘
State Monad to save board in game
我有一个模块 Game
定义了这样一个方法 play play :: Board -> Move - > Board
。
我想在另一个名为 Playing
的模块中使用 State Monad,该模块导入了 Game
模块,这样我就可以从那里循环调用 play
直到 Board
达到一定状态
我想用从 State Monad 获得的 Board
调用方法,然后用 play
返回的 Board
更新 State Monad 值。
因此,在循环进行时,我想接收移动以应用于 play
方法和我的当前状态。
但是我完全不知道如何以模块 Game
不知道我正在使用 State Monad 的方式实现这一点。
我看了很多教程和例子(比如 this, this, this 等),我觉得我理解 State Monad 在那里应用的方式,但显然还不够好,无法抽象它到这个特定的实现。
playing :: IO ()
playing = do
putStr $ "The board looks like:"
board <- get
putStr $ showBoard board
putStr $ "Indicate a move:"
move <- getLine
if validMove move then do
newBoard <- play board (getMove move)
if gameEnded newBoard then do
putStr $ "You win!" --stop the execution
else do
put newBoard
else do
putStr $ "Invalid move"
我希望 playing
处于循环状态,直到它得到一个特定的 Board
,这意味着游戏结束。并使用 State Monad 将当前 Board
发送到 play
和 Game
模块中的其他方法,如 gameEnded :: Board -> Bool
、showBoard :: Board -> String
和 'getMove :: String -> Move'.
欢迎任何帮助
如果将参数顺序交换为 play
,则函数类型为:
Move -> Board -> Board
您可以使用 Move
部分应用以获得以下类型之一:
Board -> Board
您可以将其转换为 State
上的操作,使用 modify :: (s -> s) -> State s ()
修改面板:
playing :: Move -> State Board ()
playing move = modify (play move)
这里的一个解决方案是 monad 转换器——听起来比实际更可怕。您可以使用 StateT
而不是 IO
,StateT
存储游戏状态,IO
提示用户移动。例如:
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State (evalStateT, gets, modify)
-- Get a move from the user.
getMove :: IO Move
getMove = do
line <- getLine
-- (Your implementation of parsing moves here.)
-- The initial state of the board.
initialBoard :: Board
initialBoard = -- ...
-- Whether the board represents a completed game.
boardDone :: Board -> Bool
boardDone board = -- ...
-- Main game loop.
gameLoop :: IO ()
gameLoop = evalStateT loop initialBoard
where
loop = do
move <- lift getMove
modify (play move)
done <- gets boardDone
if done then pure () else loop
您使用lift
将普通IO
动作转换为StateT Board IO
动作,modify :: (Monad m) => (s -> s) -> StateT s m ()
修改状态,gets :: (Monad m) => (s -> a) -> StateT s m a
读取属性当前状态。 loop
要么 tail-calls 自己继续玩,要么 returns.
在您编辑的问题中使用结构和名称:
playing :: IO ()
playing = evalStateT loop initialBoard
where
loop :: StateT Board IO ()
loop = do
printBoard
move <- lift promptMove
modify (play move)
ended <- gets gameEnded
if ended
then lift $ putStrLn "You win!"
else loop
printBoard :: StateT Board IO ()
printBoard = do
lift $ putStrLn $ "The board looks like:"
board <- get
lift $ putStrLn $ showBoard board
promptMove :: IO Move
promptMove = do
putStr "Indicate a move: "
move <- getLine
if validMove move
then pure $ getMove move
else do
putStrLn "Invalid move."
promptMove
我有一个模块 Game
定义了这样一个方法 play play :: Board -> Move - > Board
。
我想在另一个名为 Playing
的模块中使用 State Monad,该模块导入了 Game
模块,这样我就可以从那里循环调用 play
直到 Board
达到一定状态
我想用从 State Monad 获得的 Board
调用方法,然后用 play
返回的 Board
更新 State Monad 值。
因此,在循环进行时,我想接收移动以应用于 play
方法和我的当前状态。
但是我完全不知道如何以模块 Game
不知道我正在使用 State Monad 的方式实现这一点。
我看了很多教程和例子(比如 this, this, this 等),我觉得我理解 State Monad 在那里应用的方式,但显然还不够好,无法抽象它到这个特定的实现。
playing :: IO ()
playing = do
putStr $ "The board looks like:"
board <- get
putStr $ showBoard board
putStr $ "Indicate a move:"
move <- getLine
if validMove move then do
newBoard <- play board (getMove move)
if gameEnded newBoard then do
putStr $ "You win!" --stop the execution
else do
put newBoard
else do
putStr $ "Invalid move"
我希望 playing
处于循环状态,直到它得到一个特定的 Board
,这意味着游戏结束。并使用 State Monad 将当前 Board
发送到 play
和 Game
模块中的其他方法,如 gameEnded :: Board -> Bool
、showBoard :: Board -> String
和 'getMove :: String -> Move'.
欢迎任何帮助
如果将参数顺序交换为 play
,则函数类型为:
Move -> Board -> Board
您可以使用 Move
部分应用以获得以下类型之一:
Board -> Board
您可以将其转换为 State
上的操作,使用 modify :: (s -> s) -> State s ()
修改面板:
playing :: Move -> State Board ()
playing move = modify (play move)
这里的一个解决方案是 monad 转换器——听起来比实际更可怕。您可以使用 StateT
而不是 IO
,StateT
存储游戏状态,IO
提示用户移动。例如:
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State (evalStateT, gets, modify)
-- Get a move from the user.
getMove :: IO Move
getMove = do
line <- getLine
-- (Your implementation of parsing moves here.)
-- The initial state of the board.
initialBoard :: Board
initialBoard = -- ...
-- Whether the board represents a completed game.
boardDone :: Board -> Bool
boardDone board = -- ...
-- Main game loop.
gameLoop :: IO ()
gameLoop = evalStateT loop initialBoard
where
loop = do
move <- lift getMove
modify (play move)
done <- gets boardDone
if done then pure () else loop
您使用lift
将普通IO
动作转换为StateT Board IO
动作,modify :: (Monad m) => (s -> s) -> StateT s m ()
修改状态,gets :: (Monad m) => (s -> a) -> StateT s m a
读取属性当前状态。 loop
要么 tail-calls 自己继续玩,要么 returns.
在您编辑的问题中使用结构和名称:
playing :: IO ()
playing = evalStateT loop initialBoard
where
loop :: StateT Board IO ()
loop = do
printBoard
move <- lift promptMove
modify (play move)
ended <- gets gameEnded
if ended
then lift $ putStrLn "You win!"
else loop
printBoard :: StateT Board IO ()
printBoard = do
lift $ putStrLn $ "The board looks like:"
board <- get
lift $ putStrLn $ showBoard board
promptMove :: IO Move
promptMove = do
putStr "Indicate a move: "
move <- getLine
if validMove move
then pure $ getMove move
else do
putStrLn "Invalid move."
promptMove