Minecraft'ın işçiliği ızgarası gibi bir şeyi nasıl uygulayabilirim?


55

Minecraft işçiliği sistemi 2x2 veya 3x3 ızgara kullanır. Malzemeleri ızgaraya yerleştirirsiniz ve doğru malzemeleri doğru desene koyarsanız, tarifi etkinleştirir.

Tasarımla ilgili bazı ilginç noktalar:

  • Bazı tarifler bazılarını başkaları için değiş tokuş edebilir. Mesela, bir toplayıcı sap için çubuklar kullanır ve kafa için tahta plakalar, parke taşı, demir külçeler, altın külçeler veya elmas taşlar kullanabilir.
  • Model içindeki göreceli konum, önemli olan, ızgaradaki mutlak konumdur. Yani, çubuğu ve kömürü (veya kömürü) 3x3 ızgaradaki altı konumdan herhangi birinde doğru düzende yerleştirerek bir torç oluşturabilirsiniz .
  • Desenler yatay olarak döndürülebilir.

Çok fazla düşünüyor olabilirim, ancak bu ilginç bir arama / set-azaltma problemi gibi görünüyor. Peki, algoritmik olarak konuşursak, bu nasıl çalışır (veya işe yarayabilir)?


6
Müthiş soru! Çok ilgileniyorum! Benim zaten bazı fikirlerim var ama eve gelir gelmez bunları paylaşacağım.
jcora

Aslında "mümkün olduğunca yakın" bir Minecraft kopyası yazmaya çalışıyorum. Tüm envanter ve reçete yöneticisi sayfalarını zaten yazdım ve çok düzgün çalıştığını söylemeliyim. Temiz koddan memnunum. Ancak, bunu yazmak kolay değildi. Her şeyi güzel bir şekilde tarif etmek için yaklaşık 10 ders yazdım. Tarifler, ızgaralar, envanter, vb. Biraz zaman bulduğumda bir cevap yazacağım.
Martijn Courteaux,

Minecraft'ın işçiliği tariflerini nasıl kodladığına bakmalısınız.
Bradman175

Yanıtlar:


14

Başka bir çözüm, biraz karmaşık bir ağaç kullanmaktır. Ağacınızdaki dal düğümleri, tarifin üzerine tekrar yazılarak oluşturulur (bir kez daha kullanarak for (y) { for (x) }); Bu, stokta satılan standart ağaç yapısıdır. Son düğümünüz, boyutları tariflerle eşleştiren ek bir yapı ( Dictionary/ HashMap) içerir.

Esasen ne için gidiyorsun şudur:

Tarif Ağacı

Siyah düğümler öğe türünü belirten dallarınızdır - kırmızı olanlar yaprak / sonlandırıcılardır;

Bu ağacın üzerinde aramak için, (benim özetlendiği gibi ilk sınırlayıcı kutu bulur ilk yanıt ) ve daha sonra sen giderken ağaç geçme aynı sırada düğümler üzerinden yineleme. Sonunda, sadece içindeki Dictionaryya da boyutunuza bakarsınız HashMapve tarifin sonucunu alırsınız.

Sadece vuruşlar için gittim ve uyguladım - bu muhtemelen cevabımı netleştirecek. Ayrıca : Bunun farklı bir cevap olduğunun farkındayım - ve haklı olarak öyle: bu farklı bir çözüm.


Çok basit. Bunu sevdim.
David Eyk

Bunu kabul edeceğim, çünkü maddenin kalbini kesen ağaçları, karmaları ve diyagramları seviyorum. Diyagrama bir göz attığımda, algoritmanızı hemen hemen anladım.
David Eyk

23

Minecraft'ın yalnızca çok küçük bir dizi olası tarif kullandığını hatırlamanız gerekir, bu yüzden o kadar akıllı bir şeye gerek yoktur.

