ज्ञात कतार के साथ मैच गेम को अनुकूलित करने के लिए एल्गोरिदम


10

मैं फ्लॉवरज़ नामक गेम के लिए C # .NET में एक सॉल्वर लिखने की कोशिश कर रहा हूं। अपने संदर्भ के लिए, आप इसे एमएसएन पर यहाँ खेल सकते हैं: http://zone.msn.com/gameplayer/gameplayer.aspx?game=flowerz । मैं इसे मज़े के लिए लिख रहा हूँ, किसी प्रकार के असाइनमेंट या संबंधित किसी भी काम के लिए नहीं। इसके कारण, एकमात्र सीमा मेरा कंप्यूटर (एक इंटेल i7 कोर है, जिसमें 8 जीबी रैम है)। जहां तक ​​मेरा सवाल है, इसे कहीं और चलाने की जरूरत नहीं है।

संक्षेप में, इसके नियम इस प्रकार हैं:

  • रंगीन फूलों से भरी एक कतार है। इसकी लंबाई मनमानी है
    • कतार को प्रभावित नहीं किया जा सकता है
    • स्तर की शुरुआत में कतार उत्पन्न होती है
  • फूलों के एक या दो रंग होते हैं।
    • यदि दो रंग हैं, तो एक बाहरी रंग है, और एक आंतरिक रंग है। दो रंगों के मामले में, बाहरी रंग मिलान के लिए उपयोग किया जाता है।
    • यदि कोई मेल होता है, तो बाहरी रंग गायब हो जाता है और फूल अब एक ही रंग का फूल होता है जिसमें आंतरिक फूल होता है
  • खेल का लक्ष्य एक ही रंग के तीन (या अधिक) के मैच बनाना है
    • जब एक एकल रंग का फूल एक मैच का हिस्सा होता है, तो इसे खाली जगह बनाते हुए, खेल के मैदान से हटा दिया जाता है
    • आप एक दो रंग के फूल के बाहरी रंग के खिलाफ एक ही रंग के फूल से मेल खा सकते हैं। इस मामले में, एकल रंग का फूल गायब हो जाता है, बाहरी रंग का दो रंगों का फूल गायब हो जाता है और आंतरिक रंग बना रहता है
  • आप तब जीतते हैं जब कतार खाली होती है और कम से कम एक खाली जगह बची होती है
  • कैस्केडिंग मैच संभव हैं। एक झरना तब होता है जब तीन (या अधिक) बाहरी फूल गायब हो जाते हैं, और जब उनके आंतरिक रंग 3 (या अधिक फूल) की एक और श्रृंखला बनाते हैं।
  • खेल का मैदान हमेशा 7x7 होता है
  • मैदान के कुछ स्थान चट्टानों से ढके हुए हैं
    • तुम चट्टानों पर फूल नहीं रख सकते
  • कतार में एक कुदाल भी हो सकती है, जिसका उपयोग आप किसी भी रखे हुए फूल को किसी खाली जगह पर ले जाने के लिए कर सकते हैं
    • आपको कुदाल का उपयोग करना है, लेकिन आपको वास्तव में फूल को हिलाने की ज़रूरत नहीं है: यह पूरी तरह से कानूनी है कि इसे सही जगह पर रखें जहां से यह आया
  • कतार में रंगीन तितली भी हो सकती है। जब आप एक फूल पर इस तितली का उपयोग करते हैं, तो फूल को तितली का रंग मिलता है
    • एक तितली को दो रंगों के साथ एक फूल पर लगाने से फूल को केवल एक ही रंग मिलता है, अर्थात् तितली
    • आप एक खाली जगह या एक फूल पर तितली को बर्बाद कर सकते हैं जिसमें पहले से ही यह रंग है
  • मैदान साफ़ करने से खेल नहीं जीता जाता

