2 डी मानचित्र पर "मुख्य भूमि" का पता लगाने के लिए एक एल्गोरिथ्म है?


28

इस मानचित्र पर, "मुख्य भूमि" वह सारी भूमि है जिसे चार कार्डिनल दिशाओं (उत्तर, दक्षिण, पूर्व, पश्चिम - तिरछे नहीं) में मानचित्र के केंद्र से जोड़ा जा सकता है। यहाँ छवि विवरण दर्ज करें

मैं मुख्य भूमि का पता लगाना चाहता हूं और उसमें छेद भरना चाहता हूं। मैंने तीन चीजों के बारे में सोचा:

  1. यदि पथ खोज एल्गोरिथ्म का उपयोग करके मानचित्र के केंद्र से जोड़ा जा सकता है, तो प्रत्येक गैर-पानी (अंधेरे कोशिकाओं) सेल में खोजें। बहुत महंगा! लेकिन यह द्वीपों के लिए काम कर सकता है।

  2. मुख्य भूमि हरे रंग की बाल्टी से भरी हुई है। प्रत्येक छेद पेंट से घिरा हुआ है ... अब क्या? अगर मैं आसन्न के लिए मुख्य भूमि के अंदर हर पानी बिंदु की जाँच करता हूँ तो मैं कुछ प्रायद्वीपों और अन्य भौगोलिक विशेषताओं को हटाता हूँ जो तटरेखा पर प्रदर्शित होती हैं।

  3. मुख्य भूमि का पता लगाने के लिए किसी प्रकार की बढ़त का पता लगाना। जो भी अंदर है उसे भरकर रखें, अगर पानी है तो उसे बाहर निकाल दें। परिसर?

शायद कुछ गेम अनुभवी डेवलपर मुझे इसमें मदद कर सकते हैं, संभवतः मुझे कुछ ज्ञात एल्गोरिदम या तकनीक का नाम दे रहे हैं?


4
मैं सोच रहा हूं कि क्या आपने इस नक्शे को बनाने के लिए किसी तरह के एल्गोरिदम का उपयोग किया है। और यदि हां, तो आपने क्या इस्तेमाल किया?
jgallant

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

@ हाँ! हीरा वर्ग एल्गोरिथ्म एक ऊंचाई का नक्शा बनाने के लिए, तो सभी एक मूल्य पानी, बाकी, भूमि है। मैं इसे महाद्वीप आकार के रूप में उपयोग करने की योजना बनाता हूं, फिर इलाके विस्तार को जोड़ने के लिए एक और पास।
गेब्रियल ए। ज़ोरिल्ला 23

@ माइंड मार्चिंग स्क्वायर एल्गोरिथम महाद्वीप की सीमा को चित्रित करने के लिए काम आएगा। धन्यवाद अच्छा सुझाव!
गेब्रियल ए। ज़ोरिल्ला

जवाबों:


32

द्वीपों को हटाना

मैंने इस तरह का काम अपने एक खेल से पहले किया है। बाहरी द्वीपों से छुटकारा पाने के लिए, प्रक्रिया मूल रूप से थी:

  1. पहले गारंटी होनी चाहिए कि मानचित्र का केंद्र हमेशा मुख्य भूमि से संबंधित होगा, और प्रत्येक पिक्सेल "भूमि" या "जल" (यानी अलग-अलग रंग) के रूप में शुरू होता है।
  2. फिर एक चार दिशा की बाढ़ भरें जो केंद्र से नक्शे से शुरू होकर किसी भी "लैंड" टाइल में फैल जाए। इस बाढ़ से जाने वाले प्रत्येक पिक्सेल को एक अलग प्रकार जैसे "मेनलैंड" के रूप में चिह्नित करें।
  3. अंत में पूरे मानचित्र पर जाएं और अन्य द्वीपों से छुटकारा पाने के लिए किसी भी शेष "लैंड" पिक्सेल को "पानी" में परिवर्तित करें।

झीलों को हटाना

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

उदाहरण

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

private void GenerateSea()
{
    // Initialize visited tiles list
    visited.Clear();

    // Start generating sea from the four corners
    GenerateSeaRecursive(new Point(0, 0));
    GenerateSeaRecursive(new Point(size.Width - 1, 0));
    GenerateSeaRecursive(new Point(0, size.Height - 1));
    GenerateSeaRecursive(new Point(size.Width - 1, size.Height - 1));
}

private void GenerateSeaRecursive(Point point)
{
    // End recursion if point is outside bounds
    if (!WithinBounds(point)) return;

    // End recursion if the current spot is a land
    if (tiles[point.X, point.Y].Land) return;

    // End recursion if this spot has already been visited
    if (visited.Contains(point)) return;

    // Add point to visited points list
    visited.Add(point);

    // Calculate neighboring tiles coordinates
    Point right = new Point(point.X + 1, point.Y);
    Point left = new Point(point.X - 1, point.Y);
    Point up = new Point(point.X, point.Y - 1);
    Point down = new Point(point.X, point.Y + 1);

    // Mark neighbouring tiles as Sea if they're not Land
    if (WithinBounds(right) && tiles[right.X, right.Y].Empty)
        tiles[right.X, right.Y].Sea = true;
    if (WithinBounds(left) && tiles[left.X, left.Y].Empty)
        tiles[left.X, left.Y].Sea = true;
    if (WithinBounds(up) && tiles[up.X, up.Y].Empty)
        tiles[up.X, up.Y].Sea = true;
    if (WithinBounds(down) && tiles[down.X, down.Y].Empty)
        tiles[down.X, down.Y].Sea = true;

    // Call the function recursively for the neighboring tiles
    GenerateSeaRecursive(right);
    GenerateSeaRecursive(left);
    GenerateSeaRecursive(up);
    GenerateSeaRecursive(down);
}

