मुझे लगता है कि मैं एक गेम टिक के लिए सभी संभावित राज्यों को उत्पन्न कर सकता हूं, लेकिन चार खिलाड़ियों और 5 बुनियादी कार्यों (4 चाल और बम जगह) के साथ यह खेल के पेड़ के पहले स्तर पर 5 ^ 4 राज्य देता है।
सही बात! आपको प्रत्येक गेम टिक के लिए सभी 5 ^ 4 (या यहां तक कि 6 ^ 4) की खोज करने की आवश्यकता है, क्योंकि आप 4 दिशाओं में चल सकते हैं, रुक सकते हैं और "एक बम डाल सकते हैं?" क्रिया कर सकते हैं। लेकिन, जब एक खिलाड़ी ने पहले से ही स्थानांतरित करने का फैसला किया, तो इस कदम को निष्पादित होने तक कुछ समय लगता है (जैसे 10 गेम टिक)। इस अवधि के दौरान संभावनाओं की संख्या कम हो जाती है।
यह मान हर अगले स्तर के साथ तेजी से बढ़ेगा। क्या मैं कुछ भूल रहा हूँ? क्या इसे लागू करने के कोई तरीके हैं या क्या मुझे पूरी तरह से अलग एल्गोरिथ्म का उपयोग करना चाहिए?
आप केवल एक ही गेम स्टेट "सबट्री" की गणना करने के लिए हैश-टेबल का उपयोग कर सकते हैं। कल्पना कीजिए कि खिलाड़ी A ऊपर और नीचे चलता है, जबकि अन्य सभी खिलाड़ी "प्रतीक्षा" करते हैं, आप एक ही गेम स्टेट में समाप्त होते हैं। यह "बाएँ-दाएँ" या "दाएँ-बाएँ" के समान है। इसके अलावा "ऊपर-तब-बाएं" और "बाएं-तब-ऊपर" बढ़ने से एक ही स्थिति होती है। हैश-टेबल का उपयोग करके आप एक गेम स्थिति के लिए गणना किए गए स्कोर को "पुन: उपयोग" कर सकते हैं जिसका मूल्यांकन पहले ही किया जा चुका है। यह विकास की गति को काफी कम कर देता है। गणितीय रूप से, यह आपके घातीय वृद्धि समारोह के आधार को कम करता है। यदि यह जटिलता कम कर देता है, इसका अंदाजा लगाने के लिए, हम नक्शे पर पहुंच योग्य स्थिति (= अलग-अलग गेम स्टेट्स) की तुलना में केवल एक खिलाड़ी के लिए संभव चालों को देखते हैं, अगर खिलाड़ी सिर्फ ऊपर / नीचे / बाएं / दाएं / रुक सकता है ।
गहराई 1: 5 चाल, 5 अलग-अलग राज्य, इस पुनरावृत्ति के लिए 5 अतिरिक्त राज्य
गहराई 2: 25 चाल, 13 अलग-अलग राज्य, इस पुनरावृत्ति के लिए 8 अतिरिक्त राज्य
गहराई 3: 6125 चाल, 25 विभिन्न राज्यों, 12 अतिरिक्त राज्यों इस पुनरावृत्ति के लिए
इसकी कल्पना करने के लिए, अपने आप को उत्तर दें: मानचित्र पर किन क्षेत्रों में एक चाल, दो चाल, तीन चालों के साथ पहुंचा जा सकता है। इसका उत्तर है: प्रारंभ स्थिति से अधिकतम दूरी = 1, 2 या 3 वाले सभी क्षेत्र।
हैशटेबल का उपयोग करते समय आपको केवल एक बार प्रत्येक पहुंच योग्य खेल स्थिति (गहराई में 3 में हमारे उदाहरण 25) का मूल्यांकन करना होगा। जबकि हैशटेबल के बिना आपको कई बार उनका मूल्यांकन करने की आवश्यकता होती है, जिसका मतलब होगा गहराई स्तर पर 25 के बजाय 6125 मूल्यांकन। सबसे अच्छा: एक बार जब आपने हैशटेबल प्रविष्टि की गणना की तो आप इसे बाद के समय के चरणों में फिर से उपयोग कर सकते हैं ...
आप वृद्धिशील गहरीकरण और अल्फा-बीटा प्रूनिंग "कट" उपप्रकार का भी उपयोग कर सकते हैं जो अधिक गहराई में खोज के लायक नहीं हैं। शतरंज के लिए यह खोजे गए नोड्स की संख्या को लगभग 1% तक कम कर देता है। अल्फा-बीटा प्रूनिंग का संक्षिप्त परिचय यहां एक वीडियो के रूप में पाया जा सकता है: http://www.teachingtree.co/cs/watch?concept_name=Alpha-beta+Pruning
आगे की पढ़ाई के लिए एक अच्छी शुरुआत है http://chessprogramming.wikispaces.com/Search । पेज शतरंज से संबंधित है, लेकिन खोज और अनुकूलन एल्गोरिदम काफी समान हैं।
एक और (लेकिन जटिल) एआई एल्गोरिथ्म - जो खेल के लिए अधिक उपयुक्त होगा - "टेम्पोरल डिफरेंस लर्निंग" है।
सादर
स्टीफन
पुनश्च: यदि आप संभावित गेम स्टेट्स की संख्या कम कर देते हैं (जैसे कि नक्शे का बहुत छोटा आकार, प्रति खिलाड़ी केवल एक बम, और कुछ नहीं), तो सभी गेम स्टेट्स के लिए मूल्यांकन की पूर्व-गणना करने का मौका है।
--edit--
आप एक न्यूरोनल नेटवर्क को प्रशिक्षित करने के लिए न्यूनतम गणना के ऑफ़लाइन-गणना परिणामों का उपयोग कर सकते हैं। या आप उनका उपयोग हाथ से कार्यान्वित रणनीतियों की मूल्यांकन / तुलना करने के लिए कर सकते हैं। उदाहरण के लिए आप कुछ सुझाए गए "व्यक्तित्व" और कुछ अनुमानों को लागू कर सकते हैं जो पता लगाते हैं कि किन स्थितियों में रणनीति अच्छी है। इसलिए आपको स्थितियों (उदाहरण के लिए गेम स्टेट्स) को "वर्गीकृत" करना चाहिए। इसे एक न्यूरोनल नेटवर्क द्वारा भी नियंत्रित किया जा सकता है: वर्तमान स्थिति में कौन सी हाथ से कोडित रणनीति सबसे अच्छा खेल रही है, इसका अनुमान लगाने के लिए एक न्यूरोनल नेटवर्क को प्रशिक्षित करें। यह एक वास्तविक खेल के लिए बहुत अच्छा वास्तविक समय के निर्णय का उत्पादन करना चाहिए। कम-गहराई-सीमा वाली खोज से बहुत बेहतर, जिसे अन्यथा प्राप्त किया जा सकता है, क्योंकि इससे कोई फर्क नहीं पड़ता कि ऑफ़लाइन गणना कितनी देर तक होती है (वे खेल से पहले हैं)।
- # 2 संपादित करें -
यदि आप केवल हर 1 सेकंड में अपनी सर्वश्रेष्ठ चाल को पुनर्गणना करते हैं, तो आप अधिक उच्च स्तर की योजना बनाने का भी प्रयास कर सकते हैं। उससे मेरा मतलब क्या है? आप जानते हैं कि 1 सेकंड में आप कितने मूव कर सकते हैं। तो आप पहुंच योग्य पदों की सूची बना सकते हैं (जैसे यदि यह 1 सेकंड में 3 चाल होगी, तो आपके पास 25 पहुंच वाले स्थान होंगे)। तब आप इस तरह की योजना बना सकते हैं: "स्थिति x और एक बम रखें"। जैसा कि कुछ अन्य लोगों ने सुझाव दिया है कि आप एक "खतरे" मानचित्र बना सकते हैं, जिसका उपयोग रूटिंग एल्गोरिदम के लिए किया जाता है (स्थिति x पर कैसे जाएं? कौन सा पथ पसंद किया जाना चाहिए [ज्यादातर मामलों में कुछ बदलाव संभव हैं])। यह एक विशाल हैशटेबल की तुलना में कम मेमोरी खपत है, लेकिन कम इष्टतम परिणाम पैदा करता है। लेकिन जैसा कि यह कम मेमोरी का उपयोग करता है यह कैशिंग प्रभाव (आपके एल 1 / एल 2 मेमोरी कैश का बेहतर उपयोग) के कारण तेज हो सकता है।
ADDITIONALLY: आप प्री-सर्च कर सकते हैं, जिसमें केवल एक खिलाड़ी के लिए मूव्स होते हैं, जिसमें से प्रत्येक के लिए भिन्नताएँ होती हैं जो परिणाम खो देते हैं। इसलिए सभी अन्य खिलाड़ियों को खेल से बाहर ले जाएं ... स्टोर जो प्रत्येक खिलाड़ी को खोए बिना चुन सकते हैं, को मिलाते हैं। यदि केवल लूज़िंग मूव्स हैं, तो उस मूव कॉम्बिनेशन की तलाश करें जहाँ खिलाड़ी सबसे लंबे समय तक जीवित रहता है। इस प्रकार की ट्री संरचनाओं को संग्रहीत / संसाधित करने के लिए आपको इस तरह के इंडेक्स-पॉइंटर्स के साथ एक सरणी का उपयोग करना चाहिए:
class Gamestate {
int value;
int bestmove;
int moves[5];
};
#define MAX 1000000
Gamestate[MAX] tree;
int rootindex = 0;
int nextfree = 1;
प्रत्येक राज्य का मूल्यांकन "मूल्य" है और अगले गेमस्टेट्स से लिंक होता है जब चलती है (0 = स्टॉप, 1 = अप, 2 = राइट, 3 = डाउन, 4 = बाएं) चाल में "ट्री" के भीतर एरे इंडेक्स को संग्रहीत करके [0] ] को स्थानांतरित करने के लिए [4]। अपने पेड़ को पुनरावर्ती बनाने के लिए यह इस तरह दिख सकता है:
const int dx[5] = { 0, 0, 1, 0, -1 };
const int dy[5] = { 0, -1, 0, 1, 0 };
int search(int x, int y, int current_state, int depth_left) {
// TODO: simulate bombs here...
if (died) return RESULT_DEAD;
if (depth_left == 0) {
return estimate_result();
}
int bestresult = RESULT_DEAD;
for(int m=0; m<5; ++m) {
int nx = x + dx[m];
int ny = y + dy[m];
if (m == 0 || is_map_free(nx,ny)) {
int newstateindex = nextfree;
tree[current_state].move[m] = newstateindex ;
++nextfree;
if (newstateindex >= MAX) {
// ERROR-MESSAGE!!!
}
do_move(m, &undodata);
int result = search(nx, ny, newstateindex, depth_left-1);
undo_move(undodata);
if (result == RESULT_DEAD) {
tree[current_state].move[m] = -1; // cut subtree...
}
if (result > bestresult) {
bestresult = result;
tree[current_state].bestmove = m;
}
}
}
return bestresult;
}
इस तरह की पेड़ संरचना बहुत तेज है, क्योंकि गतिशील रूप से स्मृति आवंटित करना वास्तव में बहुत धीमा है! लेकिन, सर्च ट्री को स्टोर करना या तो काफी धीमा है ... इसलिए यह एक प्रेरणा है।