सॉल्वर का लक्ष्य सरल है: कतार को खाली करने का एक तरीका ढूंढें, जितना संभव हो खेल मैदान पर कई बचे हुए स्थान। मूल रूप से, AI मेरे लिए खेल खेलता है। सॉल्वर का आउटपुट एक सूची है जिसमें यह पाया जाता है। मुझे स्कोर में दिलचस्पी नहीं है, लेकिन जब तक संभव हो, तब तक जीवित रहने में, इसलिए मैं उन चालों में दिलचस्पी रखता हूं जो संभव के रूप में कई खुली जगहों को छोड़ देता है।

कहने की जरूरत नहीं है, खोज स्थान जल्दी से बढ़ता है जितनी बड़ी कतार मिलती है, इसलिए एक क्रूर बल प्रश्न से बाहर है। कतार 15 से शुरू होती है, और यदि मुझे सही याद है तो 5 हर दो या तीन स्तरों के साथ बढ़ती है। और, निश्चित रूप से, पहला फूल (0,0) पर रखना और दूसरा (0,1) पर पहला फूल (1,0) और दूसरा फूल (0,0) पर रखने से अलग है, खासकर जब मैदान पहले से ही पहले दौर के फूलों से आबाद है। इस तरह के एक साधारण निर्णय इसे बनाने या न करने में अंतर कर सकते थे।

मेरे पास निम्नलिखित प्रश्न हैं:

  • यह कैसी समस्या है? (थिंक ट्रैवलिंग सेल्समैन, नैकपैक, या कुछ अन्य कॉम्बिनेटरियल समस्या)। यह जानने के बाद मेरे Google-fu को और बेहतर बनाया जा सकता है।
  • किस तरह का एल्गोरिथ्म मुझे अच्छे परिणाम दे सकता है, तेज?

उत्तरार्द्ध के बारे में: सबसे पहले, मैंने अपनी खुद की हेरास्टिक एल्गोरिथ्म लिखने की कोशिश की (मूल रूप से: मैं इसे कैसे हल करूंगा, अगर मुझे पता था कि कतार?), लेकिन इसके परिणामस्वरूप बहुत सारे किनारे मामले और स्कोरिंग मिलान होते हैं जो मुझे याद हो सकते हैं।

मैं एक आनुवंशिक एल्गोरिथ्म का उपयोग करने के बारे में सोच रहा था (क्योंकि मुझे कम से कम पता है कि इसका उपयोग कैसे करना है ...), लेकिन मुझे बोर्ड के द्विआधारी प्रतिनिधित्व पर निर्णय लेने में कुछ समस्याएं हो रही हैं। फिर क्रॉसओवर मुद्दा है, लेकिन इसे एक ऑर्डर किए गए क्रॉसओवर ऑपरेटर या इसी तरह के ऑपरेशन के साथ हल किया जा सकता है।

मेरा अनुमान है कि सॉल्वर को हमेशा बोर्ड कॉन्फ़िगरेशन और खाली करने की कोशिश कर रही कतार को जानना चाहिए।

मैं कुछ अन्य अनुमानी एल्गोरिदम जैसे कि तंत्रिका नेटवर्क और फ़ज़ी लॉजिक सिस्टम के बारे में जानता हूं, लेकिन मुझे यह जानने के लिए अनुभव की कमी है कि कौन सा सबसे अच्छा लागू होता है, या यदि कोई अन्य है जो हाथ में कार्य के लिए बेहतर अनुकूल है।


मैंने एक बार काम किया था कि मैं जिस जटिल खेल में काम कर रहा था उसका खोज स्थान 32 जीबी होगा। उस समय (मेरे पास 20Mb डिस्क ड्राइव था) जो कि अक्षम्य था, लेकिन इन दिनों यह कुछ कंप्यूटरों के लिए रैम में ही संभव है।
जोनाथन

क्या केवल एक रंग के साथ फूल पूरी तरह से गायब हो जाते हैं? और दो रंगों के साथ फूल एक रंग के फूल के एकल रंग के खिलाफ उनकी बाहरी परत से मेल खा सकते हैं? मुझे लगता है कि दोनों मायने रखता है, लेकिन ये स्पष्ट रूप से समस्या वर्णन में निर्दिष्ट नहीं हैं ...
स्टीवन स्टैडनिक

@StevenStadnicki धन्यवाद! मैंने उस जानकारी को मूल प्रश्न में जोड़ दिया है।
user849924

