Bir oyun kenesi için tüm olası durumları üretebileceğimi düşünüyorum, ancak dört oyuncu ve 5 temel eylem (4 hamle ve bomba yeri) ile oyun ağacının ilk seviyesinde 5 ^ 4 eyalet veriyor.
Doğru! Her oyun kene için 5 ^ 4 (hatta 6 ^ 4, tüm yönlerde yürümek, durdurmak ve "bomba koymak?") Eylemleri aramak gerekir. AMA, bir oyuncu zaten hareket etmeye karar verdiğinde, hareketin gerçekleşmesi biraz zaman alır (örn. 10 oyun kenesi). Bu dönemde olasılıkların sayısı azalır.
Bu değer bir sonraki her seviyede katlanarak artacaktır. Bir şey mi kaçırıyorum? Uygulamanın herhangi bir yolu var mı yoksa tamamen farklı bir algoritma mı kullanmalıyım?
Bir karma tabloyu yalnızca aynı oyun durumu "alt ağacı" nı bir kez hesaplamak için kullanabilirsiniz. Oyuncu A'nın yukarı ve aşağı yürüdüğünü düşünün, diğer tüm oyuncular "beklerken", aynı oyun durumundasınız. "Sol-sağ" veya "sağ-sol" ile aynıdır. "Yukarı-sonra-sola" ve "sola-yukarı-yukarı" hareket ettirmek de aynı duruma neden olur. Karma Tablosu kullanarak, daha önce değerlendirilmiş bir oyun durumu için hesaplanan puanı "yeniden kullanabilirsiniz". Bu büyüme hızını oldukça azaltır. Matematiksel olarak, üstel büyüme fonksiyonunuzun tabanını azaltır. Karmaşıklığı ne kadar azalttığı hakkında bir fikir edinmek için, eğer oyuncu sadece yukarı / aşağı / sola / sağa / durdurma hareket edebiliyorsa, haritadaki erişilebilir konumlara (= farklı oyun durumları) kıyasla sadece bir oyuncu için mümkün olan hamlelere bakalım. .
derinlik 1: 5 hareket, 5 farklı durum, bu özyineleme için 5 ek durum
derinlik 2: 25 hareket, 13 farklı durum, bu özyineleme için 8 ek durum
derinlik 3: 6125 hareket, 25 farklı durum, bu özyineleme için 12 ek durum
Bunu görselleştirmek için kendinize cevap verin: haritadaki hangi alanlara bir hamle, iki hamle, üç hamle ile ulaşılabilir. Cevap: Başlangıç konumundan maksimum mesafe = 1, 2 veya 3 olan tüm alanlar.
Bir HashTable kullanırken, her erişilebilir oyun durumunu (örnek 3'te derinlik 3'te) yalnızca bir kez değerlendirmeniz gerekir. Bir HashTable olmadan bunları birden çok kez değerlendirmeniz gerekir, bu da derinlik seviyesi 3'te 25 yerine 6125 değerlendirme anlamına gelir. En iyisi: Bir HashTable girişini hesapladıktan sonra, sonraki zaman adımlarında tekrar kullanabilirsiniz ...
Ayrıca, daha derinlemesine araştırmaya değmeyen artımlı derinleştirme ve alfa-beta budama "kes" alt ağaçlarını da kullanabilirsiniz. Satranç için bu, aranan düğümlerin sayısını yaklaşık% 1'e düşürür. Alfa-beta budamaya kısa bir giriş burada bir video olarak bulunabilir: http://www.teachingtree.co/cs/watch?concept_name=Alpha-beta+Pruning
İleri çalışmalar için iyi bir başlangıç http://chessprogramming.wikispaces.com/Search . Sayfa satrançla ilgilidir, ancak arama ve optimizasyon algoritmaları tamamen aynıdır.
Oyuna daha uygun olacak bir başka (ama karmaşık) AI algoritması "Geçici Fark Öğrenme" dir.
Saygılarımızla
Stefan
Not: Olası oyun durumlarının sayısını azaltırsanız (örneğin haritanın çok küçük boyutu, oyuncu başına sadece bir bomba, başka bir şey yok), tüm oyun durumları için bir değerlendirmeyi önceden hesaplama şansı vardır.
--Düzenle--
Bir nöron ağını eğitmek için minimum hesaplamaları çevrimdışı hesaplanmış sonuçlarını da kullanabilirsiniz. Veya bunları elle uygulanan stratejileri değerlendirmek / karşılaştırmak için kullanabilirsiniz. Örneğin, önerilen “kişilikler” in bazılarını ve hangi durumlarda hangi stratejinin iyi olduğunu tespit eden bazı buluşsal yöntemler uygulayabilirsiniz. Bu nedenle durumları "sınıflandırmalısınız" (örneğin oyun durumları). Bu aynı zamanda bir nöronal ağ tarafından da ele alınabilir: El kodlu stratejilerden hangisinin mevcut durumda en iyi sonucu verdiğini tahmin etmek ve onu yürütmek için bir nöronal ağ eğitin. Bu, gerçek bir oyun için son derece iyi gerçek zamanlı kararlar üretmelidir. Çevrimdışı hesaplamaların ne kadar sürdüğü önemli değil (oyundan önce), aksi takdirde elde edilebilen düşük derinlik sınırındaki bir aramadan çok daha iyi.
- düzenle # 2 -
En iyi hamlelerinizi yalnızca her 1 saniyede bir yeniden hesaplarsanız, daha yüksek seviye planlaması yapmaya da çalışabilirsiniz. Bununla ne demek istiyorum? 1 saniyede kaç hamle yapabileceğinizi biliyorsunuz. Böylece ulaşılabilir konumların bir listesini yapabilirsiniz (örneğin, bu 1 saniyede 3 hamle olursa, 25 ulaşılabilir pozisyonunuz olacaktır). Sonra şöyle planlayabilirsiniz: "X konumuna gidin ve bir bomba yerleştirin". Bazılarının önerdiği gibi, yönlendirme algoritması için kullanılan bir "tehlike" haritası oluşturabilirsiniz (x konumuna nasıl gidilir? Hangi yol tercih edilmelidir [çoğu durumda bazı değişiklikler olabilir]). Bu, büyük bir HashTable'a kıyasla daha az bellek tüketir, ancak daha az optimum sonuç verir. Ancak daha az bellek kullandığı için önbellekleme efektleri nedeniyle daha hızlı olabilir (L1 / L2 bellek önbelleklerinizin daha iyi kullanılması).
EK OLARAK: Kaybedilen varyasyonları sıralamak için her biri bir oyuncu için hamle içeren ön aramalar yapabilirsiniz. Bu nedenle diğer tüm oyuncuları oyundan çıkarın ... Her oyuncunun kaybetmeden hangi kombinasyonları seçebileceğini kaydedin. Yalnızca kaybedilen hamleler varsa, oyuncunun en uzun süre hayatta kaldığı hareket kombinasyonlarını arayın. Bu tür ağaç yapılarını saklamak / işlemek için, aşağıdaki gibi dizin işaretleyicileri olan bir dizi kullanmalısınız:
class Gamestate {
int value;
int bestmove;
int moves[5];
};
#define MAX 1000000
Gamestate[MAX] tree;
int rootindex = 0;
int nextfree = 1;
Her durumun bir değerlendirme "değeri" vardır ve dizi ağacını "ağaç" içinde hareketlerle depolayarak (0 = dur, 1 = yukarı, 2 = sağ, 3 = aşağı, 4 = sol) hareket ederken bir sonraki Gamestates'e bağlanır [0 ] hareket etmek [4]. Ağacınızı özyinelemeli olarak oluşturmak için bu şöyle görünebilir:
const int dx[5] = { 0, 0, 1, 0, -1 };
const int dy[5] = { 0, -1, 0, 1, 0 };
int search(int x, int y, int current_state, int depth_left) {
// TODO: simulate bombs here...
if (died) return RESULT_DEAD;
if (depth_left == 0) {
return estimate_result();
}
int bestresult = RESULT_DEAD;
for(int m=0; m<5; ++m) {
int nx = x + dx[m];
int ny = y + dy[m];
if (m == 0 || is_map_free(nx,ny)) {
int newstateindex = nextfree;
tree[current_state].move[m] = newstateindex ;
++nextfree;
if (newstateindex >= MAX) {
// ERROR-MESSAGE!!!
}
do_move(m, &undodata);
int result = search(nx, ny, newstateindex, depth_left-1);
undo_move(undodata);
if (result == RESULT_DEAD) {
tree[current_state].move[m] = -1; // cut subtree...
}
if (result > bestresult) {
bestresult = result;
tree[current_state].bestmove = m;
}
}
}
return bestresult;
}
Bu tür ağaç yapısı çok daha hızlıdır, çünkü dinamik olarak bellek ayırmak gerçekten çok yavaştır! Ancak, arama ağacını saklamak da oldukça yavaş ... Bu daha çok ilham kaynağı.