2010-09-11 3 views
12

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

वर्तमान में मेरे पास प्रोलॉग (100+) में तथ्यों और नियमों का एक समूह है। फॉर्म में कई व्यक्त चीजें, यदि game_state फिर कार्रवाई xyz करते हैं। अधिकांश नियम काफी जटिल हैं, जिनमें से कुछ जटिल हैं। मैंने एक सीमित राज्य मशीन दृष्टिकोण देखा, लेकिन यह बड़ी परिस्थितियों में इतनी अच्छी तरह से स्केल नहीं लग रहा था। सी ++ में इसे कोड करने का मेरा पहला प्रयास तब और अगर केस स्टेटमेंट्स का एक बड़ा दुःस्वप्न था।

if(this->current_game_state->some_condition == true){ 
     if(this->current_game_state->some_other_condition == false){  
       //some code 
     }else{ 
      return do_default_action(); 
     } 
    }else if(this->current_game->another_condition){ 
     //more code 
    } 

जटिलता जल्दी से अनियंत्रित हो गया: मैं हर जगह ऊपर पॉपिंग कोड की इस तरह की थी।

यदि सी ++ में इस प्रकार की समस्या को कोड करने का कोई अच्छा तरीका है? क्या इस तरह की स्थिति से निपटने के लिए कोई अच्छा डिजाइन पैटर्न है? स्रोत के भीतर तर्क को निहित करने की कोई आवश्यकता नहीं है, इसे केवल सी ++ से सुलभ होने की आवश्यकता है। केवल वास्तविक आवश्यकता यह है कि यह काफी तेज़ है।

मैंने नियम इंजनों को भी देखा और यदि पर्याप्त तेज़ हो तो वे उचित हो सकते थे। क्या आपको पता है कि ओपन सोर्स सी ++ नियम इंजन है जो उपयुक्त होगा?

उत्तर

9

कोड डेटा है, और डेटा कोड है। आपके पास कामकाजी कोड है - आपको इसे संकलित करने के तरीके में सी ++ को बेनकाब करने की आवश्यकता है, फिर आप इसका मूल्यांकन करने के लिए न्यूनतम दुभाषिया को कार्यान्वित कर सकते हैं।

एक संभावना है कि आप अपने प्रोलॉग नियमों को ले लें और उन्हें डेटा संरचना के लिए सबसे प्रत्यक्ष तरीके से अनुवाद करें।

struct { 
    State coming_from; 
    Event event; 
    void (*func)(some, args); 
    State going_to; 
} rules[] = { 
    { WANDERING_AROUND, HEAR_SOUND, look_around, ENEMY_SEEN }, 
    { ENEMY_SEEN,  GUN_LOADED, fire_gun, SNEEK_AWAY }, 
    { next, rule, goes, here }, 
    etc... 
} 

इसी प्रकार, फ़ंक्शन कॉल इस तरह से डेटा संरचनाओं को पॉप्युलेट कर सकते हैं कि यह अपने मूल Prolog जैसा दिखता है::

void init_rules() { 
    rule("Parent", "Bill", "John"); 
    rule("Parent", "Paul", "Bill"); 
    // 99 more rules go here... 
} 

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

+0

यह एक सीमित राज्य मशीन है, जो ठीक है उसने कहा कि उसने पहले कोशिश की और उसके चेहरे पर उड़ा दिया। – Potatoswatter

+0

यह इतना नहीं था कि एक सीमित राज्य मशीन जो मैं चाहता था वह नहीं था, यह अधिक था कि एक सीमित राज्य मशीन के निष्पक्ष कार्यान्वयन को प्रबंधित करने के लिए बहुत जटिल था। यह सुझाव जटिलता को बेहतर तरीके से प्रबंधित करने में मदद करता है। दुभाषिया का उपयोग सिर्फ वही होना चाहिए जो मुझे चाहिए यदि मैं इस दृष्टिकोण का पालन करना चाहता हूं। हालांकि मैं अभी भी एक सीमित राज्य मशीन दृष्टिकोण – shuttle87

+2

का उपयोग करने पर पूरी तरह से बेचा नहीं गया हूं पहला खंड एक राज्य मशीन है, लेकिन मेरा मुद्दा यह था कि आप इसे नेस्टेड के गुच्छा के बजाय टेबल संचालित एल्गोरिदम के रूप में कार्यान्वित कर सकते हैं, एल्स या एक बड़ा बुरा स्विच स्टेटमेंट। दूसरा खंड केवल सी ++ वाक्यविन्यास का उपयोग कर डीएसएल दिखाने की कोशिश कर रहा है। यह एक साधारण राज्य मशीन से अधिक हो सकता है। आपने प्रोलॉग काम कर लिया है, इसलिए इसे सी ++ में अनुवाद करने की कोशिश करने के बजाय, मुझे लगता है कि सी ++ को सिखाए जाने के लिए यह सरल और साफ हो सकता है कि आपके मौजूदा कोड/डेटा को कैसे समझें। हो सकता है कि आप अपने नियमों/तथ्यों का सबसेट पोस्ट कर सकें ताकि हम इसे बेहतर उपचार दे सकें और उचित उदाहरण दे सकें। – xscott

