2012-09-06 12 views
11

मैं एक ट्रैवलर गेम लिखने के लिए प्रतिक्रियाशील-केले का उपयोग करना चाहता हूं, जिसके लिए लोग बॉट लिख सकते हैं। एफआरपी मेरे लिए बिल्कुल नया है, और मुझे शुरुआत करने में परेशानी हो रही है। जब मैंने शुरू किया, मैंने एक और विस्तृत ग्राफ बनाया, लेकिन मेरे उद्देश्यों के लिए, मैंने इसे यथासंभव सरल बनाने की कोशिश की है। मुझे कुछ मार्गदर्शन चाहिए, अधिकांशतः कहां से शुरू करना है, और इस बड़ी समस्या को हल करने के लिए छोटी समस्याओं में कैसे टूटना है। प्रारंभिक डिजाइन यहाँ है।कार्यात्मक-केला ट्रैवलर गेम - दिलचस्प और पागलपन

विचार है कि एलएनओड्स का ग्राफ होना चाहिए (भारित किनारों के साथ मैं सादगी के लिए अब अनदेखा कर रहा हूं)। ये एलएनओड्स मैं Planet एस के रूप में वर्णन करता हूं। Planet के पास एक नाम है, Planet और Resource पर खिलाड़ियों का मानचित्र। खिलाड़ियों के पास एक आईडी, संसाधन और ग्रह है। यहां डेटा संरचनाएं और कुछ संबंधित कार्य हैं, इसके बाद और अधिक चर्चा हुई है।

-- 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 

यहाँ मैं अब तक क्या पता है, मेरी घटनाक्रम प्रकार Planet और Player शामिल होगी है। व्यवहार में कदम, शामिल होना, लेना और देना शामिल होगा। जब कोई खिलाड़ी जुड़ता है, तो यह एक नया Player ईवेंट बनाएगा और उस Player के साथ वल्कन पर मानचित्र अपडेट करेगा। मूव एक LNode से दूसरे पर एक ट्रैवर्सल की अनुमति देगा, बशर्ते LNode एस किनारे से जुड़े हों। वर्तमान PlanetPlayer से resources निकाल देंगे ले लो और Player करने के लिए उन resources जोड़ें "पर" है। विपरीत विपरीत करेंगे।

मैं इस बड़ी समस्या को छोटी समस्याओं में कैसे तोड़ सकता हूं ताकि मैं इस सामान के आसपास अपना सिर प्राप्त कर सकूं?

अद्यतन: बाहर निकलता है Wumpus एफआरपी सीखने में मदद करने के लिए एक अच्छा विकल्प नहीं है, एफआरपी here के लिए क्या है इसकी एक व्याख्या देखें। यह हेनरिक अपफेलमस की प्रतिक्रिया में है।

उस ने कहा, मैं अब नेटवर्किंग कोड को पूरी तरह से अनदेखा कर दूंगा। मैं समय परीक्षण करने के लिए बस कुछ डमी बॉट लिख सकता हूं।

अपडेट: कुछ लोगों को इस समस्या में रूचि लगती है, इसलिए मैं यहां संबंधित प्रश्नों को ट्रैक करने जा रहा हूं।

building a timer

input handling

उत्तर

10

यह एक जटिल परियोजना है। एक खेल मोटे तौर पर निम्नलिखित टुकड़ों में विघटित हो जाता है:

  • एक इनपुट परत (खेल की घटनाओं के लिए इनपुट तब्दील)
  • एक इंजन (एक घटना और वर्तमान स्थिति लेता है एक नया खेल राज्य का निर्माण करने के)
  • एक आउटपुट परत (प्रदर्शित करता है खेल राज्य और संदेशों प्रदर्शन घटनाओं द्वारा उत्पादित)

मैं पहली बार कंसोल इनपुट और आउटपुट का उपयोग करके इनपुट और आउटपुट परतों को आसान बनाने में होता है (यानी stdin और stdout।) बाद में आप नेटवर्क समर्थन जोड़ सकते हैं।

दूसरा, मैं गेम को सरल बना दूंगा। उदाहरण के लिए, एक सिंगल प्लेयर गेम से शुरू करें। हाल ही में मुझे लिस्प गेम "ग्रैंड थेफ्ट वम्पस" की भूमि हास्केल में अनुवाद करने में बहुत मज़ा आया था।

तीसरा, मैं गेम इंजन से शुरू करूंगा। इसका मतलब है कि आपको इसके बारे में सोचना होगा:

  • गेम स्थिति क्या है?
  • खेल आयोजन क्या हैं?

खेल स्थिति और प्रत्येक घटना के लिए हास्केल डेटा संरचनाओं को परिभाषित करें। ध्यान दें कि खेल राज्य को सब कुछ रिकॉर्ड करने की आवश्यकता है जो गेम के बारे में प्रासंगिक है: नक्शा, खिलाड़ी स्थान, खिलाड़ियों की स्थिति और यहां तक ​​कि यादृच्छिक संख्या के बीज भी।

खेल राज्य आम तौर पर एक उत्पाद के प्रकार होगा:

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 

फिर एक इनपुट पाश जोड़ने के लिए, लिखने के एक प्रारंभिक गेमस्टेट बनाने के लिए फ़ंक्शन, और इससे पहले कि आप इसे जानते हों, आपके पास एक वास्तविक इंटरैक्टिव गेम होगा!

+1

आह wumpus का शिकार! हाँ एक अच्छी चाल की तरह छोटी आवाज़ शुरू कर रहा है। –

संबंधित मुद्दे