มีอัลกอริทึมในการตรวจจับ "แผ่นดินใหญ่" บนแผนที่ 2D หรือไม่?


28

บนแผนที่นี้ "แผ่นดินใหญ่" เป็นดินแดนทั้งหมดที่สามารถเชื่อมต่อกับศูนย์กลางของแผนที่ในสี่ทิศทางที่สำคัญ (เหนือ, ใต้, ตะวันออก, ตะวันตก - ไม่ใช่แนวทแยงมุม) ป้อนคำอธิบายรูปภาพที่นี่

ฉันต้องการตรวจจับแผ่นดินใหญ่และเติมเต็มหลุมในนั้น ฉันคิดถึงสามสิ่ง:

  1. ค้นหาในทุกเซลล์ที่ไม่ใช่น้ำ (เซลล์มืด) หากสามารถเชื่อมต่อกับศูนย์กลางของแผนที่โดยใช้อัลกอริทึมการค้นหาเส้นทาง แพงเกินไป! แต่มันสามารถใช้ได้กับเกาะต่างๆ

  2. แผ่นดินใหญ่เต็มไปด้วยถังสีเขียว แต่ละหลุมล้อมรอบด้วยสี ... ตอนนี้อะไรนะ? หากฉันตรวจสอบทุก ๆ จุดน้ำในแผ่นดินใหญ่ฉันจะลบคาบสมุทรและคุณลักษณะทางภูมิศาสตร์อื่น ๆ ที่แสดงบนชายฝั่ง

  3. การตรวจจับขอบบางชนิดเพื่อค้นหาแผ่นดินใหญ่ เก็บสิ่งที่อยู่ข้างในเติมถ้ามันเป็นน้ำเอาสิ่งที่อยู่ข้างนอกออก คอมเพล็กซ์?

บางทีผู้พัฒนาเกมที่มีประสบการณ์บางคนสามารถช่วยฉันได้ด้วยสิ่งนี้อาจให้ชื่อฉันกับอัลกอริทึมหรือเทคนิคที่รู้จักกันบ้าง?


4
ฉันแค่สงสัยว่าคุณใช้อัลกอริทึมบางอย่างเพื่อสร้างแผนที่นี้หรือไม่ และถ้าเป็นเช่นนั้นคุณใช้อะไร?
jgallant

มูลค่ามองเมื่อทำงานในสภาพแวดล้อมที่ tilebased เป็นสี่เหลี่ยมวงอัลกอริทึม ด้วยคุณสามารถตรวจจับและรักษาเกาะเล็ก ๆ ได้เช่นกันจากนั้นเรียงลำดับตามขนาดที่ละทิ้งเกาะเซลล์เดี่ยวหรือเกณฑ์ที่คุณอาจมี
William Mariager

@ จอนใช่! อัลกอรึทึมสี่เหลี่ยมจัตุรัสเพื่อสร้างแผนที่ความสูงจากนั้นค่าหนึ่งคือน้ำส่วนที่เหลือที่ดิน ฉันวางแผนที่จะใช้สิ่งนี้เป็นรูปแบบทวีปจากนั้นอีกหนึ่งเส้นทางเพื่อเพิ่มรายละเอียดภูมิประเทศ
Gabriel A. Zorrilla

@Mind Marching Squares อัลกอริทึมจะมีประโยชน์ในการวาดชายแดนของทวีป ขอบคุณคำแนะนำดี ๆ !
Gabriel A. Zorrilla

คำตอบ:


32

การถอดหมู่เกาะ

ฉันเคยทำสิ่งนี้มาก่อนในเกมของฉัน เพื่อกำจัดเกาะรอบนอกกระบวนการเป็นพื้น:

  1. ก่อนอื่นจะต้องมีการรับประกันศูนย์กลางของแผนที่จะเป็นของที่ดินหลักเสมอและแต่ละพิกเซลจะเริ่มต้นเป็น "ที่ดิน" หรือ "น้ำ" (เช่นสีที่ต่างกัน)
  2. จากนั้นเติมน้ำท่วมสี่ทิศทางเริ่มต้นจากศูนย์กลางแผนที่และกระจายไปทั่วแผ่น "ที่ดิน" ทำเครื่องหมายทุกพิกเซลที่มีผู้เยี่ยมชมกรอกข้อมูลนี้เป็นประเภทอื่นเช่น "MainLand"
  3. ในที่สุดไปทั่วทั้งแผนที่และแปลงพิกเซล "Land" ที่เหลืออยู่เป็น "Water" เพื่อกำจัดเกาะอื่น ๆ

การถอด Lakes

สำหรับการกำจัดหลุม (หรือทะเลสาบ) ภายในเกาะคุณทำกระบวนการที่คล้ายกัน แต่เริ่มจากมุมของแผนที่และแพร่กระจายผ่านแผ่น "น้ำ" แทน สิ่งนี้จะช่วยให้คุณแยก "ทะเล" ออกจากแผ่นน้ำอื่น ๆ และจากนั้นคุณสามารถกำจัดมันได้เหมือนกับที่คุณกำจัดเกาะมาก่อน

ตัวอย่าง

ผมขอขุดขึ้นมาการดำเนินงานของฉันเติมน้ำท่วมที่ฉันมีที่นี่ที่ไหนสักแห่ง (ปฏิเสธผมไม่ได้ดูแลเกี่ยวกับประสิทธิภาพดังนั้นฉันแน่ใจว่ามีหลายวิธีที่มีประสิทธิภาพมากขึ้นในการใช้มัน):

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;
}

แก้ไข