2

मुझे वास्तव में यह नहीं पता कि एक सीमित राज्य मशीन आपके गेम के लिए क्यों नहीं है। आप जो करना चाहते हैं उसे करने का यह एक आम तरीका है। आप इसे कंक्रीट क्रियाओं से साफ कोड रखने के लिए डेटा संचालित कर सकते हैं। परिमित राज्य एम। "एआई फॉर गेम देव" O'Reilly (डेविड एम। बौर्ग & ग्लेन सेमैन) में भी वर्णित है शायद आप मशीन को छोटे और समझने योग्य रखने के लिए कई छोटे नियम सेटों में नियमों को विभाजित करना चाहते हैं।

3

आप ग ++ कोड अपने prolog कोड में परिवर्तित करना चाहते हैं, तो केस्टर पुस्तकालय (C++) पर एक नज़र जो C++ तर्क प्रोग्रामिंग सक्षम है: तो अपने आप को मैं इसे बाहर प्रयास नहीं किया है http://www.mpprogramming.com/Cpp/Default.aspx

, मैं इसके प्रदर्शन के बारे में कुछ भी नहीं जानता।

आप एक राज्य मशीन का उपयोग करना चाहते हैं, कैसे के बारे में उपयोग पारा Boost.Meta राज्य मशीन

+0

बहुत अच्छा, अच्छी प्रस्तुति! – Potatoswatter

+0

लिंक/सुझाव के लिए बहुत दिलचस्प धन्यवाद लगता है! – shuttle87

1

पर एक नजर है?इसकी मूल रूप से सी कोड के साथ इंटरफ़ेस के लिए बनाया गया है।

+0

क्या पारा के लिए विशेष रूप से एक सी ++ इंटरफ़ेस है? इसके अलावा मुझे स्रोत से पारा संकलन में बहुत परेशानी हुई है। – shuttle87

+0

सी ++ में इंटरफेसिंग ईज़ी गेम है। लेकिन हाँ, जब तक आप कंपाइलर काम नहीं कर लेते हैं, तब तक इसका बेकार बेकार: पी – oadams

4

आप पॉलिमॉर्फिज्म का उपयोग कर सकते हैं। वर्चुअल फ़ंक्शन को कॉल करना प्रभावी रूप से एक बड़ा-गधा स्विच/केस है जो आपके द्वारा संकलित द्वारा किया गया और अनुकूलित किया गया है।

class GameState { 
    virtual void do_something() { std::cout << "GameState!"; } 
    // some functions 
    virtual ~GameState() {} 
}; 
class SomeOtherState : public GameState { 
    // some other functions 
    virtual void do_something() { std::cout << "SomeOtherState!"; } 
}; 
class MyFinalState : public GameState { 
    virtual void do_something() { std::cout << "MyOtherState!"; } 
}; 
class StateMachine { 
    std::auto_ptr<GameState> curr_state; 
public: 
    StateMachine() 
     : curr_state(NULL) {} 
    void DoSomething() { curr_state->DoSomething(); } 
    void SetState(GameState* ptr) { curr_state = ptr; } 
    template<typename T> void SetState() { curr_state = new T; } 
}; 
int main() { 
    StateMachine sm; 
    sm.SetState(new SomeOtherState()); 
    sm.SetState<SomeOtherState>(); 
    sm.DoSomething(); // prints "SomeOtherState!" 
    sm.SetState<MyFinalState>(); 
    sm.DoSomething(); // prints "MyFinalState!" 
} 

उपरोक्त उदाहरण में, मैं राज्यों में से किसी के बारे में स्विच करने के लिए, या भी जानते हैं कि अलग-अलग राज्यों में मौजूद हैं या वे क्या करते हैं (StateMachine कक्षा में, वैसे भी), चयन तर्क किया गया था जरूरत नहीं थी कंपाइलर द्वारा।

+0

यह फ़ंक्शन पॉइंटर्स के समूह के उपयोग पर वापस कटौती करने का एक शानदार तरीका प्रतीत होता है। भविष्य में परियोजनाओं के लिए मैं निश्चित रूप से कुछ ध्यान रखूंगा। – shuttle87

0

राज्य मशीनों के साथ प्रोल की अभिव्यक्तिपूर्ण शक्ति से मेल खाने का प्रयास करना साइकिल के साथ एक कार को बाहर निकालने की कोशिश करना है।

कास्टर शायद जाने का रास्ता है। यह बहुत हल्का है और तर्क प्रोग्रामिंग और शेष सी ++ के बीच चिकनी इंटरऑप की अनुमति देता है। ट्यूटोरियल वीडियो पर http://www.mpprogramming.com/cpp