1
एक छोटे नोट के रूप में, संयोग से, यह अत्यधिक संभावना है कि इस समस्या का 'बूलियन' संस्करण है (क्या बोर्ड को अंत में पूरी तरह से खाली छोड़ने के लिए कतार में फूलों को रखने का कोई तरीका है?) एनपी-पूर्ण है; यह क्लिकोमेनिया समस्या ( erikdemaine.org/clickomania ) की स्पष्ट समानता को दर्शाता है, जो NP-पूर्ण है, और समस्या NP की तुलना में अधिक कठिन नहीं है क्योंकि एक कथित समाधान (बहुपद की लंबाई) को देखते हुए इसे केवल सिमुलेशन चलाने से सत्यापित करना आसान है। इसका मतलब यह है कि अनुकूलन समस्या एफपी ^ एनपी में है।
स्टीवन स्टैडनिक

जवाबों:


9

पहली नज़र में , यह मुझे एकल एजेंट खोज समस्या लगता है । वह है: आपके पास एक एजेंट (एआई "खिलाड़ी") है। गेम बोर्ड और कतार की स्थिति का प्रतिनिधित्व करने वाला एक गेम स्टेट है , और आपके पास एक उत्तराधिकारी फ़ंक्शन है जो किसी दिए गए राज्य से नए राज्य उत्पन्न कर सकता है।

एक लक्ष्य मानदंड भी है जो आपको बताता है कि राज्य "हल" स्थिति है। और एक पथ लागत - किसी दिए गए राज्य को आगे बढ़ाने की लागत (इस मामले में हमेशा "1 चाल")।

इस तरह की एक प्रोटोटाइप पहेली 15 पहेली है । और इसे हल करने का विशिष्ट तरीका एक सूचित खोज के साथ है - उदाहरण के लिए, क्लासिक अनुमानी खोज ए * और इसके वेरिएंट।


हालाँकि, इस पर पहली नज़र में समस्या है। ए * जैसे एल्गोरिदम आपको एक लक्ष्य के लिए सबसे छोटा रास्ता देने के लिए डिज़ाइन किया गया है (उदाहरण के लिए: सबसे छोटी संख्या की चाल)। आपके मामले में, चाल की संख्या हमेशा तय हो गई है - कोई कम से कम मार्ग है - तो एक अनुमानी खोज केवल आपके दे देंगे एक करने के लिए पथ एक पूरा खेल।

आप जो चाहते हैं वह चालों का एक क्रम है जो आपको सबसे अच्छा गेम स्टेट देता है।

