首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Functional-Banana Traveller游戏-耐人寻味和令人抓狂

Functional-Banana Traveller游戏-耐人寻味和令人抓狂
EN

Stack Overflow用户
提问于 2012-09-06 12:20:13
回答 1查看 430关注 0票数 12

我想用reactive-banana写一个旅行者游戏,人们可以为它写机器人。FRP对我来说是全新的,我在起步时遇到了麻烦。当我开始的时候,我创建了一个更精致的Graph,但为了我在这里的目的,我试图使它尽可能简单。我想要一些指导,主要是关于从哪里开始,以及如何将这个更大的问题分解成更小的问题来解决。这是最初的设计。

这个想法是要有一个LNodes图(为了简单起见,我现在忽略了带权重的边)。我将这些LNodes描述为PlanetPlanet有一个名称,PlanetResource上的球员地图。球员有一个id,资源和一个Players。下面是数据结构和一些相关函数,随后将进行更多讨论。

代码语言:javascript
复制
-- 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

到目前为止,我所知道的是,我的事件将涉及PlanetPlayer类型。行为将涉及移动、加入、获取和给予。当玩家加入时,它将创建一个新的Player事件,并使用该Player更新火神上的地图。Move将允许从一个LNode遍历到另一个LNode,前提是这些an通过边连接。Take将从当前的Planet中删除Player为"on“的resources,并将这些resources添加到Player。给予会适得其反。

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

更新:事实证明,猎杀文帕斯不是一个很好的选择,以帮助学习玻璃钢,请参阅什么是玻璃钢是here的解释。这是海因里希·阿普菲尔默斯的回复。

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

更新:一些人似乎对这个问题感兴趣,所以我将在这里跟踪相关问题。

building a timer

input handling

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-09-07 02:12:29

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

  • an input layer (将输入转换为游戏事件)
  • an engine (采用事件和当前状态来生成新的游戏状态)
  • an output layer (显示游戏状态和执行事件产生的消息)

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

其次,我会简化游戏本身。例如,从单人游戏开始。最近,我在把Lisp的游戏“侠盗猎王”翻译成Haskell时获得了很大的乐趣。

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

  • 游戏状态是什么?
  • 游戏事件是什么?

为游戏状态和每个事件定义Haskell数据结构。请注意,游戏状态需要记录与游戏相关的所有内容:地图、玩家位置、玩家状态,甚至是随机数种子。

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

代码语言:javascript
复制
data GameState = {  _mapNodes :: [Node]
                   ,_mapEdges  :: [ (Node,Node) ]
                   ,_player   :: Node
                   , ...
                 }

游戏事件应定义为sum类型:

代码语言:javascript
复制
data GameEvent =
  | MovePlayer Node
  | Look
  | ...

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

代码语言:javascript
复制
performEvent :: GameEvent -> GameState -> IO(GameState)

performEvent的结果是IO(GameState)的原因是你可能需要通知玩家发生了什么,而在游戏的这个阶段使用IO monad将是最简单的方法(没有双关语)。有一些方法可以净化像performEvent这样的函数,但那是另一个话题。

示例:

代码语言:javascript
复制
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

一旦测试了performEvent,就可以添加一个前端将一行文本转换为GameEvent

代码语言:javascript
复制
parseInput :: Text -> Maybe GameEvent
parseInput t = case Text.words t of
                 ("look":_)    -> Just Look
                 ("move":n:_)  -> Move <$> (parseNode n)
                 otherwise     -> Nothing

然后添加一个输入循环,编写一个函数来创建初始GameState,不知不觉中您就拥有了一个真正的交互式游戏!

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12292934

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档