Bilinen kuyruklu bir maç oyununu optimize etmek için algoritma


10

Flowerz olarak bilinen bir oyun için C # .NET bir çözücü yazmaya çalışıyorum. Referans olması için MSN'de oynatabilirsiniz: http://zone.msn.com/gameplayer/gameplayer.aspx?game=flowerz . Eğlence için yazıyorum, herhangi bir görev veya işle ilgili herhangi bir şey için değil. Bu nedenle, tek sınır bilgisayarımdır (8GB RAM ile bir intel i7 çekirdeği). Endişelendiğim kadarıyla başka bir yerde çalışmasına gerek yok.

Kısacası, kuralları şöyle:

  • Renkli çiçeklerle dolu bir kuyruk var. Uzunluğu keyfi
    • Sıra etkilenemez
    • Kuyruk, seviyenin başında oluşturulur
  • Çiçekler bir veya iki renge sahiptir.
    • İki renk varsa, bir dış renk ve bir iç renk vardır. İki renk durumunda, dış renk eşleştirme için kullanılır.
    • Bir eşleşme varsa, dış renk kaybolur ve çiçek artık iç çiçekle aynı renkte tek bir renk çiçektir.
  • Oyunun amacı, aynı renkteki üç (veya daha fazla) eşleşmeyi oluşturmak
    • Tek bir renkteki bir çiçek bir eşleşmenin parçası olduğunda, boş bir alan oluşturarak oyun alanından kaldırılır.
    • Tek renkli bir çiçeği iki renkli bir çiçeğin dış rengiyle eşleştirebilirsiniz. Bu durumda, tek renkli çiçek kaybolur, iki renkli çiçeğin dış rengi kaybolur ve iç renk kalır
  • Sıra boş olduğunda ve en az bir boş alan kaldığında raundu kazanırsınız
  • Basamaklı maçlar mümkündür. Bir çağlayan, üç (veya daha fazla) dış çiçeğin kaybolması ve iç renklerinin başka bir 3 (veya daha fazla çiçek) zinciri oluşturmasıdır.
  • Oyun alanı her zaman 7x7
  • Sahadaki bazı alanlar kayalarla kaplıdır
    • Kayaların üzerine çiçek koyamazsın
  • Kuyrukta, yerleştirilmiş herhangi bir çiçeği boş bir alana taşımak için kullanabileceğiniz bir kürek de bulunabilir.
    • Küreyi kullanmalısın, ama aslında çiçeği hareket ettirmek zorunda değilsin: geldiği yerden geri yerleştirmek tamamen yasal.
  • Kuyruk renkli bir kelebek de içerebilir. Bu kelebeği bir çiçek üzerinde kullandığınızda, çiçek kelebeğin rengini alır
    • Bir kelebeğin iki renge sahip bir çiçeğe uygulanması, çiçeğin tek bir renk elde etmesine, yani kelebeğin
    • Kelebeği boş bir alana veya zaten bu renge sahip bir çiçeğe harcayabilirsiniz
  • Sahayı temizlemek oyunu kazanmaz

Çözücünün amacı basit: kuyruğu boşaltmanın bir yolunu bulun ve oyun alanında mümkün olduğunca fazla boşluk bırakın. Temel olarak, AI oyunu benim için oynuyor. Çözücünün çıktısı, bulduğu hareketleri içeren bir listedir. Skorla ilgilenmiyorum, ancak mümkün olduğunca uzun süre hayatta kalmakla, bu yüzden mümkün olduğunca çok açık alan bırakan hareketlerle ilgileniyorum.

Söylemeye gerek yok, arama alanı hızlı bir şekilde büyür, kuyruk büyür, bu yüzden kaba bir güç söz konusu değildir. Sıra 15'ten başlar ve doğru hatırlarsam her iki veya üç seviyede 5 büyür. Ve elbette, ilk çiçeğin (0,0) ve ikincisinin (0,1) üzerine yerleştirilmesi, ilk çiçeğin (1,0) üzerine ve ikinci çiçeğin (0,0) üzerine yerleştirilmesinden farklıdır, özellikle tarla daha önceki bir turdan çiçeklerle dolu. Böylesine basit bir karar verme konusunda fark yaratabilir.

