Fonksiyonel dillerde iki boyutlu masa oyunları için veri yapısı


9

İşlevsel programlama dili Elixir'de basit bir MiniMax uygulaması oluşturuyorum . Birçok mükemmel bilgi oyunu (tic tac toe, connect-four, dama, satranç vb.) Olduğundan, bu uygulama bu oyunlardan herhangi biri için oyun AI'ları oluşturmak için bir çerçeve olabilir.

Bununla birlikte, karşılaştığım bir sorun, bir oyun durumunun işlevsel bir dilde düzgün bir şekilde nasıl saklanacağıdır. Bu oyunlar çoğunlukla aşağıdaki işlemlerin sık olduğu iki boyutlu oyun tahtaları ile ilgilenmektedir:

  • Belirli bir kart konumunun içeriğini okuma
  • Belirli bir kart konumunun içeriğini güncelleme (yeni bir taşıma olasılığı döndürürken)
  • Geçerli konuma bağlı olan bir veya daha fazla konumun içeriği dikkate alındığında (yani bir sonraki veya bir önceki yatay, dikey veya çapraz konumlar)
  • Herhangi bir yönde birden çok bağlı yerin içeriğini göz önünde bulundurmak.
  • Tüm dosyaların, sıralamaların ve köşegenlerin içeriği göz önüne alındığında.
  • Tahtayı döndürme veya yansıtma (önceden hesaplanmış bir şeyle aynı sonucu veren simetrileri kontrol etmek için).

Çoğu işlevsel dil Bağlantılı Listeleri ve Tuples'ları çok elemanlı veri yapılarının temel yapı taşları olarak kullanır. Ancak, bunlar iş için çok kötü yapılmış gibi görünüyor:

  • Bağlantılı listelerin O (n) (doğrusal) arama süresi vardır. Ayrıca, tahta üzerinde tek bir taramada 'kartı tarayamaz ve güncelleyemeyiz', listelerin kullanılması çok pratik görünmüyor.
  • Tuples O (1) (sabit) arama süresine sahiptir. Bununla birlikte, kartı sabit boyutlu bir demet olarak temsil etmek, sıralar, dosyalar, köşegenler veya diğer ardışık kareler üzerinde yinelemeyi çok zorlaştırır. Ayrıca, hem İksir hem de Haskell (bildiğim iki fonksiyonel dil) , bir grubun n . Elementini okumak için sözdiziminden yoksundur . Bu, keyfi boyuttaki panolarda işe yarayacak dinamik bir çözüm yazmayı imkansız hale getirecektir.

Elixir, O (log n) (logaritmik) öğelerine erişime izin veren yerleşik bir Harita veri yapısına (Ve Haskell sahiptir Data.Map) sahiptir . Şu anda x, y, konumu anahtarlar olarak gösteren tupleslerle bir harita kullanıyorum .

Bu 'işe yarıyor' ama tam olarak nedenini bilmememe rağmen haritaları bu şekilde kötüye kullanmak yanlış geliyor. İki boyutlu bir oyun tahtasını işlevsel bir programlama dilinde saklamanın daha iyi bir yolunu arıyorum.


1
Praksis hakkında konuşamam, ancak Haskell'den aklıma iki şey geliyor: fermuarlar , veri yapıları üzerinde sabit zamanlı "adımlara" izin veren fermuarlar ve ne hatırladığım ne de düzgün bir şekilde anladığım bazı teorilerle fermuarlarla ilgili olan komonadlar; )
phipsgabler

