2012-09-06 38 views
11

我想用反應香蕉來寫一個旅行者遊戲,人們可以編寫機器人。 FRP對我來說是全新的,而且我很難開始。我開始時創建了一個更精細的Graph,但爲了我的目的,我試圖儘可能簡化它。 我想要一些指導,主要是從哪裏開始,以及如何將這個更大的問題分解成更小的問題來解決。這是最初的設計。功能香蕉旅行者遊戲 - 令人反感和瘋狂

這個想法是有一個LNodes圖(爲了簡單起見,我現在忽略了加權邊緣)。這些LNodes我描述爲Planet s。 Planet有一個名字,玩家地圖PlanetResource s。玩家有一個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 

這是我目前所知,我的活動將涉及類型PlanetPlayer。行爲 將涉及移動,加入,採取和給予。當玩家加入時,它會創建一個新的Player事件並使用該Player更新Vulcan上的地圖。移動將允許從一個 LNode穿越到另一個,只要LNode s通過邊緣連接。 Take將從當前的Planet中刪除resources,Player爲「on」並將那些resources添加到 Player。 Give會做相反的事情。

我該如何將這個大問題分解成更小的問題,以便我能夠解決這個問題?

更新:結果尋找Wumpus不是一個很好的選擇來幫助學習FRP,請參閱對於here FRP的解釋。這是Heinrich Apfelmus的迴應。

這就是說,我現在完全忽略網絡代碼。我只能寫一些虛擬機器人來測試時間等。

更新:有些人似乎對這個問題感興趣,所以我要在這裏跟蹤相關的問題。

building a timer

input handling

回答

10

這是一個複雜的工程。一種遊戲大致分解成以下幾部分:

  • 輸入層(轉換輸入到遊戲事件)
  • 發動機(採用的事件和當前狀態,以產生一個新的遊戲狀態)
  • 輸出圖層(顯示遊戲狀態和執行事件產生的消息)

我會首先使用控制檯輸入和輸出(即stdin和stdout)來簡化輸入和輸出圖層。稍後您可以添加網絡支持。其次,我會簡化遊戲本身。例如,從單人遊戲開始。最近我將Lisp遊戲「Grand Theft Wumpus」翻譯成Haskell很有趣。

第三,我會從遊戲引擎開始。這意味着你必須考慮:

  • 什麼是遊戲狀態?
  • 什麼是遊戲事件?

定義遊戲狀態和每個事件的Haskell數據結構。請注意,遊戲狀態需要記錄與遊戲有關的所有:地圖,玩家位置,玩家狀態,甚至是隨機數種子。

遊戲狀態通常是產品類型:

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

遊戲事件應該定義爲和類型:

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

後你定義這些數據已經被定義的結構,寫performEvent功能:

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

結果的原因o f performEventIO(GameState)是你可能需要告訴玩家發生了什麼事情,並且使用monod將是在遊戲的這個階段完成這個最簡單的方法(沒有雙關語意思)。像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 

然後添加一個輸入迴路,寫函數來創建最初的GameState,並且在你知道它之前你會有一個真正的互動遊戲!

+1

啊狩獵的wumpus!是的,開始較小的聲音就像是一個好的舉動。 –