Sorularım şunlardır:

  • Bu nasıl bir problem? (seyahat eden satıcıyı, sırt çantasını veya başka bir kombinatoryal problemi düşünün). Bunu bilmek Google-fu'umu biraz daha iyi hale getirebilir.
  • Ne tür bir algoritma bana iyi sonuçlar verebilir?

İkincisi ile ilgili olarak: İlk başta kendi sezgisel algoritmamı yazmaya çalıştım (temel olarak: kuyruğu bilseydim nasıl çözerim?), Ancak bu çok sayıda son durum ve atlayabileceğim puanlama eşleşmesi ile sonuçlanır.

Genetik bir algoritma kullanmayı düşünüyordum (çünkü en azından bunu nasıl kullanacağımı biliyorum ...), ancak tahtanın ikili sunumuna karar vermede bazı sorunlar yaşıyorum. Sonra crossover sorunu var, ancak sipariş edilen bir crossover operatörü veya benzer bir işlemle çözülebilir.

Tahminime göre çözücü her zaman pano yapılandırmasını ve boşaltmaya çalıştığı kuyruğu bilmelidir.

Sinir ağları ve bulanık mantık sistemleri gibi birkaç sezgisel algoritmayı biliyorum, ancak hangisinin en uygun olduğunu veya eldeki işe daha uygun başkaları olup olmadığını bilmek için deneyimim yok.


Bir keresinde üzerinde çalıştığım bazı karmaşık oyunların arama alanının 32Gb olacağını öğrendim. O zaman (20Mb disk sürücüm vardı) bu mümkün değildi, ama bu günlerde bazı bilgisayarlar için RAM'de hemen hemen yapılabilir.
Jonathan

Sadece bir renkteki çiçekler eşleştiğinde tamamen kaybolur mu? Ve iki renkteki çiçekler dış katmanlarını tek renkli bir çiçeğin tek rengiyle eşleştirebilir mi? Ben her iki sayımda bunu
varsayıyorum

@StevenStadnicki Teşekkürler! Bu bilgiyi orijinal soruya ekledim.
user849924

1
Küçük bir not olarak, bu sorunun ezici bir şekilde, bu sorunun 'boolean' versiyonunun (çiçekleri sonunda tahtayı tamamen boş bırakmak için sıraya koymanın bir yolu var mı?) NP-tamdır; NP-complete olan Clickomania sorununa ( erikdemaine.org/clickomania ) bariz benzerlikler taşır ve sorun, NP'den daha zor değildir, çünkü iddia edilen bir çözüm (polinom uzunluğunda) verildiğinde, sadece simülasyonu kullanarak doğrulamak kolaydır. Bu, optimizasyon sorununun muhtemelen FP ^ NP'de olduğu anlamına gelir.
Steven Stadnicki

Yanıtlar:


9

İlk bakışta , bu bana tek bir ajan arama sorunu gibi geliyor . Yani: bir temsilciniz var (AI "oyuncusu"). Oyun tahtası ve kuyruğun durumunu temsil eden bir oyun durumu var ve belirli bir durumdan yeni durumlar oluşturabilen bir halef işleviniz var.

Ayrıca , devletin "çözülmüş" durum olduğunu söyleyen bir hedef kriteri de vardır . Ve bir yol maliyeti - belirli bir duruma ilerlemenin maliyeti (bu durumda her zaman "1 hareket").

Bu tür bir prototip bulmaca 15 Bulmaca . Ve bunu çözmenin tipik yolu bilgili bir aramadır - örneğin, klasik sezgisel arama A * ve varyantları.


Ancak bu ilk bakışta bir sorun var. A * gibi algoritmalar size bir hedefe en kısa yolu verecek şekilde tasarlanmıştır (örneğin: en az sayıda hamle). Senin durumunda, hamle sayısı her zaman sabittir - hayır kısa yol yoktur - Bir sezgisel arama sadece seni verecektir böylece bir yolunu bir tamamlanmış oyun.

İstediğiniz, size en iyi tamamlanmış oyun durumunu veren bir dizi hamle .

Yapmanız gereken şey sorunu biraz tersine çevirmek. Oyun tahtası "durum" olmak yerine, hamlelerin sırası "durum" haline gelir. (Yani: Öğeleri sıraya "D2, A5, C7, B3, A3, ..." konumlarına yerleştirin.)

