Dinamik, sınırsız boyutlu bir "labirent" için veri yapısını nasıl oluşturmalıyım?


43

Aslında "labirent" in doğru terim olduğundan emin değilim. Temel olarak, kullanıcılar Room4 kapılı (N, S, E ve W) bir single ile başlar . Herhangi bir yöne gidebilirler ve sonraki odaların her birinde diğer odalara giden 1 ila 4 kapı arasında başka bir oda bulunur.

"Labirent" boyutunda sınırsız olması ve oda hareket ettikçe büyümek gerekiyordu. Sınırlı sayıda Roomsmevcuttur, ancak mevcut sayı dinamiktir ve değişebilir.

Benim sorunum, bu tür bir kalıp için en iyi veri yapısından emin değilim.

İlk önce sadece bir [X] [X] Roomnesne dizisi kullanmayı düşündüm , ama olayın herhangi bir yöne doğru büyümesi gerektiğinden ve sadece “ziyaret edilen” odaların inşa edilmesi gerektiğinden kaçınmayı tercih ederim.

Diğer düşünce, her bir Roomsınıfın RoomN, S, E ve W için 4 bağlantılı özellik içermesi ve sadece bir önceki ile bağlantı kurmasıydı Room, ancak bir kullanıcının bir odaya girip girmeyeceğini nasıl belirleyeceğimi bilmiyorum. zaten "inşa edilmiş" bir bitişik odaya sahip

Örneğin,

--- --- ----------
| | | |
   5 4 Başlat
| | | |
--- --- --- ---
--- --- ---------- --- ---
| | | | | |
| 1 2 3
| | | | | |
--- --- --- --- ----------

Kullanıcı Başlat> 1> 2> 3> 4> 5’den hareket ederse, Room# 5’in W’nin başlangıç ​​odasını içerdiğini bilmesi gerekir, S oda # 2’dir ve bu durumda kullanılamamalıdır ve N ya yeni Roomveya bir duvar (hiçbir şey).

Belki dizinin ve bağlantılı odaların bir karışımına ihtiyacım var ya da belki sadece bu yanlış yola bakıyorum.

Bu tür "labirent" için veri yapısını oluşturmanın daha iyi bir yolu var mı? Yoksa şu anki düşünce sürecimle doğru yolda mıyım ve sadece birkaç bilgi parçasını mı kaçırıyorum?