Yapacağım şey, uygun olan en küçük ızgarayı bulmak olduğunu söyledi (yani, 2x2 veya 3x3 veya 2x3 (kapı) olup olmadığını bulmak için boş satırları ve sütunları yoksay). Ardından, bu büyüklükteki tarifler listesinde döngü yapın, yalnızca öğe türünün aynı olup olmadığını kontrol edin (yani, öğeler ve bloklar için bir tamsayı türü kimliği kullandığından minecraft'ta en kötü 9 tamsayı karşılaştırması) ve bir eşleşme bulduğunuzda durun.

Bu yol aynı zamanda öğelerin göreceli pozisyonlarını da alakasız kılar (elektirik ızgarasının herhangi bir yerine bir el feneri koyabilirsiniz ve çalışacaktır çünkü çoğunlukla boş olan 3x3 bir kutu değil 1x2 kutu olarak görür).

Eğer muhtemel eşleşmelerde doğrusal bir arama yapmak için çok fazla miktarda reçeteniz varsa, listeyi sıralamak ve ikili bir arama yapmak (O (log (N)) vs O (N)) mümkün olur. Bu, listenin oluşturulmasında fazladan çalışmaya neden olur, ancak bu başlangıçta bir kez yapılabilir ve daha sonra bellekte tutulabilir.

Ayrıca son yatay, tarifi yatay çevirme izin vermek en basit yansıtılmış versiyonu listeye eklemek olacaktır.

İkinci bir tarif eklemeden yapmak isterseniz, giriş tarifinin [0,0] 'da [0,2]' den yüksek (0,2) (veya 2x2 için [0,1] 'den daha yüksek bir maddeye sahip olup olmadığını kontrol edebilirsiniz. 1x2 için ve eğer öyleyse yansıt, sonunu alana kadar bir sonraki satırı kontrol etmeye devam etmiyorsan .. Bunu kullanarak tariflerin doğru rotasyonda eklendiğinden emin olmalısın.


1
Minecraft'taki az sayıdaki tarifin doğrusal bir aramaya katkısı olmayabileceğini merak ettim.
David Eyk

1
Bunu yapar ve temelde yukarıda yazdığım şeydir (ikili aramanın olası optimizasyonu ile). Ancak bu temelde her yere sığabileceğin el fenerleri için birkaç seçenek sunar. İlk önce tarifin boyutunu aldığınızda, meşaleler için 6 tarif eklemeniz gerekmez ve aramanızı yalnızca bir adımda doğru boyutta olanlarla sınırlandırırsınız. - Yani temel olarak 2. puanınız için seçenekler boyuta göre gruplandırılmış, tarifleri bir köşeye getiriniz ya da daha fazla tarif ekleyiniz.
Elva

15

Belirli bir ızgara yapılandırmasının belirli bir tarifle eşleşip eşleşmediğini görmek, 3x3 ızgarasını bir dize olarak kodlarsanız ve normal ifade eşleşmesi kullanıyorsanız kolaydır . Aramayı hızlandırmak, sonunda konuşacağım farklı bir konudur. Daha fazla bilgi için okumaya devam edin.

Adım 1) Izgarayı String olarak kodla

Her bir hücreye bir karakter numarası verin ve her şeyi yan yana sıralayın:

123
456 => 123456789
789

Daha somut bir örnek olarak, W'nin ahşabın durduğu ve E'nin boş bir hücre olduğu (sadece boş bir char '' kullanabilirsiniz):

EEE
WEE => EEEWEEWEE
WEE

Adım 2) Normal İfadeyi Kullanarak Reçeteyi Eşleştir (veya String.Concon veri üzerinde biraz işlem yapar)

Yukarıdaki örnekten devam edersek, formasyonu hareket ettirsek bile, ipte hala bir desen vardır (her iki tarafta E tarafından doldurulmuş WEEW):

EEW
EEW => EEWEEWEEE
EEE