मैंने अपने खेल में झीलों से छुटकारा पाने के लिए पहले कदम के रूप में इसका इस्तेमाल किया। कॉल करने के बाद, मुझे बस इतना करना होगा:

private void RemoveLakes()
{
    // Now that sea is generated, any empty tile should be removed
    for (int j = 0; j != size.Height; j++)
        for (int i = 0; i != size.Width; i++)
            if (tiles[i, j].Empty) tiles[i, j].Land = true;
}

संपादित करें

टिप्पणियों के आधार पर कुछ अतिरिक्त जानकारी जोड़ना। यदि आपका खोज स्थान बहुत बड़ा है, तो एल्गोरिथ्म के पुनरावर्ती संस्करण का उपयोग करने पर आपको स्टैक ओवरफ़्लो का अनुभव हो सकता है। एल्गोरिथ्म के एक गैर पुनरावर्ती संस्करण के लिए स्टैकओवरफ़्लो (दंडित इरादा :-)) पर एक लिंक दिया गया है, Stack<T>इसके बजाय मेरे उत्तर का मिलान करने के लिए (सी # में भी) का उपयोग करते हुए , लेकिन अन्य भाषाओं के लिए अनुकूल होना आसान है, और उस पर अन्य कार्यान्वयन भी हैं लिंक भी)।


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

इनलाइन के साथ जॉर्जडकेट ने क्या कहा - आप गुग्लिंग ब्लॉब डिटेक्शन एल्गोरिदम की कोशिश कर सकते हैं: एफटीआईआर के साथ मल्टी-टच में यह एक आम समस्या है। मुझे याद है कि मैं एक चालाक एल्गोरिथ्म के साथ आया था, लेकिन मुझे मेरे जीवन के लिए याद नहीं है कि यह कैसे काम करता है।
जोनाथन डिकिंसन

जैसा कि मैंने PHP में स्टैक के मुद्दों पर चर्चा की है, मैंने LIFO बाढ़ भरण को लागू किया, अद्भुत रूप से काम किया।
गेब्रियल ए। ज़ोरिल्ला

क्या यह केवल एक बाढ़ भर नहीं है जब बीएफएस एल्गोरिथ्म से डीएफएस एल्गोरिथ्म कहा जाता है? कृपया किसी को समझाएं।
जकोरा

@Bane तुम्हारा क्या मतलब है? डीएफएस या बीएफएस के बीच का अंतर सिर्फ वह क्रम है जिसमें नोड्स का दौरा किया जाता है। मुझे नहीं लगता कि बाढ़ भराव एल्गोरिथ्म ट्रैवर्सल के आदेश को निर्दिष्ट करता है - यह परवाह नहीं करता है जब तक कि यह पूरे क्षेत्र को नोड्स को फिर से भरने के बिना भर देता है। आदेश कार्यान्वयन पर निर्भर करता है। एक पंक्ति बनाम स्टैक का उपयोग करते समय ट्रैवर्सल के क्रम की तुलना करने वाले चित्र के लिए विकिपीडिया प्रविष्टि के नीचे देखें । पुनरावर्ती कार्यान्वयन को एक स्टैक के रूप में भी माना जा सकता है (क्योंकि यह कॉल स्टैक का उपयोग करता है)।
डेविड गॉविया


5

यह इमेज प्रोसेसिंग में एक मानक ऑपरेशन है। आप दो-चरण ऑपरेशन का उपयोग करते हैं।

मानचित्र की प्रतिलिपि बनाकर प्रारंभ करें। इस नक्शे से, समुद्र की सीमा में आने वाले सभी भूमि पिक्सल को समुद्र के पिक्सेल में बदल दें। यदि आप एक बार ऐसा करते हैं, तो यह 2x2 द्वीपों को समाप्त कर देगा, और बड़े द्वीपों को सिकोड़ देगा। यदि आप इसे दो बार करते हैं, तो यह 4x4 द्वीपों, वगैरह को खत्म कर देगा।

चरण दो में, आप लगभग उल्टा करते हैं: भूमि के बॉर्डर वाले सभी पिक्सेल पिक्सेल में बदल जाते हैं, लेकिन केवल तभी जब वे मूल मानचित्र में लैंड पिक्सल होते हैं (इसलिए आपने चरण 1 में प्रतिलिपि बनाई है)। यह द्वीपों को उनके मूल रूप में वापस कर देता है, जब तक कि उन्हें चरण 1 में पूरी तरह से समाप्त नहीं किया गया था।


1

मेरे पास एक समान मुद्दा था लेकिन खेल विकास में नहीं। मुझे एक छवि में पिक्सेल ढूंढने थे जो एक दूसरे से सटे हुए थे और समान मूल्य (जुड़े हुए क्षेत्र) थे। मैंने एक पुनरावर्ती बाढ़ का उपयोग करने की कोशिश की, लेकिन स्टैक ओवरफ्लो का कारण बना रहा (मैं नौसिखिया प्रोग्रामर हूं: पी)। फिर मैंने इस विधि की कोशिश की http://en.wikipedia.org/wiki/Connected-component_labeling यह वास्तव में मेरी समस्या के लिए और अधिक कुशल था, वैसे भी।


आपको बाढ़ के अहंकार के स्टैक संस्करण की कोशिश करनी चाहिए और स्टैक समस्याओं को बचाना चाहिए। सीसीएल की तुलना में बहुत अधिक सरल होने के परिणामस्वरूप (जो कि मैं पिछले 2 सप्ताह से बिना किसी भाग्य के काम कर रहा हूं।)
गेब्रियल ए। ज़ोरिल्ला

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