तो आपको जो करना चाहिए, वह समस्या को थोड़ा बढ़ा देता है। खेल बोर्ड के "राज्य" होने के बजाय, चालों का क्रम "राज्य" बन जाता है। (यानी: आइटम को "D2, A5, C7, B3, A3, ..." पर कतार में रखें

इसका मतलब है कि हम वास्तव में परवाह नहीं करते हैं कि उन राज्यों को कैसे उत्पन्न किया जाता है। बोर्ड स्वयं आकस्मिक है, केवल किसी दिए गए राज्य की गुणवत्ता का मूल्यांकन करने के लिए आवश्यक है।

यह समस्या को एक अनुकूलन समस्या में बदल देता है , जिसे स्थानीय खोज एल्गोरिदम के साथ हल किया जा सकता है (जिसका मूल अर्थ है किसी दिए गए राज्य के चारों ओर राज्य बनाना और राज्यों के बीच के मार्ग की परवाह किए बिना, सर्वश्रेष्ठ राज्य का चयन करना।)

इस तरह की प्रोटोटाइप पहेली आठ क्वींस पहेली है

समस्या की इस श्रेणी में, आप एक अच्छा समाधान खोजने के लिए राज्य स्थान की खोज कर रहे हैं, जहां "अच्छा" का मूल्यांकन एक उद्देश्य फ़ंक्शन (जिसे मूल्यांकन फ़ंक्शन भी कहा जाता है , या आनुवांशिक एल्गोरिदम, एक फिटनेस फ़ंक्शन ) द्वारा किया जाता है।

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

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


स्थानीय खोज एल्गोरिदम के उदाहरणों के लिए : मूल पैटर्न एक पहाड़ी पर चढ़ने वाली खोज है जो किसी दिए गए राज्य को लेती है, इसे उत्परिवर्तित करती है, और अगले राज्य की ओर बढ़ती है जो बेहतर परिणाम देता है।

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

इस पर एक विशेष भिन्नता आनुवंशिक एल्गोरिथम ( विकिपीडिया ) है। एक आनुवंशिक एल्गोरिथ्म के लिए बुनियादी कदम हैं:

  1. राज्य को किसी प्रकार की स्ट्रिंग में बदलने का कोई तरीका निर्धारित करें। आपके मामले में यह 1 से 49 तक कतार-लंबाई अंकों की एक स्ट्रिंग हो सकती है (7x7 बोर्ड पर सभी संभावित प्लेसमेंट का प्रतिनिधित्व करते हुए, संभवतः प्रत्येक 1 बाइट संग्रहीत)। (आपके "कुदाल" टुकड़े को बाद के प्रत्येक चरण के लिए दो बाद की कतार प्रविष्टियों द्वारा दर्शाया जा सकता है।)
  2. रैंडम तरीके से प्रजनन करने वाली आबादी का चयन करें, जो बेहतर फिटनेस वाले राज्यों को अधिक संभावना दे । प्रजनन आबादी मूल जनसंख्या के समान आकार होनी चाहिए - आप कई बार मूल आबादी से राज्यों को चुन सकते हैं।
  3. प्रजनन की आबादी में राज्यों को जोड़ा जाता है (पहले दूसरे के साथ जाता है, तीसरा चौथे के साथ जाता है, आदि)।
  4. प्रत्येक जोड़ी (स्ट्रिंग में एक स्थिति) के लिए यादृच्छिक रूप से क्रॉसओवर बिंदुओं का चयन करें ।
  5. क्रॉसओवर बिंदु के बाद स्ट्रिंग के हिस्से को स्वैप करके प्रत्येक जोड़ी के लिए दो संतान बनाएं।
  6. प्रत्येक वंश को बेतरतीब ढंग से उत्परिवर्तित करते हैं। उदाहरण के लिए: बेतरतीब ढंग से स्ट्रिंग में एक यादृच्छिक स्थिति को यादृच्छिक मान में बदलने के लिए चुनें।
  7. नई आबादी के साथ प्रक्रिया को दोहराएं जब तक कि आबादी एक या एक से अधिक समाधानों पर (या दी गई कई पीढ़ियों के बाद, या एक पर्याप्त रूप से अच्छा समाधान नहीं मिल जाता)।

एक आनुवंशिक एल्गोरिथ्म समाधान ऐसा महसूस करता है कि यह आपकी समस्या के लिए उपयुक्त हो सकता है - कुछ समायोजन के साथ। मुझे सबसे बड़ी कठिनाई यह दिखाई देती है कि, उपरोक्त स्ट्रिंग प्रतिनिधित्व के साथ, आप पाएंगे कि बहुत अलग मोर्चे वाले राज्यों के पूंछ हिस्सों को स्विच करने से "मृत" अवस्थाओं में परिणाम होने की संभावना है (दो हिस्सों के बीच परस्पर विरोधी चाल के कारण, वह परिणाम कम फिटनेस स्कोर में)।

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

"स्क्वायर पूर्ण है" विफलता स्थिति का सामना करने के लिए अधिक प्रतिरोधी (शायद पूरी तरह से प्रतिरक्षा) के भीतर चालों के प्रतिनिधित्व के साथ आना भी संभव हो सकता है। शायद चाल के रूप में प्रतिनिधित्व पिछले चाल से निर्देशांक। या चाल चलने से दी गई स्थिति के लिए निकटतम खाली स्थान का चयन करें।

इस तरह से सभी गैर-तुच्छ एआई समस्याओं के साथ, इसे कुछ महत्वपूर्ण छेड़छाड़ की आवश्यकता होगी।

और, जैसा कि मैंने पहले उल्लेख किया है, दूसरी बड़ी चुनौती केवल आपके उद्देश्य समारोह का अनुकूलन है। इस तेजी से बनाने से आप बड़ी मात्रा में स्थान खोज सकते हैं, और लंबी कतारों वाले खेलों के समाधान की खोज कर सकते हैं।


इस उत्तर के लिए, विशेष रूप से सभी शब्दावली को ठीक से प्राप्त करने के लिए, मुझे रसेल और नॉर्विग द्वारा अपने विश्वविद्यालय एआई की पाठ्यपुस्तक, "आर्टिफिशियल इंटेलिजेंस: ए मॉडर्न अप्रोच" को खोदना पड़ा। निश्चित नहीं है कि यह "अच्छा" है (मेरे पास इसकी तुलना करने के लिए कोई अन्य AI पाठ नहीं है), लेकिन यह बुरा नहीं है। कम से कम यह काफी बड़ा है;)


