कार्यात्मक भाषाओं में दो-आयामी बोर्ड गेम के लिए डेटा संरचना


9

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

हालाँकि, मैं एक समस्या का सामना कर रहा हूँ, वह यह है कि किसी कार्यात्मक अवस्था में गेम स्टेट को ठीक से कैसे स्टोर किया जाए। ये गेम ज्यादातर दो-आयामी गेम बोर्ड के साथ काम कर रहे हैं, जहां निम्नलिखित ऑपरेशन अक्सर होते हैं:

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

अधिकांश कार्यात्मक भाषाएँ लिंक्ड सूचियों और टुपल्स का उपयोग बहु-तत्व डेटा संरचनाओं के बुनियादी भवन ब्लॉकों के रूप में करती हैं। हालांकि, ये काम के लिए बहुत बुरी तरह से किए गए लगते हैं:

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

अमृत ​​में एक अंतर्निहित मानचित्र डेटा संरचना (और हास्केल है Data.Map) जो ओ (लॉग एन) (लॉगरिदमिक) तत्वों तक पहुंच की अनुमति देता है। अभी मैं एक नक्शे का उपयोग करता हूं, x, yटुपल्स के साथ जो कुंजियों के रूप में स्थिति का प्रतिनिधित्व करता है।

यह 'काम करता है' लेकिन इस तरह से नक्शों का दुरुपयोग करना गलत लगता है, हालाँकि मुझे ठीक-ठीक पता नहीं है। मैं एक कार्यात्मक प्रोग्रामिंग भाषा में दो-आयामी गेम बोर्ड को स्टोर करने का एक बेहतर तरीका ढूंढ रहा हूं।


1
मैं प्रैक्सिस के बारे में नहीं बोल सकता, लेकिन हास्केल से दो बातें मेरे दिमाग में आती हैं: जिपर , डेटा संरचनाओं पर लगातार समय "कदम" की अनुमति देना, और कोमोनॉड्स, जो कुछ सिद्धांत द्वारा ज़िपर से संबंधित हैं जो मुझे न तो याद है और न ही ठीक से समझ में आता है; )
phipsgabler

यह प्लेइंग बोर्ड कितना बड़ा है? बिग ओ की विशेषता है कि एक एल्गोरिथ्म कैसे मापता है, न कि यह कितना तेज़ है। एक छोटे बोर्ड पर (कहते हैं, प्रत्येक दिशा में 100 से कम), ओ (1) बनाम ओ (एन) बहुत ज्यादा मायने नहीं रखता है, यदि आप केवल प्रत्येक वर्ग को एक बार छूते हैं।
रॉबर्ट हार्वे

@ रोबर्टहवे यह अलग-अलग होगा। लेकिन एक उदाहरण देने के लिए: शतरंज में, हमारे पास एक 64x64 बोर्ड है, लेकिन सभी चालन के लिए जाँच करने के लिए कि क्या चालें संभव हैं, और वर्तमान स्थिति के आनुमानिक मूल्य (सामग्री में अंतर, राजा की जाँच या नहीं, पारित पंजे, आदि) का निर्धारण करने के लिए सभी को बोर्ड के वर्गों तक पहुंचने की आवश्यकता है।
Qqwy

1
आपके पास शतरंज में 8x8 का बोर्ड है। C जैसी मेमोरी-मैप की गई भाषा में, आप किसी सेल का सटीक पता प्राप्त करने के लिए एक गणितीय गणना कर सकते हैं, लेकिन यह मेमोरी-प्रबंधित भाषाओं में सच नहीं है (जहां क्रमिक पता एक कार्यान्वयन विवरण है)। यह मुझे आश्चर्यचकित नहीं करेगा अगर एक अधिकतम 14 नोड्स एक स्मृति तत्व प्रबंधित भाषा में सरणी तत्व को संबोधित करने के दौरान (अधिकतम) 14 कूदता है।
रॉबर्ट हार्वे

यह भी देखें stackoverflow.com/q/9611904/124319
coredump

जवाबों:


6

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