Bu, bu devletlerin nasıl oluşturulduğunu gerçekten umursamadığımız anlamına gelir. Kurulun kendisi tesadüfi olup, sadece belirli bir devletin kalitesini değerlendirmek için gereklidir.

Bu, sorunu yerel bir arama algoritmasıyla çözülebilen bir optimizasyon problemine dönüştürür (temel olarak belirli bir durumun etrafında durumların oluşturulması ve durumlar arasındaki yolu önemsemeden en iyi durumun seçilmesi anlamına gelir.)

Bu tür prototip bulmaca Sekiz Kraliçe Bulmaca .

Bu problem sınıfında, iyi bir çözüm bulmak için durum alanını araştırıyorsunuz, burada "iyi" objektif bir fonksiyon tarafından değerlendirilir (aynı zamanda bir değerlendirme fonksiyonu veya genetik algoritmalar için bir fitness fonksiyonu olarak da adlandırılır ).

Sorununuz için, bir nesnel işlev 0 ile N arasında bir değer döndürebilir, kuyruktaki bir hata durumuna ulaşmadan önce kullanılan öğe sayısı için (burada N, kuyruğun uzunluğudur). Aksi takdirde, N + M değeri, burada M, sıra boş olduktan sonra tahtada kalan boş alan sayısıdır. Bu şekilde - değer ne kadar yüksek olursa, "nesnel olarak daha iyi" çözüm.

(Bu noktada, boku oyunu çalıştıran koddan optimize etmeniz gerektiğini belirtmek gerekir - bu, bir durumu objektif işlev için kullanılabilecek bitmiş bir panoya dönüştürür.)


Yerel arama algoritmalarının örneklerine gelince : Temel örüntü, belirli bir durumu alan, onu değiştiren ve daha iyi sonuç veren bir sonraki duruma doğru ilerleyen bir tepe tırmanma araştırmasıdır.

Açıkçası bu yerel maksimumlarda (ve benzerlerinde) sıkışabilir. Bu formda açgözlü bir yerel arama denir . Bu ve diğer sorunlarla başa çıkmak için bir sürü varyasyon var ( Wikipedia sizi ele aldı ). Bunlardan bazıları (örneğin: yerel ışın arama ) aynı anda birden çok durumu izler.

Bununla ilgili belirli bir varyasyon genetik algoritmadır ( Wikipedia ). Genetik algoritmanın temel adımları şunlardır:

  1. Bir durumu bir tür dizgeye dönüştürmenin bir yolunu belirleyin. Sizin durumunuzda bu, 1'den 49'a kadar bir sıra uzunluğu rakamı dizisi olabilir (7x7 kartındaki olası tüm yerleşimleri temsil eder, muhtemelen her biri 1 bayt depolanır). ("Kürek" parçanız, hareketin her aşaması için birbirini izleyen iki kuyruk girişi ile temsil edilebilir.)
  2. Rastgele bir üreme popülasyonu seçin, daha iyi zindeliği olan durumlara daha yüksek olasılık verir . Üreme popülasyonu, orijinal popülasyonla aynı boyutta olmalıdır - orijinal popülasyondan durumları birkaç kez seçebilirsiniz.
  3. Damızlık popülasyondaki eşleşme durumları (ilki ikincisiyle, üçüncü dördüncü ile vs.)
  4. Her çift için rastgele bir geçiş noktası seçin ( dizgideki bir konum).
  5. Çaprazlama noktasından sonra dizenin bir kısmını değiştirerek her çift için iki yavru oluşturun.
  6. Her bir yavru durumu rastgele mutasyona uğratın. Örneğin: dizede rastgele bir konumu rastgele bir değere değiştirmeyi rastgele seçer.
  7. Nüfus bir veya daha fazla çözelti (veya belirli sayıda nesilden sonra veya yeterince iyi bir çözüm bulunana kadar) birleşene kadar işlemi yeni popülasyonla tekrarlayın.