मैंने उस समस्या को एक क्रॉसओवर के साथ भी पहचाना: यह बहुत अच्छी तरह से संभव है कि बच्चे के पास कतार में उपलब्ध वस्तुओं की तुलना में अधिक आइटम हों (टीएसपी के लिए जीए की कमी: वह दो बार या अधिक शहरों में जा सकता है (या बिल्कुल नहीं!) एक के बाद क्रॉसओवर। हो सकता है कि एक आदेशित क्रॉसओवर ( permutationcity.co.uk/projects/mutants/tsp ) काम कर सके। यह विशेष रूप से तब लागू होता है जब आप राज्य को स्थानांतरित करने का क्रम बनाते हैं।
user849924

यह सुनिश्चित नहीं है कि मेरे दिमाग में यह बिल्कुल सही है - विफलता की स्थिति यह है कि एक टुकड़ा एक ऐसी स्थिति में रखा गया है जो पहले से ही कब्जा कर लिया गया है (इस तरह उस खेल को जल्दी समाप्त कर दिया जाता है, जिसके परिणामस्वरूप कम फिटनेस स्कोर होता है)। तो कतार की लंबाई आनुवंशिक स्ट्रिंग की लंबाई से मेल खाती है - यह कभी गलत लंबाई नहीं है। फिर भी - आप स्वैपिंग और ऑर्डर करने के विचार के साथ कुछ पर हो सकते हैं। यदि दिए गए आदेश में एक पूरा खेल होता है, और आप दो चालों की अदला-बदली करते हैं, तो मैं कल्पना करता हूं कि उत्परिवर्तित राज्य का एक बहुत अच्छा मौका भी है जब आप केवल एक (या दो?) की चाल को बेतरतीब ढंग से सेट करने की तुलना में एक पूर्ण खेल हो सकते हैं? ।
एंड्रयू रसेल

विफलता की स्थिति तब होती है जब आपके पास चालें रखने के लिए और कोई विकल्प नहीं होता है, अर्थात जब आप खाली स्थानों से बाहर निकलते हैं और उस चाल के साथ कोई मैच नहीं होता है। जैसा आप कह रहे हैं वैसा ही: आपको इसे एक ऐसी स्थिति पर रखना होगा जो पहले से ही कब्जे में है (लेकिन यह केवल तभी सच है जब शुरू करने के लिए अधिक स्थान नहीं हैं)। मेरे द्वारा पोस्ट किया गया क्रॉसओवर दिलचस्प हो सकता है। गुणसूत्र A में A1, B1, ..., G1, A2, B2 और C2 और गुणसूत्र B पर G7 ... A7, G6, F6 और E6 हैं। ए से कुछ रैंडम का चयन करें और उनके सूचकांक को रखें। बी से ए के पूरक का चयन करें और उनके सूचकांक को रखें और एक बच्चे के लिए विलय करें।
user849924

इस क्रॉसओवर के साथ 'समस्या' यह है कि एक ही स्थान पर कई चालों की अनुमति है। लेकिन यह आसानी से Stefan K के समाधान से SimulateAutomaticChanges के समान कुछ के साथ सॉल्व होना चाहिए: बच्चे के मूवमेंट / स्टेट को बेस स्टेट पर लागू करें (केवल सभी मूव्स, एक-एक करके सभी) प्लेइंग फील्ड के लिए और यदि एक्सेप्टेंस स्टेट (खाली कतार) ) प्राप्त नहीं किया जा सकता (क्योंकि आपको एक जगह पर एक फूल रखना है), फिर बच्चा अमान्य है और हमें फिर से प्रजनन करना होगा। यहां पर आपकी विफलता की स्थिति पॉप अप होती है। मुझे वह अब मिल गया है, हे। : D
user849924