Bu yüzden, çubuğu nereye hareket ettirdiğiniz önemli değil, yine de aşağıdaki normal ifadeyle eşleşecektir: /^E*WEEWE*$/

Normal ifadeler ayrıca belirttiğiniz koşullu davranışı gerçekleştirmenize de izin verir. Mesela (tarifte hazırlanmış), aynı sonucu vermek için demir veya taştan yapılmış bir kazma istiyorsanız , yani:

III    SSS
EWE or EWE
EWE    EWE

Her ikisini de normal ifadede birleştirebilirsiniz: /^(III)|(SSS)EWEEWE$/

Yatay çeviriler de aynı şekilde kolayca eklenebilir (| operatörünü kullanarak).

Düzenleme: Neyse, regex kısmı kesinlikle gerekli değildir. Sorunu tek bir ifadeyle kapamanın yalnızca bir yolu Ancak değişken konum sorunu için, herhangi bir doldurma boşluğunun (veya bu örnekte E'nin) ızgara dizesini düzeltebilir ve bir String.Contains () yapabilirsiniz. Çoklu bileşen problemi veya aynalı tarifler için hepsini aynı çıktıya sahip çoklu (yani ayrı) tarifler olarak kullanabilirsiniz.

Adım 3) Arama hızlandırmak

Aramayı azaltmak için, tarifleri birlikte gruplamak ve aramaya yardımcı olmak için bazı veri yapıları oluşturmanız gerekecektir. Kılavuzun sicim olarak işlenmesi burada da bazı avantajlara sahiptir :

  1. Bir tarifin "uzunluğunu", boş olmayan ilk karakter ile boş olmayan son karakter arasındaki mesafe olarak tanımlayabilirsiniz. Basit bir Trim().Length()bilgi size bu bilgiyi verir. Yemek tarifleri uzunluğa göre gruplandırılmış ve bir sözlükte saklanmış olabilir.

    veya

    Alternatif bir "uzunluk" tanımı boş olmayan karakterlerin sayısı olabilir. Başka hiçbir şey değişmez. Siz de tarifleri bu kritere göre gruplandırabilirsiniz.

  2. Eğer 1 numaralı nokta yeterli değilse, tarifler ayrıca tarifte görünen ilk bileşenin türüne göre de gruplandırılabilir. Bu yapmak kadar basit olabilir Trim().CharAt(0)(ve Trim'e karşı boş bir dizgeyle sonuçlanan koruma).

Mesela tarifleri a:

Dictionary<int, Dictionary<char, List<string>>> _recipes;

Ve aramayı aşağıdaki gibi yapın:

// A string encode of your current grid configuration 
string grid;

// Get length and first char in our grid
string trim = grid.Trim();
int length = trim.Length();
char firstChar = length==0 ? ' ' : trim[0];

foreach(string recipe in _recipes[length][firstChar])
{
    // Check for a match with the recipe
    if(Regex.Match(grid, recipe))
    {
        // We found a matching recipe, do something with it
    }
}

3
Bu ... çok çok akıllı.
Raveline

18
Bir problemin var ve bunu düzenli ifadeler kullanarak mı çözmek istiyorsun? Şimdi 2 probleminiz var :)
Kromster

2
@Krom muhtemelen sadece şaka yapıyorsun, ama bu yorumun arkasındaki herhangi bir sebep olduğunu düşünüyorum? Düzenli bir ifade kullanmak, kısa bir veridir (tek bir kod satırına sahip reçeteyle eşleşir) ve esnek (bu sorunun tüm gereksinimlerine uyar) bir veri kümesinde (ızgara içeriği) eşleme yapmanın bir yoludur. Kendi eşleştirme algoritmanızı yuvarlamanın önemli bir dezavantajı göremiyorum ve tüm süreci kolaylaştırıyor.
David Gouveia

