功能香蕉旅行者游戏 - 有趣的和使人发狂(Functional-Banana Traveller G

2019-06-27 21:00发布

我想用无功香蕉写一个旅行者的游戏,人们可以写为机器人。 玻璃钢是完全新的给我,和我有麻烦的开始。 我创建了一个更复杂的图形,当我开始,但我在这里的目的,我试图使它尽可能简单。 我想一些指导,主要是从哪里开始,如何分解这个更大的问题分成更小的问题需要解决。 这里的初始设计。

这个想法是有的lnode的图(加权边缘,我忽略了,现在为了简单起见)。 这些的lnode我形容为Planet秒。 该Planet ■找一个名字,在地图上的玩家的PlanetResource秒。 玩家有一个ID,资源和Planet.Here的数据Structues和一些相关功能,其次是更多的讨论。

-- 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 。 此举将允许从一个遍历LNode到另一个,所提供的LNode S按一个边缘相连接。 以将删除resources从目前的PlanetPlayer是“开”和那些添加resourcesPlayer 。 给却反其道而行之。

我怎样才能打破这种大问题分成更小的问题,这样我就可以解决这个东西我的头?

更新:原来亨特Wumpus是不是一个很好的选择,以帮助学习FRP,见什么FRP是一个交代在这里 。 这是由海因里希Apfelmus的响应。

这就是说,我会完全忽视网络代码现在。 我可以只写一些虚拟机器人测试时间等。

更新:有些人似乎是有意这个问题,所以我要在这里追踪相关的问题。

建立一个计时器

输入处理

Answer 1:

这是一个复杂的工程。 一个游戏大致分解成以下几部分:

  • 一个输入层(转换输入到游戏事件)
  • 发动机(采用的事件和当前状态,以产生一个新的游戏状态)
  • 的输出层(显示游戏状态和消息通过执行事件产生)

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

其次,我将简化游戏本身。 例如,开始一个单人游戏。 最近,我有很多的乐趣翻译的Lisp游戏的土地“侠盗猎Wumpus”哈斯克尔。

第三,我将与游戏引擎启动。 这意味着你要想一想:

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

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

本场比赛状态一般是产品类型:

data GameState = {  _mapNodes :: [Node]
                   ,_mapEdges  :: [ (Node,Node) ]
                   ,_player   :: Node
                   , ...
                 }

本场比赛的事件应该定义为和类型:

data GameEvent =
  | MovePlayer Node
  | Look
  | ...

你定义这些数据已经被定义的结构之后,写performEvent功能:

performEvent :: GameEvent -> GameState -> IO(GameState)

之所以说结果performEventIO(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

一旦你已经测试performEvent ,你可以添加一个前端翻译一行文本到的GameEvent

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

然后添加一个输入回路,编写一个函数来创建初始的游戏状态,你知道它之前,你将有一个真正的互动游戏!



文章来源: Functional-Banana Traveller Game - Intriguing and Maddening