Genetik bir algoritma çözümü , bazı ayarlamalar ile probleminiz için uygun olabileceğini düşünüyor. Gördüğüm en büyük zorluk, yukarıdaki dize gösterimi ile, çok farklı ön yarılara sahip devletlerin kuyruk yarısını değiştirmenin muhtemelen "ölü" durumlarla sonuçlanacağını göreceksiniz (iki yarı arasındaki çelişkili hareketler nedeniyle, bu sonuç düşük bir fitness skorunda).

Belki de bu sorunun üstesinden gelmek mümkündür. Akla gelen bir fikir, benzer ön yarıya sahip devletlerin üreme çiftleri olma olasılığını arttırmaktır. Bu, eşleştirmeden önce devletlerin üreme popülasyonunu sıralamak kadar basit olabilir. Ayrıca, üretim sayısı arttıkça, geçişin olası konumunu dizenin başlangıcından sonuna kadar kademeli olarak hareket ettirmeye yardımcı olabilir.

"Kare dolu" başarısızlık durumuyla karşılaşmaya karşı daha dirençli (belki de tamamen bağışık) bir durum içindeki hareketlerin bir temsilini bulmak da mümkün olabilir. Belki de hamleleri önceki hamlenin göreceli koordinatları olarak temsil etmek. Ya da hamle yapmak, verilen konuma en yakın boş alanı seçer.

Bunun gibi önemsiz olmayan AI problemlerinde olduğu gibi, bazı önemli müdahaleler gerektirecektir.

Daha önce de belirttiğim gibi, diğer büyük zorluk objektif işlevinizi optimize etmektir. Bunu daha hızlı yapmak, çok fazla alan aramanıza ve daha uzun kuyruklara sahip oyunlara çözüm aramanıza olanak tanır.


Bu cevap için, özellikle tüm terminolojiyi doğru bir şekilde elde edebilmek için, üniversitenin yapay zeka ders kitabını, Russell ve Norvig'in "Yapay Zeka: Modern Bir Yaklaşım" ı kazmak zorunda kaldım. Emin değilim "iyi" (başka bir AI metin ile karşılaştırmak yok), ama kötü değil. En azından oldukça büyük;)


Bu sorunu bir çaprazlama ile de tanımladım: Bir çocuğun kuyrukta mevcut olandan daha fazla eşyaya sahip olması çok iyi olabilir (TSP için GA eksikliği var: Şehirleri iki kez veya daha fazla ziyaret edebilir (veya hiç!) Belki sıralı bir crossover ( permutationcity.co.uk/projects/mutants/tsp.html ) işe yarayabilir.Bu özellikle durum hareket sırasını yaptığınızda geçerlidir
user849924

Bunun doğru olduğundan emin değilim - aklımda, başarısızlık durumu, bir parçanın zaten işgal edilmiş bir konuma yerleştirilmesidir (böylece oyunu erken bitirerek düşük bir fitness skoru ile sonuçlanır). Böylece kuyruk uzunluğu genetik dizginin uzunluğuyla eşleşir - asla yanlış uzunluk değildir. Yine de - takas ve sipariş verme fikriyle bir şeyin üzerinde olabilirsiniz. Verilen bir sipariş tamamlanmış bir oyunla sonuçlanırsa ve iki hamleyi değiştirirseniz, mutasyona uğramış durumun da tamamlanmış bir oyun olma şansının, sadece bir (veya iki?) Hamle pozisyonunu rastgele ayarlamanızdan çok daha iyi olduğunu düşünüyorum. .
Andrew Russell

Başarısızlık durumu, hamle yerleştirmek için başka seçeneğiniz olmadığında, yani boş alanlarınız bittiğinde ve bu hamleyle hiçbir eşleşmenin olmadığı durumdur. Söylediklerinize benzer: onu zaten işgal edilmiş bir konuma yerleştirmelisiniz (ancak bu sadece başlamak için daha fazla yer olmadığında doğrudur). Gönderdiğim geçit ilginç olabilir. Kromozom A'nın A1, B1, ..., G1, A2, B2 ve C2 üzerine yerleştirilmiş öğeleri ve G7 ... A7, G6, F6 ve E6 üzerine B kromozomu vardır. A'dan birkaç rastgele seçim yapın ve dizinlerini koruyun. A'nın B'den tamamlayıcısını seçin ve indekslerini koruyun ve bir çocuk için birleştirin.
user849924

Bu geçiş ile ilgili 'sorun' aynı noktada birden fazla harekete izin verilmesidir. Ancak bu, Stefan K'nin çözümünden SimulateAutomaticChanges'e benzer bir şeyle kolayca çözülebilir olmalıdır: Çocuğun hamle / durumunu oyun alanının temel durumuna (tüm hareketleri tek tek uygulayın) ve kabul durumu (boş kuyruk) ) elde edilemez (çünkü dolu bir yere bir çiçek yerleştirmeniz gerekir), o zaman çocuk geçersizdir ve tekrar ürememiz gerekir. Başarısızlık durumunuz burada ortaya çıkıyor. Bunu şimdi anladım, heh. : D
user849924