Bu oyun tahtası ne kadar büyük? Big O, bir algoritmanın ne kadar hızlı olduğunu değil, nasıl ölçeklendiğini karakterize eder. Küçük bir tahtada (örneğin, her yönde 100'den az), her kareye yalnızca bir kez dokunursanız, O (1) ve O (n) çok önemli değildir.
Robert Harvey

@RobertHarvey Değişecektir. Ancak bir örnek vermek gerekirse: Satranç'ta 64x64'lük bir tahta var, ancak hangi hareketlerin mümkün olduğunu kontrol etmek ve mevcut pozisyonun sezgisel değerini belirlemek için tüm hesaplamalar (malzeme farkı, kontrolde kral olsun ya da olmasın, geçen piyonlar, vb.) hepsi tahta karelere erişmek gerekir.
Qqwy

1
Satrançta 8x8 tahta var. C gibi bir bellek eşlemeli dilde, bir hücrenin tam adresini almak için matematiksel bir hesaplama yapabilirsiniz, ancak bu bellek tarafından yönetilen dillerde geçerli değildir (sıra adreslemenin bir uygulama ayrıntısı olduğu yerlerde). 14 düğüme atlamak (bellek) tarafından yönetilen bir dilde bir dizi öğesine hitap etmekle aynı miktarda zaman alırsa beni şaşırtmaz.
Robert Harvey

Yanıtlar:


6

A Maptam olarak burada doğru temel veri yapısıdır. Seni neden tedirgin edeceğinden emin değilim. İyi arama ve güncelleme sürelerine sahiptir, dinamik boyuttadır ve türev veri yapıları oluşturmak çok kolaydır. Örneğin (haskell'de):

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

Programcıların tam değişmezlikle ilk kez programlamaya başladıklarında kavramaları genellikle zor olan bir diğer şey de, tek bir veri yapısına bağlı kalmanıza gerek olmamasıdır. Genellikle "gerçeğin kaynağı" olarak bir veri yapısı seçersiniz, ancak istediğiniz kadar türev, hatta türev türevleri yapabilirsiniz ve bunların, ihtiyaç duyduğunuz sürece senkronize kalacağını bilirsiniz.

Bu Map, en üst düzeyde bir a kullanabilir , ardından satır analizi için Listsveya Arrayssatır analizi için geçiş yapabilir , daha sonra Arrayssütun analizi için diğer yolu dizine ekleyebilir , daha sonra desen analizi için bit maskeleri, ardından Stringsgörüntüleme için anlamına gelir. İyi tasarlanmış fonksiyonel programlar etrafta tek bir veri yapısı geçirmez. Bunlar, bir veri yapısını içine alan ve bir sonraki adıma uygun yeni bir veri yayan bir dizi adımdır.

En üst seviyenin anlayabileceği bir formatta bir hareketle diğer tarafa çıkabildiğiniz sürece, aradaki verileri ne kadar yeniden yapılandırdığınızdan endişelenmenize gerek yoktur. Değişmez, bu yüzden en üst düzeyde hakikatin kaynağına giden bir yolu izlemek mümkündür.


4

Bunu son zamanlarda F #'da yaptım ve tek boyutlu bir liste kullandım (F # 'da, bu tek bağlantılı bir liste). Uygulamada, O (n) liste indeksleyicisinin hızı, insan tarafından kullanılabilen tahta boyutları için bir darboğaz değildir. Ben 2d dizisi gibi diğer türleri ile denedim, ama sonunda, ya kendi değer-eşitlik kontrol kodumu yazma ya da dizin ve geri sıra ve dosya çevirisi için takas oldu. İkincisi daha basitti. Önce çalışmasını sağlayın, ardından gerekirse veri türünüzü daha sonra optimize edin. Madde için yeterince büyük bir fark yaratması muhtemel değildir.

Sonunda, yönetim kurulu faaliyetleriniz Kurul tipi ve işlemleri tarafından uygun şekilde kapsüllendiği sürece, uygulamanızın önemi yoktur. Örneğin, testlerimden bazıları bir tahta oluşturmak için nasıl görünebilir:

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)
    |> ...

Bu koda (veya herhangi bir çağrı koduna), panonun nasıl temsil edildiği önemli değildir. Kurul, altta yatan yapısından daha fazla işlemlerle temsil edilir.


Merhaba, kod bir yerde yayınlanmış mı? Şu anda F # 'da (eğlenceli bir proje için) satranç benzeri bir oyun üzerinde çalışıyorum ve hatta tahtayı temsil etmek için bir Harita <Kare, Parça> kullandığımda, onu bir Tahtaya nasıl eklediğinizi görmek isterim tipi ve modülü.
asibahi

Hayır, hiçbir yerde yayınlanmıyor.
Kasey Speakman

şu anki uygulamama bir göz atmayı ve onu nasıl geliştirebileceğimi tavsiye eder misiniz?
asibahi

Tiplere baktığımda, Pozisyon tipine gelene kadar çok benzer bir uygulamaya karar verdim. Daha sonra Kod İncelemesi hakkında daha ayrıntılı bir analiz yapacağım.
Kasey Speakman
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.