(İlgilendiğiniz takdirde, proje Munchkin Quest'e çok benzeyen bir oyundur )


Odalar herhangi bir yöne doğru büyüyeceğinden, herhangi bir dizi çalışacağını sanmıyorum ... Yani [0,0] 'dan başlayıp sola doğru hareket ederseniz? dener [-1, 0].
Paul

@Paul Bir satır / sütun ekleyin, tüm dizi verilerini kaydırın, ardından yeni harita dizisini eşleştirmek için tüm oynatıcı konumlarını kaydırın. Yavaş ve ne kadar kaydırılması gerektiğine bağlı olarak zor olabilir, ancak mümkün. Yine de, Bubblewrap'in cevabı çok daha iyi.
Izkata

Muhtemelen yanılıyorum, ama bu GameDev.SE'de daha iyi olmaz mıydı?
Dinamik

1
@ Dinamik Bu bir veri yapısı sorusudur, bu yüzden burada gayet iyi uyuyor.
Izkata

Yanıtlar:


44

Her Oda koordinatlarını verin (başlangıç ​​(0,0) olur) ve oluşturulan her Odaya bir sözlükte / hashmap'te koordinatlar halinde saklayın.

Bir Odanın mevcut olup olmadığını kontrol etmek için kullanabileceğiniz her Oda için bitişik koordinatları belirlemek önemsizdir. Oda bulunmadığının belirlendiği yerleri göstermek için boş değerler ekleyebilirsiniz. (veya bu mümkün değilse, emin değilim atm, bir Oda içermeyen koordinatlar için ayrı bir sözlük / hasmap)


2
Her bir Oda nesnesinin diğer Oda nesnelerine kuzey-doğu-güney-batı bilgisi içermesini sağlayın; Bazen, Oda nesnesi X'in kuzeyinin ne olduğunu bilmek isteyeceksiniz.
Neil

2
@Paul Sanırım fikrim bir oda sözlüğü oluşturmak ve yeni bir bina inşa ederken Room, sözlükte bitişik odalara bakmak Roomve diğer taraflara yeni odalar eklemeden önce nesneye bağlantılarını eklemek . Yeni bir oluştururken sadece zaman Yani oluşacak Sözlük arama olduğu Roomarasında seyahat ederken nesne değilRooms
Rachel

7
Bir Roomnesnenin içindeki diğer odalara bağlantılar depolamak için hiçbir neden yoktur . Odadaysanız (x,y)ve Kuzey'e gitmek istiyorsanız (x,y+1), sözlükte oda arayarak mevcut değilse oluşturun. Sözlük aramaları çok hızlı O(1).
Karl Bielefeldt

3
@KarlBielefeldt: @ odası (x,y+1)mevcut olabilir, ancak (x,y)kuzeye gitmek için yönlendirilemez . Başka bir deyişle, bir kenar doğrudan ile (x,y)başa çıkamayabilir (x,y+1).
Steven Evers

2
Siz çocuklar bunu karmaşıklaştırıyorsunuz. Bu, temelde bir dizi ile aynıdır. İki boyutlu bir dizi yerine bir sözlükte aramanız dışında. Bir dizi ile karşılaştığınız sorunlar da orada olacak ... ama yine de bir dizi kullanacaktı.
user606723

15

Çoğu durumda, tarif ettiğiniz şey bir grafik gibi görünür. Gereksinimlerinizden bazıları (büyüyen yönü) göz önüne alındığında, muhtemelen uygulama olarak bir bitişiklik listesi seçerdim (diğer ortak seçenek bir bitişiklik matrisi olur ).

Hangi dili kullandığınızdan emin değilim, ancak .NET'te bir bitişik liste ile uygulanan bir grafik için uygulama ayrıntılarıyla ilgili iyi bir öğretici / açıklama burada .


1
N / E / S / W grafik kavramının bir parçası olmadığından bir grafiğin bunu tanımlamak için yeterli olduğundan emin değil; Sadece bağlı ve muhtemelen giriş / çıkış var.
Edward Strange

3
@ CrazyEddie: Veri yapısının doğrudan herhangi bir alanla (aslında, onların yararı ile) eşlenmesinin amaçlanmadığını unutmayın. Bu özel örnekte, grafik yönlü olacaktır ve kenarlar enum ile önemsiz şekilde açıklamalı olabilir.
Steven Evers

Açıklama, hatta doğu-batı, kuzey-güney ilişkisini önemsizce uygulayabilir. Bu, kötü bir bağlantı nedeniyle A odasından B odasına batıya gitme ve sonra B odasından C odasına (A odasının yerine) geçme sorunlarını ortadan kaldıracaktır.
Peter Smith

3
Diyelim ki oyuncuya on kareyi rastgele bir doğrultuda ışınlayarak kendisini koruyan bir büyücü tarafından doldurulmuş bir odayı uygulamak istediğimizi söyleyelim. Grafik tabanlı bir veri yapısında bu noktayı bulmak çok pahalı olurdu, özellikle de o oda mevcut değilse ve o odayı grafikteki başka bir odaya bağlayan odaların hepsini oluşturmak zorunda kalsaydınız (yapı birden fazla izin vermediğinden, karşılıklı zindan bölümleri kesildi).
Mark Booth

2
@MarkBooth ama sonra gereksinimleri değiştirdik, hayır?
Joshua Drake

9

Uygulama hakkında bir kaç düşünce (Matrisin oluşturulmasında zorlayıcı yanları olan Carcassonne'yi düşündüm - bu bir zamanlar sorulan bir röportaj sorusuydu bile).

Bu yapıdan sorulan bazı sorular var:

  1. X'de bir oda var mı, Y?
  2. X, Y boş yerinin bir odası [N / S / E / W] var mı?

Basit listelerde ve grafiklerde problem

Basit listelerde zorluk, bir yere belirli bir yere yerleştirilip yerleştirilmediğini test etmek için yapıyı yürümek zorunda kalmasıdır. Sistem, isteğe bağlı bir O (1) konumuna erişme yoluna ihtiyaç duyar.

Düşünmek:

1 2 3 ? 4
5 . . * 6
7 8 9 A B

Bilgiyi bulmak mümkün olan '?' Noktasını bulmak için, 3'te bir kişi 4 e kadar dolaşmak zorundadır. Kaç tane düğüm atladığına dair ekstra bilgi içeren bağlantının cevabı (3, 4'e bağlanır. 'atlama 1' ile ekstra bilgi) hala 6 veya A'dan itibaren '*' bitişikliğini bulurken yürüyüşler yapılmasını gerektirir.

Basit, büyük, diziler

Sınırlı sayıda oda bulunmaktadır.

Bu büyük bir sayı değilse, sadece 2N x 2N dizisi oluşturun ve orada bırakın. 100 x 100 dizi 'sadece' 10.000 işaretçi ve 50 nesnedir. Diziye doğrudan indeksleyin.

Sınır dışı faaliyetler diziyi her zaman kısıtlamaların içinde olacak şekilde kaydırırsa, bu sadece NxN'ye düşürülebilir. Örneğin, dizinin altındaki bir oda eklenecekse, her elemanı bir konum değiştirecek şekilde ızgaraya geçin, böylece artık su akışı olmaz. Bu noktada, 50 oda için dünyanın büyüklüğü 2500 işaretçi ve 50 nesne olacaktır.

Seyrek diziler ve matrisler

Bunu en iyi şekilde oluşturmak için, elementlerin çoğunun 0 veya boş olduğu seyrek bir diziye bakın. İki boyut için, bu seyrek bir matris olarak bilinir . Pek çok programlama dili, bu yapıyla çalışmak için çeşitli kütüphanelere sahiptir. Eğer kitaplık tamsayılarla sınırlıysa, bir kişi bu tamsayıyı sabit bir oda dizisine indeks olarak kullanabilir.

Tercih edilen yaklaşımım, odaların bir yapı olarak (bitişik odalara işaretçiler ve koordinatlar) sahip olması ve aynı zamanda odanın koordinat üzerinde toplanmış bir karma tablodan bir işaretçi olması olacaktır. Bu durumda boş bir odadan [N / S / E / W] odasının ne olduğunu sormak için, bir hash tablosu sorgulanır. Bu, tesadüfen, seyrek bir matris depolamak için 'DOK' formatıdır.


1
Bubblewrap'ın önerdiği gibi güzel bir cevap, anahtar olarak bir pozisyon tupleı olan bir sözlük, seyrek bir matris uygulamak için kullanılacak makul bir yapıdır.
Mark Booth

2

Buna ne dersin..

Her hücreye komşularının her birine bir link verin. Her hücreye bir tür ad verin (yani "0/0", "-10/0" (0,0'dan başladığınızı varsayalım)). İçindeki tüm adlara sahip bir HashSet ekleyin. Başka bir hücreye taşınmaya çalıştığınızda, sadece komşunun HashSet'te bulunmadığını kontrol edin.

Ayrıca, başka bir odaya açılan bir boşluk yaratırsanız, bu odaların var olduğu anlamına mı geliyor? Böylece boş bir odaya, hiçbir odaya bağlantısı olmayan bir bağlantı oluşturacaksınız. Muhtemelen yeni komşu odalarını da kontrol etmek istersiniz. Eğer biri varsa ve yeni odanıza açılırsa, o odaya bir kapı ekleyin.

   Empty   
   (0,1)        

---    ---            ----------
|        |            |        |
    0,0       1,0        2,0       Empty
|        |            |        |   (3,0)
---    --- ---------- ---    ---
|        | |        | |        |
|   0,-1      1,-1       2,-1      Empty
|        | |        | |        |   (3,-1)
---    --- ---    --- ----------

   Empty     Empty   
  (0,-2)    (1,-2)

HashSet = {0 | 0, 1 | 0, 2 | 0, 3 | 0, 0 | -1, 1 | -1 ....} 1,0: W = 0,0 / Kapı; 1,0: N = 1,1 / Boş; E = 2,0 / Kapı; S = 1, -1 / Duvar

Ayrıca labirentin bu yönde büyümesi için her yeni odaya en az bir bitişik olmayan (başka bir odaya) bir kapı verdiğinizden emin olmalısınız.


1

Tasarladığınız şey bir grafik gibi görünüyor.

Labirentin grafiği

Bunlar genellikle durumlarının kümesi olarak temsil edilir Q , bir ilk durumu q, 0S ve bazı geçiş Δ . Yani, böyle saklamanızı öneririm.

  • Bir küme Q : {s, 1, 2, 3, 5, 6}
  • Bir başlangıç ​​durumu q 0 : s
  • Geçiş ilişkileri haritası Δ : {s → 1, s → 5, 1 → 2, 2 → 3, 3 → 4, 4 → 5}

En makul dillerin hem haritaları hem de setleri vardır.


0

4 yollu bağlantılı bir liste düşünebilirsiniz ...

İlk önce sadece [X] [X] Room nesneleri dizisini kullanmayı düşünmüştüm, ama olayın herhangi bir yöne doğru büyümesi gerektiğinden ve sadece “ziyaret edilen” odaların inşa edilmesi gerektiğinden kaçınmayı tercih ederim.

Bunun için hala [] [] kullanabilirsiniz. Dinamik büyüme, özellikle başlangıçta eklerken yavaş olabilir, ama belki de o kadar da kötü olmayabilir. Kontrol etmek için profil vermelisin. Bazı bağlantılı liste veya haritayı manipüle etmeye çalışmak aslında daha kötü olabilir ve ara sıra olmak yerine sabit bir seviyede olabilir.

Tembel değerlendirme uygulayarak sadece ziyaret edilen odalar inşa edebilirsiniz. Oda içeren bir nesne olabilir ve room()çağrılana kadar o odayı yapmaz . Sonra her seferinde aynı şeyi verir. Yapması zor değil.


1
"4 yönlü bağlantı listesi" ile neyi kastettiğinizi genişletebilir misiniz? Bulabildiğim tek şey, bir bitişiklik listesi olarak ortaya çıkan bir CodeProject makalesiydi.
Steven Evers

0

Bunu "Bilgisayarınızda Macera Oyunları Oluşturma" kitabı aracılığıyla yapmayı öğrendim. BASIC kodunu incelemeye istekliysen (kitap çok eski) buradan oku:

http://www.atariarchives.org/adventure/chapter1.php

Kısayol için, yaptıkları bir oda dizisi vardır, her dizideki bir öğe gidebileceğiniz başka bir odaya bir işaretçidir, 0 ile yapamayacağınızı belirtmek için 0 (bu gün -1'i kullanırdım) o tarafa git. Örneğin, bir oda yapısı (veya sınıf ya da neyin varsa) yaparsınız

room {
 int north_dir;
 int south_dir;
 int west_dir;
 int east_dir;
 // All other variables and code you care about being attached to the rooms here
}

Sonra bir dizi veya bağlantılı liste yapısına sahip olabilirsiniz ya da odalarınızı yönetmek istersiniz. Dizi tarzı (veya C ++ vektörleri) için bu kodu kullanırdım ve yönergeler bağlandıkları odanın dizinini tutar; Bağlantılı bir liste için yandaki işaretçileri kullanabilirsiniz.

Diğerlerinin söylediği gibi, insanlar girdiğinde dinamik olarak yeni odalar oluşturmanız gerekiyorsa, bunu da yapabilirsiniz.


0

Tüm problemleri tek bir yapıyla çözmeye çalışmayın.

Diğer nesnelerle olan ilişkilerini değil, odanın hakkındaki şeyleri saklamak için odanızın nesnesini tanımlayın. Sonra bir 1D dizi, liste vb istediğiniz gibi büyüyebilir.

Ayrı bir yapı "ulaşılabilirliği" tutabilir - içinde bulunduğum odadan hangi odalara erişilebilir. O zaman geçişli ulaşılabilirliğin hızlı bir hesaplama yapıp yapmayacağına karar verin. Bir kaba kuvvet hesaplama ve bir önbellek isteyebilirsiniz.

Erken optimizasyondan kaçının. Gerçekten basit yapılar kullanın ve aptalca kolayca anlaşılan algoritmaları kullanın ve daha sonra kullanılanları optimize edin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.