मैं इसे दो कारणों से उत्तर के रूप में स्वीकार कर रहा हूं। पहला: आपने मुझे इस समस्या के लिए काम करने के लिए जीए प्राप्त करने के लिए आवश्यक विचार दिया। दूसरा: आप पहले थे। ; पी
user849924

2

वर्गीकरण

उत्तर आसान नहीं है। गेम थ्योरी में गेम के लिए कुछ वर्गीकरण हैं, लेकिन लगता है कि उस गेम के लिए कोई विशेष सिद्धांत 1: 1-मैच नहीं है। यह कॉम्बिनेटरियल समस्या का एक विशेष रूप है।

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

Knapsack मेल नहीं खाता क्योंकि कुछ आइटम "knapsack" में डालते समय कुछ फ़ील्ड खाली हो जाते हैं। तो यह शायद उस का कुछ विस्तारित रूप है, लेकिन संभवत: एल्गोरिदम इस वजह से लागू नहीं होगा।

विकिपीडिया यहाँ वर्गीकरण पर कुछ संकेत देता है: http://en.wikipedia.org/wiki/Game_theory#Types_of_games

मैं इसे "असतत-समय इष्टतम नियंत्रण समस्या" ( http://en.wikipedia.org/wiki/Optimal_control ) के रूप में वर्गीकृत करूंगा , लेकिन मुझे नहीं लगता कि इससे आपको मदद मिलेगी।

एल्गोरिदम

यदि आप वास्तव में पूरी कतार जानते हैं, तो आप पेड़ खोज एल्गोरिदम लागू कर सकते हैं। जैसा कि आपने कहा, कतार की लंबाई के साथ समस्या की जटिलता बहुत तेजी से बढ़ती है। मेरा सुझाव है कि "डेप्थ-फर्स्ट सर्च (डीएफएस)" जैसे एल्गोरिथ्म का उपयोग करें, जिसमें बहुत अधिक मेमोरी की आवश्यकता नहीं है। जैसा कि स्कोर आपके लिए कोई मायने नहीं रखता है, आप पहले समाधान खोजने के बाद बस रोक सकते हैं। यह तय करने के लिए कि किस उप-शाखा को पहले खोजना है, आपको आदेश देने के लिए एक अनुमान लागू करना चाहिए। इसका मतलब है कि आपको एक मूल्यांकन फ़ंक्शन लिखना चाहिए (उदाहरण के लिए: खाली फ़ील्ड की संख्या; यह जितना अधिक परिष्कृत होता है, उतना बेहतर), यह तुलना करने के लिए एक अंक देता है कि अगला अगला कदम सबसे आशाजनक है।

आपको केवल निम्नलिखित भागों की आवश्यकता है:

  1. खेल राज्य का मॉडल, जो खेल की सभी जानकारी संग्रहीत करता है (जैसे बोर्ड की स्थिति / नक्शा, कतार, संख्या / कतार में स्थिति)
  2. एक चाल जनरेटर, जो आपको दिए गए खेल राज्य के लिए सभी वैध चाल देता है
  3. "डू मूव" और "अनडू मूव" फंक्शन; जो खेल राज्य में दिए गए (वैध) कदम को लागू / पूर्ववत करता है। जबकि "डू मूव" फ़ंक्शन को "पूर्ववत करें" फ़ंक्शन के लिए कुछ "पूर्ववत जानकारी" संग्रहीत करनी चाहिए। खेल की स्थिति की नकल करना और प्रत्येक पुनरावृत्ति में इसे संशोधित करना खोज को काफी धीमा कर देता है! कम से कम स्टैक पर राज्य को स्टोर करने का प्रयास करें (= "नया" का उपयोग करके कोई गतिशील आवंटन नहीं)।
  4. एक मूल्यांकन समारोह, जो प्रत्येक खेल राज्य के लिए एक तुलनीय स्कोर देता है
  5. खोज समारोह

यहां गहराई-पहली खोज के लिए एक अपूर्ण संदर्भ कार्यान्वयन है:

public class Item
{
    // TODO... represents queue items (FLOWER, SHOVEL, BUTTERFLY)
}

public class Field
{
    // TODO... represents field on the board (EMPTY or FLOWER)
}

public class Modification {
    int x, y;
    Field originalValue, newValue;

    public Modification(int x, int y, Field originalValue, newValue) {
        this.x = x;
        this.y = y;
        this.originalValue = originalValue;
        this.newValue = newValue;
    }

    public void Do(GameState state) {
        state.board[x,y] = newValue;
    }

    public void Undo(GameState state) {
        state.board[x,y] = originalValue;
    }
}

class Move : ICompareable {

    // score; from evaluation function
    public int score; 

    // List of modifications to do/undo to execute the move or to undo it
    Modification[] modifications;

    // Information for later knowing, what "control" action has been chosen
    public int x, y;   // target field chosen
    public int x2, y2; // secondary target field chosen (e.g. if moving a field)


    public Move(GameState state, Modification[] modifications, int score, int x, int y, int x2 = -1, int y2 = -1) {
        this.modifications = modifications;
        this.score = score;
        this.x = x;
        this.y = y;
        this.x2 = x2;
        this.y2 = y2;
    }

    public int CompareTo(Move other)
    {
        return other.score - this.score; // less than 0, if "this" precededs "other"...
    }

    public virtual void Do(GameState state)
    {
        foreach(Modification m in modifications) m.Do(state);
        state.queueindex++;
    }

    public virtual void Undo(GameState state)
    {
        --state.queueindex;
        for (int i = m.length - 1; i >= 0; --i) m.Undo(state); // undo modification in reversed order
    }
}

class GameState {
    public Item[] queue;
    public Field[][] board;
    public int queueindex;

    public GameState(Field[][] board, Item[] queue) {
        this.board = board;
        this.queue = queue;
        this.queueindex = 0;
    }

    private int Evaluate()
    {
        int value = 0;
        // TODO: Calculate some reasonable value for the game state...

        return value;
    }

    private List<Modification> SimulateAutomaticChanges(ref int score) {
        List<Modification> modifications = new List<Modification>();
        // TODO: estimate all "remove" flowers or recoler them according to game rules 
        // and store all changes into modifications...
        if (modifications.Count() > 0) {
            foreach(Modification modification in modifications) modification.Do(this);

            // Recursively call this function, for cases of chain reactions...
            List<Modification> moreModifications = SimulateAutomaticChanges();

            foreach(Modification modification in modifications) modification.Undo(this);

            // Add recursively generated moves...
            modifications.AddRange(moreModifications);
        } else {
            score = Evaluate();
        }

        return modifications;
    }

    // Helper function for move generator...
    private void MoveListAdd(List<Move> movelist, List<Modifications> modifications, int x, int y, int x2 = -1, int y2 = -1) {
        foreach(Modification modification in modifications) modification.Do(this);

        int score;
        List<Modification> autoChanges = SimulateAutomaticChanges(score);

        foreach(Modification modification in modifications) modification.Undo(this);

        modifications.AddRange(autoChanges);

        movelist.Add(new Move(this, modifications, score, x, y, x2, y2));
    }


    private List<Move> getValidMoves() {
        List<Move> movelist = new List<Move>();
        Item nextItem = queue[queueindex];
        const int MAX = board.length * board[0].length + 2;

        if (nextItem.ItemType == Item.SHOVEL)
        {

            for (int x = 0; x < board.length; ++x)
            {
                for (int y = 0; y < board[x].length; ++y)
                {
                    // TODO: Check if valid, else "continue;"

                    for (int x2 = 0; x2 < board.length; ++x2)
                    {
                        for(int y2 = 0; y2 < board[x].length; ++y2) {
                            List<Modifications> modifications = new List<Modifications>();

                            Item fromItem = board[x][y];
                            Item toItem = board[x2][y2];
                            modifications.Add(new Modification(x, y, fromItem, Item.NONE));
                            modifications.Add(new Modification(x2, y2, toItem, fromItem));

                            MoveListAdd(movelist, modifications, x, y, x2, y2);
                        }
                    }
                }
            }

        } else {

            for (int x = 0; x < board.length; ++x)
            {
                for (int y = 0; y < board[x].length; ++y)
                {
                    // TODO: check if nextItem may be applied here... if not "continue;"

                    List<Modifications> modifications = new List<Modifications>();
                    if (nextItem.ItemType == Item.FLOWER) {
                        // TODO: generate modifications for putting flower at x,y
                    } else {
                        // TODO: generate modifications for putting butterfly "nextItem" at x,y
                    }

                    MoveListAdd(movelist, modifications, x, y);
                }
            }
        }

        // Sort movelist...
        movelist.Sort();

        return movelist;
    }


    public List<Move> Search()
    {
        List<Move> validmoves = getValidMoves();

        foreach(Move move in validmoves) {
            move.Do(this);
            List<Move> solution = Search();
            if (solution != null)
            {
                solution.Prepend(move);
                return solution;
            }
            move.Undo(this);
        }

        // return "null" as no solution was found in this branch...
        // this will also happen if validmoves == empty (e.g. lost game)
        return null;
    }
}

यह कोड काम करने के लिए सत्यापित नहीं है, न ही यह संकलन योग्य या पूर्ण है। लेकिन यह आपको एक विचार देना चाहिए कि यह कैसे करना है। सबसे महत्वपूर्ण कार्य मूल्यांकन कार्य है। यह जितना अधिक परिष्कृत है, गलत "एल्गोरिथ्म" बाद में करने की कोशिश करेगा (और पूर्ववत करना होगा)। यह जटिलता को कम करता है।

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

मुझे लगता है कि इस समस्या को हल करने के लिए कोई अन्य एल्गोरिदम नहीं है (जैसा कि आपके द्वारा वर्णित है) अधिक कुशल, मान लिया कि आपका मूल्यांकन कार्य इष्टतम है ...


हां, मैं पूरी कतार जान सकता हूं। क्या मूल्यांकन समारोह का कार्यान्वयन भी मान्य होगा, लेकिन संभावित रूप से खराब प्लेसमेंट? संभावित रूप से बुरा एक चाल है जैसे इसे एक अलग रंग के फूल के बगल में रखना जब मैदान पर पहले से ही समान रंग होता है? या कहीं एक फूल रखकर जो जगह की कमी के कारण एक पूरी तरह से अलग मैच के ब्लॉक करता है?
user849924

इस जवाब ने मुझे मॉडल के लिए विचार दिए और खेल के नियमों के साथ कैसे काम करना है, इसलिए मैं इसे बढ़ा दूंगा। आपके सहयोग के लिए धन्यवाद!
user849924

@ user849924: हां, निश्चित रूप से मूल्यांकन फ़ंक्शन को उसके लिए मूल्यांकन "मूल्य" की गणना करनी चाहिए। जितना अधिक वर्तमान खेल की स्थिति खराब होती है (हारने के करीब), उतना ही खराब मूल्यांकन मूल्य होना चाहिए। सबसे आसान मूल्यांकन खाली खेतों की संख्या वापस करना होगा। आप इसी तरह के रंग के फूल के बगल में रखे प्रत्येक फूल के लिए 0.1 जोड़कर इसमें सुधार कर सकते हैं। अपने फ़ंक्शन को सत्यापित करने के लिए कुछ यादृच्छिक गेम स्टेट्स चुनें, उनके मूल्य की गणना करें और उनकी तुलना करें। अगर आपको लगता है कि राज्य A राज्य B से बेहतर है, तो स्कोर का अंक A, B. के लिए एक से बेहतर होना चाहिए
SDwarfs
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.