Bunu iki nedenden dolayı cevap olarak kabul ediyorum. İlk olarak: GA'nın bu sorun için çalışmasını sağlamak için bana gerekli fikri verdin. İkincisi: sen ilktin. ; p
user849924

2

Sınıflandırma

Cevap kolay değil. Oyun teorisinin oyunlar için bazı sınıflandırmaları vardır, ancak o oyun için özel bir teoriyle net bir 1: 1 maç yoktur. Kombinatoryal problemin özel bir biçimidir.

Bu, son düğümden bir sonraki düğüme ulaşmak için bir miktar maliyetle "düğümleri" ziyaret ettiğiniz bir siparişe karar verecek olan satış elemanı değil. Sırayı yeniden sıralayamazsınız veya haritadaki tüm alanları kullanmanız gerekmez.

Sırt çantası eşleşmiyor çünkü bazı öğeler "sırt çantasına" yerleştirilirken bazı alanlar boş oluyor. Yani belki bunun genişletilmiş bir şekli, ama büyük olasılıkla algoritmalar bu nedenle uygulanamaz.

Wikipedia, burada kategorilere ayırma konusunda bazı ipuçları vermektedir: http://en.wikipedia.org/wiki/Game_theory#Types_of_games

Bunu "ayrık zamanlı optimal kontrol sorunu" olarak sınıflandırırdım ( http://en.wikipedia.org/wiki/Optimal_control ), ama bunun size yardımcı olacağını düşünmüyorum.

Algoritmalar

Kuyruğun tamamını gerçekten biliyorsanız, ağaç arama algoritmalarını uygulayabilirsiniz. Dediğiniz gibi, sorunun karmaşıklığı kuyruk uzunluğu ile birlikte çok hızlı büyür. Çok fazla bellek gerektirmeyen "Önce Derinlik Arama (DFS)" gibi bir algoritma kullanmanızı öneririm. Skor sizin için önemli olmadığından, ilk çözümü bulduktan sonra durabilirsiniz. İlk olarak hangi alt dalı arayacağınıza karar vermek için, sipariş vermek için bir buluşsal yöntem uygulamalısınız. Bu, bir değerlendirme fonksiyonu yazmanız gerektiği anlamına gelir (örneğin: boş alan sayısı; bu ne kadar karmaşıksa, o kadar iyidir), bu da hangi sonraki hareketin en umut verici olduğunu karşılaştırmak için bir puan verir.

O zaman sadece aşağıdaki parçalara ihtiyacınız vardır:

  1. Oyunun tüm bilgilerini depolayan oyun durumunun modeli (örn. tahta durumu / harita, kuyruk, sayı / sıradaki hareket)
  2. belirli bir oyun durumu için tüm geçerli hamleleri veren bir hamle jeneratörü
  3. bir "hamle yap" ve "hamle geri al" fonksiyonu; bir oyun durumuna belirli (geçerli) bir hamle uygulayan / geri alan. Oysa "do taşıma" işlevi "geri alma" işlevi için bazı "geri alma bilgilerini" saklamalıdır. Oyun durumunu kopyalamak ve her yinelemede değiştirmek, aramayı önemli ölçüde yavaşlatır! En azından durumu yığına kaydetmeye çalışın (= yerel değişkenler, "yeni" kullanarak dinamik ayırma yok).
  4. her oyun durumu için karşılaştırılabilir bir puan veren bir değerlendirme fonksiyonu
  5. arama fonksiyonu

Önce derinlik araması için eksik başvuru uygulaması:

public class Item
{
    // TODO... represents queue items (FLOWER, SHOVEL, BUTTERFLY)
}

public class Field
{
    // TODO... represents field on the board (EMPTY or FLOWER)
}

public class Modification {
    int x, y;
    Field originalValue, newValue;

    public Modification(int x, int y, Field originalValue, newValue) {
        this.x = x;
        this.y = y;
        this.originalValue = originalValue;
        this.newValue = newValue;
    }

    public void Do(GameState state) {
        state.board[x,y] = newValue;
    }

    public void Undo(GameState state) {
        state.board[x,y] = originalValue;
    }
}

class Move : ICompareable {

    // score; from evaluation function
    public int score; 

    // List of modifications to do/undo to execute the move or to undo it
    Modification[] modifications;

    // Information for later knowing, what "control" action has been chosen
    public int x, y;   // target field chosen
    public int x2, y2; // secondary target field chosen (e.g. if moving a field)


    public Move(GameState state, Modification[] modifications, int score, int x, int y, int x2 = -1, int y2 = -1) {
        this.modifications = modifications;
        this.score = score;
        this.x = x;
        this.y = y;
        this.x2 = x2;
        this.y2 = y2;
    }

    public int CompareTo(Move other)
    {
        return other.score - this.score; // less than 0, if "this" precededs "other"...
    }

    public virtual void Do(GameState state)
    {
        foreach(Modification m in modifications) m.Do(state);
        state.queueindex++;
    }

    public virtual void Undo(GameState state)
    {
        --state.queueindex;
        for (int i = m.length - 1; i >= 0; --i) m.Undo(state); // undo modification in reversed order
    }
}

class GameState {
    public Item[] queue;
    public Field[][] board;
    public int queueindex;

    public GameState(Field[][] board, Item[] queue) {
        this.board = board;
        this.queue = queue;
        this.queueindex = 0;
    }

    private int Evaluate()
    {
        int value = 0;
        // TODO: Calculate some reasonable value for the game state...

        return value;
    }

    private List<Modification> SimulateAutomaticChanges(ref int score) {
        List<Modification> modifications = new List<Modification>();
        // TODO: estimate all "remove" flowers or recoler them according to game rules 
        // and store all changes into modifications...
        if (modifications.Count() > 0) {
            foreach(Modification modification in modifications) modification.Do(this);

            // Recursively call this function, for cases of chain reactions...
            List<Modification> moreModifications = SimulateAutomaticChanges();

            foreach(Modification modification in modifications) modification.Undo(this);

            // Add recursively generated moves...
            modifications.AddRange(moreModifications);
        } else {
            score = Evaluate();
        }

        return modifications;
    }

    // Helper function for move generator...
    private void MoveListAdd(List<Move> movelist, List<Modifications> modifications, int x, int y, int x2 = -1, int y2 = -1) {
        foreach(Modification modification in modifications) modification.Do(this);

        int score;
        List<Modification> autoChanges = SimulateAutomaticChanges(score);

        foreach(Modification modification in modifications) modification.Undo(this);

        modifications.AddRange(autoChanges);

        movelist.Add(new Move(this, modifications, score, x, y, x2, y2));
    }


    private List<Move> getValidMoves() {
        List<Move> movelist = new List<Move>();
        Item nextItem = queue[queueindex];
        const int MAX = board.length * board[0].length + 2;

        if (nextItem.ItemType == Item.SHOVEL)
        {

            for (int x = 0; x < board.length; ++x)
            {
                for (int y = 0; y < board[x].length; ++y)
                {
                    // TODO: Check if valid, else "continue;"

                    for (int x2 = 0; x2 < board.length; ++x2)
                    {
                        for(int y2 = 0; y2 < board[x].length; ++y2) {
                            List<Modifications> modifications = new List<Modifications>();

                            Item fromItem = board[x][y];
                            Item toItem = board[x2][y2];
                            modifications.Add(new Modification(x, y, fromItem, Item.NONE));
                            modifications.Add(new Modification(x2, y2, toItem, fromItem));

                            MoveListAdd(movelist, modifications, x, y, x2, y2);
                        }
                    }
                }
            }

        } else {

            for (int x = 0; x < board.length; ++x)
            {
                for (int y = 0; y < board[x].length; ++y)
                {
                    // TODO: check if nextItem may be applied here... if not "continue;"

                    List<Modifications> modifications = new List<Modifications>();
                    if (nextItem.ItemType == Item.FLOWER) {
                        // TODO: generate modifications for putting flower at x,y
                    } else {
                        // TODO: generate modifications for putting butterfly "nextItem" at x,y
                    }

                    MoveListAdd(movelist, modifications, x, y);
                }
            }
        }

        // Sort movelist...
        movelist.Sort();

        return movelist;
    }


    public List<Move> Search()
    {
        List<Move> validmoves = getValidMoves();

        foreach(Move move in validmoves) {
            move.Do(this);
            List<Move> solution = Search();
            if (solution != null)
            {
                solution.Prepend(move);
                return solution;
            }
            move.Undo(this);
        }

        // return "null" as no solution was found in this branch...
        // this will also happen if validmoves == empty (e.g. lost game)
        return null;
    }
}

Bu kodun çalıştığı doğrulanmamış, ayrıca derlenebilir veya tam değil. Ama bunu nasıl yapacağınıza dair bir fikir vermeli. En önemli çalışma değerlendirme fonksiyonudur. Ne kadar karmaşıksa, algoritma yanlış "dener" sonra daha sonra dener (ve geri almak zorunda). Bu karmaşıklığı son derece azaltır.

Bu çok yavaşsa, HashTable olarak iki kişilik oyunların bazı yöntemlerini uygulamayı deneyebilirsiniz. Bunun için, bir çözüme götürmeyen durumları değerlendirdiğiniz ve işaretlediğiniz her oyun durumu için (yinelemeli) bir karma anahtarı hesaplamanız gerekir. Örneğin, Search () yöntemi "null" değerini her döndürmeden önce bir HashTable girdisi oluşturulmalı ve Search () girilirken bu duruma şimdiye kadar pozitif bir sonuç olmadan ulaşılıp ulaşılmadığını kontrol edersiniz ve eğer öyleyse "null" daha fazla araştırma. Bunun için büyük bir hash tablosuna ihtiyacınız olacak ve muhtemelen mevcut bir çözüm bulamamanıza neden olabilecek "hash çarpışmalarını" kabul etmek zorunda kalacaksınız, ancak hash işlevleriniz yeterince iyi ve tablonuz yeterince büyük (hesaplanabilir risk riski).

Bu sorunu (sizin tarafınızdan tarif edildiği gibi) daha verimli çözecek başka bir algoritma olmadığını düşünüyorum, değerlendirme fonksiyonunuzun en uygun olduğunu varsayalım ...


Evet, kuyruğun tamamını bilebilirim. Değerlendirme işlevinin uygulanması aynı zamanda geçerli, ancak potansiyel olarak kötü bir yerleşim olarak mı düşünülebilir? Sahada benzer bir renk varken, farklı bir renkteki çiçeğin yanına koymak gibi bir hareket olması potansiyel olarak kötü mü? Veya yer eksikliği nedeniyle tamamen farklı bir eşleşmeyi engelleyen bir yere bir çiçek yerleştirmek mi?
user849924

Bu cevap bana model ve oyun kurallarıyla nasıl çalışılacağına dair fikirler verdi, bu yüzden oylayacağım. Girdiniz için teşekkürler!
user849924

@ user849924: Evet, elbette değerlendirme işlevi bunun için bir değerlendirme "değeri" hesaplamalıdır. Mevcut oyun durumu ne kadar kötüye giderse (kaybetmeye yakınsa), iade edilen değerlendirme değeri o kadar kötü olmalıdır. En kolay değerlendirme boş alanların sayısını döndürmek olacaktır. Benzer renkteki bir çiçeğin yanına yerleştirilen her çiçek için 0.1 ekleyerek bunu geliştirebilirsiniz. İşlevinizi doğrulamak için bazı rastgele oyun durumları seçin, değerlerini hesaplayın ve karşılaştırın. A eyaletinin B eyaletinden daha iyi olduğunu düşünüyorsanız, A puanının B için olandan daha iyi olması gerekir
SDwarfs
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.