2
@DavidGouveia, bu Regex ile çok rahat olmayan insanlar için bir ifadedir. Eğer regex ile ilgili bir problemi çözmeye karar verirlerse, şimdi iki problemleri vardır: (1) çözmek istedikleri asıl problem (2) bu problemi çözmek için regex desenini yazmak
Aralık'ta

2
'Şimdi iki sorununuz var' bölümüne geri dönün - Regex işlemciniz unicode'u desteklemediği ve belirli kombinasyonların regex NFA derleyicisini açmadığını garanti ederseniz, ASCII yazdırılabilir aralığından daha fazla öğeye sahip olduğunuzda Regex'in sorunları olur yukarı. Kalıpları eşleştirmek için kendi DFA'nızı (aslında kolayca) bir araya getirebilirsiniz; ancak regex prototip yaparken gidilecek en iyi yol olacaktır.
Jonathan Dickinson

3

Size Minecraft'ın nasıl çalıştığını söyleyemem - MCP'ye bakıp bakmadığınızdan emin olmama rağmen (yasal bir Minecraft kopyası varsa) öğrenebilirsiniz.

Bunu şu şekilde uygulardım:

  1. Disk tabanlı bazı sözlük / hashmap ( B + Ağaç / SQL-Işık ) var; değer, tarif sonucunun öğe kimliği olacaktır.
  2. Bir kullanıcı bir şey üretiyorsa, BAĞIMSIZ bir madde ile ilk satırı / sütunu bulması yeterlidir ; bunları bir ofset halinde birleştirin.
  3. Bir öğeyle son satırı / sütunu bağımsız olarak tekrar bulun.
  4. Öğelerden genişlik ve yüksekliği hesaplayın ve anahtara ekleyin.
  5. Yeni hesapladığınız sınırlama kutusunun aralığının üzerinde dolaşın ve her bir öğenin kimliğini ekleyin (normalinizi kullanarak for (y) { for (x) }).
  6. Veritabanındaki anahtarı ara.

Mesela iki malzememiz olduğunu söyleyelim; X ve Y ve boşluklar *. Aşağıdaki tarifi alın:

**X
**Y
**Y

İlk önce, sınırlayıcı kutuyu seçiyoruz, verimli oluyoruz (2,0)-(2,2). Bu nedenle anahtarımız şöyle görünür [1][3](1 genişlik, 3 yükseklik). Daha sonra sınırlama kutusundaki her bir öğe üzerinde döngü kurar ve ID'yi ekleriz, böylece anahtar olur [1][3][X][Y][Y]- daha sonra bunu sözlük / DB'nizde ararsınız ve bu tarifin sonucunu alırsınız.

2. adımdaki bağımsızlığı daha net bir şekilde açıklamak için aşağıdaki tarifi dikkate alın:

*XX
XXX
XX*

Sol üst / sol açıkça 0,0 - - ancak karşılaşacağınız ilk öğe 0,1 veya 1,0 olacaktır (döngünüze bağlı olarak). Bununla birlikte, ilk boş olmayan sütunu ve ilk boş olmayan satırı bulup bu koordinatları birleştirirseniz 0,0 elde edersiniz - aynı ilke sınırlayıcı kutunun alt / sağına uygulanır.


Bukkit deposu herhangi Minecraft kodu yok, sen ihtiyacım olacağını MCP (ve Minecraft bir kopyasını)
BlueRaja - Dany Pflughoeft

Bu sadece diğer cevabınızın tersi değil mi?
David Eyk

@DavidEyk (Bu benim ilk cevabım) gerçekten değil, daha çok hashmap yapısına (ister ister büyük ister ister hafızadan olsun) bağımlıdır. Tekrar cevap vermemin sebebi, en azından hafıza içi bir hasatta, düşük performansa neden olan karmaşalı çarpışmalardan endişe duymamdı. Diğer cevabım daha fazla öğe ve hafıza içi bir yapı için daha iyi çalışacaktı - bunun SQL / etc'de olsaydı daha iyi çalışacağı yerdi.
Jonathan Dickinson

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.