การเพิ่มข้อมูลพิเศษบางอย่างขึ้นอยู่กับความคิดเห็น ในกรณีที่พื้นที่การค้นหาของคุณใหญ่เกินไปคุณอาจประสบปัญหาโอเวอร์โฟลว์แบบสแต็กเมื่อใช้อัลกอริทึมเวอร์ชันเรียกซ้ำ นี่คือลิงค์ของ stackoverflow (ปุนตั้งใจ :-)) ไปยังอัลกอริทึมที่ไม่ซ้ำแบบซ้ำโดยใช้Stack<T>แทน (เช่นใน C # เพื่อให้ตรงกับคำตอบของฉัน แต่ควรง่ายต่อการปรับให้เข้ากับภาษาอื่น ๆ และมีการใช้งานอื่น ๆ ลิงก์ด้วย)


11
หากคุณไม่สามารถรับประกันได้ว่ากระเบื้องกลางจะเป็นที่ดินและเป็นส่วนหนึ่งของแผ่นดินใหญ่เสมอให้ใช้การเติมน้ำท่วมเพื่อทำเครื่องหมายแต่ละแผ่นกระเบื้องว่าเป็นของเกาะใดเกาะหนึ่ง blobid เดียวกันถ้ามันยังไม่มี) จากนั้นลบทั้งหมดยกเว้นหยดที่มีกระเบื้องมากที่สุด
George Duckett

สอดคล้องกับสิ่งที่ GeorgeDuckett กล่าว - คุณอาจลองใช้อัลกอริทึมตรวจจับหยดของ Googling: มันเป็นปัญหาที่พบบ่อยในมัลติทัชโดยใช้ FTIR ฉันจำได้ว่าฉันคิดอัลกอริทึมอย่างชาญฉลาด แต่ฉันจำไม่ได้ว่าชีวิตของฉันทำงานอย่างไร
Jonathan Dickinson

เนื่องจากฉันมีปัญหาสแต็คใน PHP ฉันใช้ LIFO ท่วมเติมทำงานอย่างน่าพิศวง
Gabriel A. Zorrilla

น้ำท่วมไม่ใช่การเติมเมื่อมีการเรียกอัลกอริทึม DFS จากอัลกอริทึม BFS หรือไม่ กรุณาอธิบายใครบางคน
jcora

@Bane คุณหมายถึงอะไร ความแตกต่างระหว่าง DFS หรือ BFS เป็นเพียงลำดับที่โหนดเข้าชม ฉันไม่คิดว่าอัลกอริทึมเติมน้ำท่วมระบุลำดับของการแวะผ่าน - มันไม่สนใจตราบใดที่มันเติมเต็มทั้งภูมิภาคโดยไม่ต้องไปที่โหนด ลำดับขึ้นอยู่กับการใช้งาน ดูด้านล่างของรายการวิกิพีเดียสำหรับรูปภาพเปรียบเทียบลำดับของการแวะผ่านเมื่อใช้คิวกับการใช้สแต็ค การใช้งานแบบเรียกซ้ำสามารถพิจารณาได้ว่าเป็นสแต็กด้วย (เนื่องจากใช้ call stack)
David Gouveia


5

นี่เป็นการดำเนินงานมาตรฐานในการประมวลผลภาพ คุณใช้การดำเนินการสองเฟส

เริ่มต้นด้วยการสร้างสำเนาของแผนที่ จากแผนที่นี้ให้เปลี่ยนเป็นพิกเซลน้ำทะเลทั้งหมดพิกเซลที่ดินที่ล้อมรอบทะเล หากคุณทำสิ่งนี้ครั้งเดียวมันจะกำจัดเกาะ 2x2 และหดเกาะที่ใหญ่กว่า ถ้าคุณทำสองครั้งมันจะกำจัดเกาะ 4x4 และอื่น ๆ

ในขั้นตอนที่สองคุณทำสิ่งที่ตรงกันข้าม: เปลี่ยนเป็นพิกเซลที่ดินพิกเซลทะเลทั้งหมดที่ล้อมรอบที่ดินแต่เฉพาะถ้าพิกเซลเหล่านั้นเป็นพิกเซลที่ดินในแผนที่ดั้งเดิม (นั่นคือเหตุผลที่คุณทำสำเนาในระยะที่ 1) ปลูกต้นไม้ให้เกาะอยู่ในสภาพดั้งเดิมยกเว้นพวกมันจะถูกกำจัดในระยะที่ 1


1

ฉันมีปัญหาที่คล้ายกัน แต่ไม่ใช่ในการพัฒนาเกม ฉันต้องหาพิกเซลในภาพที่อยู่ติดกันและมีค่าเดียวกัน (ภูมิภาคที่เชื่อมต่อ) ฉันพยายามใช้โปรแกรมเติมน้ำท่วมซ้ำ แต่เก็บไว้เป็นสาเหตุให้เกิดโอเวอร์โฟลว์ซ้อนกัน (ฉันเป็นโปรแกรมเมอร์มือใหม่: P) จากนั้นฉันลองใช้วิธีนี้http://en.wikipedia.org/wiki/Connected-component_labelingจริง ๆ แล้วมันมีประสิทธิภาพมากกว่าสำหรับปัญหาของฉัน


คุณควรลองใช้เวอร์ชันอัลโตของสแต็กน้ำท่วมและบันทึกปัญหาสแต็ก ส่งผลให้ง่ายกว่า CCL (ซึ่งฉันทำงานมาแล้ว 2 สัปดาห์โดยไม่มีโชค)
Gabriel A. Zorrilla

ใช่ฉันลองทั้งคู่ แต่ฉันได้ CCL มาทำงานก่อน: P และคิดว่ามันเป็นความคิดที่เรียบร้อย ดีใจที่คุณแก้ปัญหา :)
Elegant_Cow
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.