功能性香蕉旅行者游戏 - 迷人和疯狂

Mic*_*ard 12 haskell reactive-banana

我想用反应性香蕉写一个人们可以写机器人的旅行者游戏.FRP对我来说是全新的,我在开始时遇到了麻烦.当我开始时,我创建了一个更精细的图表,但出于我的目的,我试图让它尽可能简单.我想要一些指导,主要是关于从哪里开始,以及如何将这个更大的问题分解为更小的问题来解决.这是最初的设计.

我们的想法是拥有一个TIode图表(为了简单起见,我现在忽略加权边缘).这些LNode我描述为Planets.该Planet■找一个名字,在地图上的玩家的PlanetResource秒.玩家拥有一个id,资源和一个Planet.Here是数据结构和一些相关的功能,接下来是更多的讨论.

-- Graph-building records and functions

data PlanetName = Vulcan
                | Mongo
                | Arakis
                | Dantooine
                | Tatooine
                     deriving (Enum,Bounded,Show)

data Planet = Planet {pName :: PlanetName
                     ,player :: IntMap Player
                     ,resources :: Int
                     } deriving Show

data Player = Player {pid :: Int
                    ,resources :: Int
                    } deriving Show



makePlanetNodes :: PlanetName -> [LNode Planet]
makePlanetNodes planet = Prelude.map makePlanetNodes' $
                         zip [planet ..] [0 ..]
  where makePlanetNodes' (planet,i) =
          (i,Planet {pName = planet, players = IM.empty})

makePlanetGraph p = mkGraph p [(0,1,1),(1,2,2),(2,3,4),(3,4,3),(4,0,2)]

-- Networking and command functions

prepareSocket :: PortNumber -> IO Socket
prepareSocket port = do
   sock' <- socket AF_INET Stream defaultProtocol
   let socketAddress = SockAddrInet port 0000
   bindSocket sock' socketAddress
   listen sock' 1
   putStrLn $ "Listening on " ++ (show port)
   return sock'

acceptConnections :: Socket -> IO ()
acceptConnections sock' = do
   forever $ do
       (sock, sockAddr) <- Network.Socket.accept sock'
       handle <- socketToHandle sock ReadWriteMode
       sockHandler sock handle

sockHandler :: Socket -> Handle -> IO ()
sockHandler sock' handle = do
    hSetBuffering handle LineBuffering
    forkIO  $ commandProcessor handle
    return ()

commandProcessor :: Handle -> IO ()
commandProcessor handle = untilM (hIsEOF handle) handleCommand >> hClose handle
  where
    handleCommand = do
        line <- hGetLine handle
        let (cmd:arg) = words line
        case cmd of
            "echo" -> echoCommand handle arg
            "move" -> moveCommand handle arg
            "join" -> joinCommand handle arg
            "take" -> takeCommand handle arg
            "give" -> giveCommand handle arg
            _ -> do hPutStrLn handle "Unknown command"


echoCommand :: Handle -> [String] -> IO ()
echoCommand handle arg = do
    hPutStrLn handle (unwords arg)

moveCommand = undefined

joinCommand = undefined

takeCommand = undefined

giveCommand = undefined
Run Code Online (Sandbox Code Playgroud)

这是我目前所知的,我的活动将涉及类型PlanetPlayer.行为将涉及移动,加入,接受和给予.当玩家加入时,它将创建一个新Player事件并使用它更新Vulcan上的Map Player.LNode如果LNodes通过边连接,则移动将允许从一个到另一个的遍历 .以将删除resources从目前PlanetPlayer是"上",并添加这些resourcesPlayer.给出相反的意思.

我怎样才能将这个大问题分解成更小的问题,这样我才能理解这些东西?

更新:结果亨特不是一个帮助学习玻璃钢的好选择,请参阅FRP在这里的解释.这是Heinrich Apfelmus的回应.

也就是说,我现在完全会忽略网络代码.我可以写一些虚拟机器人来测试时序等.

更新:有些人似乎对此问题感兴趣,所以我将在此处跟踪相关问题.

建立计时器

输入处理

Eri*_*ikR 11

这是一个复杂的项目.游戏大致分解为以下几个部分:

  • 输入层(将输入转换为游戏事件)
  • 引擎(接受事件和当前状态以产生新的游戏状态)
  • 输出层(显示游戏状态和通过执行事件产生的消息)

我首先使用控制台输入和输出(即stdin和stdout)简化输入和输出层.稍后您可以添加网络支持.

其次,我会简化游戏本身.例如,从单人游戏开始.最近,我将Lisp的游戏"Grand Theft Wumpus"翻译成Haskell有很多乐趣.

第三,我将从游戏引擎开始.这意味着你必须考虑:

  • 什么是游戏状态?
  • 什么是比赛活动?

为游戏状态和每个事件定义Haskell数据结构.需要注意的是游戏状态需要记录的一切就是有关游戏简介:玩家甚至随机数种子的地图,玩家的位置,状态.

游戏状态通常是产品类型:

data GameState = {  _mapNodes :: [Node]
                   ,_mapEdges  :: [ (Node,Node) ]
                   ,_player   :: Node
                   , ...
                 }
Run Code Online (Sandbox Code Playgroud)

游戏事件应定义为总和类型:

data GameEvent =
  | MovePlayer Node
  | Look
  | ...
Run Code Online (Sandbox Code Playgroud)

定义了这些数据结构后,编写performEvent函数:

performEvent :: GameEvent -> GameState -> IO(GameState)
Run Code Online (Sandbox Code Playgroud)

之所以说的结果performEvent就是IO(GameState)就是,你可能需要通知的球员发生了什么,并用IO单子将是在这一阶段的比赛要做到这一点最简单的方法(没有双关语意.)有办法净化类似的功能performEvent,但这是一个完整的其他主题.

例:

performEvent :: GameEvent -> GameState -> IO(GameState)
performEvent (Move p n) s =
  do putStrLn "Moving from " ++ (show $ s _player) ++ " to " ++ (show n)
     return s { _player = n }
performEvent Look       s =
  do putStrLn "You are at " ++ (show $ s _player)
     return s
Run Code Online (Sandbox Code Playgroud)

测试完成后performEvent,可以添加前端以将一行文本转换为GameEvent:

parseInput :: Text -> Maybe GameEvent
parseInput t = case Text.words t of
                 ("look":_)    -> Just Look
                 ("move":n:_)  -> Move <$> (parseNode n)
                 otherwise     -> Nothing
Run Code Online (Sandbox Code Playgroud)

然后添加一个输入循环,编写一个函数来创建初始GameState,在你知道它之前你将拥有一个真正的互动游戏!