filterWithKey (\k _ -> (snd k) == column) -- All pieces in given column
filterWithKey (\k _ -> (fst k) == row)    -- All pieces in given row
mapKeys (\(x, y) -> (-x, y))              -- Mirror

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

इसका मतलब है कि आप Mapसबसे ऊपरी स्तर पर उपयोग कर सकते हैं , फिर पंक्ति विश्लेषण के लिए Listsया उसके बाद स्विच कर सकते हैं , फिर स्तंभ विश्लेषण के लिए दूसरे तरीके को अनुक्रमित किया जा सकता है, फिर पैटर्न विश्लेषण के लिए बिटमास्क, फिर प्रदर्शन के लिए। अच्छी तरह से डिज़ाइन किए गए कार्यात्मक कार्यक्रम एक भी डेटा संरचना को पास नहीं करते हैं। वे ऐसे चरणों की एक श्रृंखला है जो एक डेटा संरचना को लेते हैं और एक नए का उत्सर्जन करते हैं जो अगले चरण के लिए अनुकूल है।ArraysArraysStrings

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


4

मैंने हाल ही में एफ # में यह किया है और मैंने एक-आयामी सूची (एफ # में, एक एकल-लिंक सूची का उपयोग करके समाप्त किया है)। व्यवहार में, O (n) सूची अनुक्रमणिका की गति मानव-प्रयोग करने योग्य बोर्ड आकारों के लिए अड़चन नहीं है। मैंने 2d सरणी जैसे अन्य प्रकारों के साथ प्रयोग किया, लेकिन अंत में, यह मेरी वैल्यू-इक्वैलिटी-चेकिंग कोड लिखने या रैंक और फ़ाइल का इंडेक्स और बैक में अनुवाद करने का ट्रेड-ऑफ था। उत्तरार्द्ध सरल था। मैं कहता हूं कि इसे पहले काम करना चाहिए, और फिर बाद में यदि आवश्यक हो तो अपने डेटा प्रकार का अनुकूलन करें। यह बात करने के लिए एक बड़ा पर्याप्त अंतर बनाने की संभावना नहीं है।

अंत में आपके क्रियान्वयन में उतनी देर नहीं होनी चाहिए जब तक कि आपके बोर्ड संचालन को बोर्ड प्रकार और संचालन द्वारा ठीक से समझाया नहीं जाता है। उदाहरण के लिए, मेरे बोर्ड की स्थापना के लिए मेरे कुछ परीक्षण इस प्रकार हो सकते हैं:

let pos r f = {Rank = r; File = f} // immutable record type
// or
let pos r f = OnBoard (r, f) // algebraic type
...
let testBoard =
    Board.createEmpty ()
    |> Board.setPiece p (pos 1 2)
    |> ...

इस कोड (या किसी भी कॉलिंग कोड) के लिए, यह मायने नहीं रखता कि बोर्ड का प्रतिनिधित्व कैसे किया गया। बोर्ड को इसकी अंतर्निहित संरचना से अधिक इस पर परिचालन द्वारा दर्शाया गया है।


नमस्कार, क्या आपके पास कोड कहीं प्रकाशित है? मैं वर्तमान में F # में एक शतरंज जैसे गेम पर काम कर रहा हूं (मजेदार प्रोजेक्ट के लिए), और यहां तक ​​कि मैं बोर्ड का प्रतिनिधित्व करने के लिए एक मैप <स्क्वायर, पीस> का उपयोग कर रहा हूं, मुझे यह देखना अच्छा लगेगा कि आपने इसे कैसे एक बोर्ड में शामिल किया प्रकार और मॉड्यूल।
असीबाही

नहीं, यह कहीं भी प्रकाशित नहीं है।
कासे स्पीकमैन

तो क्या आप मेरे वर्तमान कार्यान्वयन पर एक नज़र डालेंगे और सलाह देंगे कि मैं इसे कैसे सुधार सकता हूं?
असीबाही

प्रकारों को देखते हुए, मैं तब तक उसी तरह के कार्यान्वयन पर बस गया जब तक कि मैं स्थिति के प्रकार से नहीं मिला। मैं बाद में कोड की समीक्षा पर अधिक गहन विश्लेषण करूंगा।
केसी स्पीकमैन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.