Saturday, June 15, 2013

Oyunlarda Yapay Zeka - Artificial Intelligence for Battle City [Proje]

Bu yıl üniversitedeki son dönemim, hatta bu yazıyı yazarken diplomayı alıp mezun olamama yaklaşık 1 ay gibi kısa bir süre kaldı. Mezun oluyorum diye seviniyorum, ama buruk bir sevinç. Bir çok kişinin aksine üniversitede geçirdiğim yılları özleyeceğim. Normalde geçmiş yıllardan aldığım fazladan dersler ile kredimi ziyadesiyle doldurmuştum. Aslında bu hazırlıkların hepsi son yılımın çok rahat geçmesi için yapılan yatırımlardı. Ancak geçmiş yılların yoğunluğunun verdiği alışkanlık ya da açılan seçmeli derslerin cazibesinden olsa gerek son yılım pek rahat geçmedi. Bilakis, diğer yıllara nazaran daha yoğun olduğu zamanlar bile oldu. Çünkü 1 adet bitirme projesinin yanında 2 adet projeli ders aldım. Bu projeli derslerden birisi "SE 354" kodlu, "Oyunlarda Yapay Zeka" dersiydi. Ders ilk defa verilmekle birlikte, öğretim görevlisi, üniversiteye ilk başladığım sıralar okulumuzda asistanlık yapan ve SDL ile tanışmama vesile olan Kaya Oğuz hocamızdı. Pojemiz Unity oyun motoru üzerinde C# programlama dili ve Standart Unity kütüphanelerini kullanarak "Battle City" benzeri bir oyun yapmaktı. Aslında belli bir arayüz hocamız tarafından bize sağlandı. Bizim yapmamız gereken ise bu arayüz üzerinde bulunan tanklara yapay zeka yazmak idi. Arayüzden kastım, harita, harita üzerinde bulunan çeşitli eşyalar(silah, sağlık) ve 4 tane tank bulunmaktaydı. Daha detaylı bilgi projenin anasayfasında mevcuttur. Aslında oyunun tamamını hocamız yazmış, sadece tanklar için gerekli yapay zekanın yazılmasını bizden istemişti. Proje aşaması çok eğlenceliydi. Ancak bazı noktalarda insanın kafası karışabiliyordu. Sonsuz olasılıkları düşününce iş içinden çıkılmaz bir hal alıyordu.

Yazıyı çok fazla uzatmadan, projenin bazı detaylarına değinmek istiyorum. Öncelikle her türlü ihtimali düşünemeyeceğimi farkettim. Buradan tankın belli başlı davranışları üzerinde yoğunlaşmam gerektiği gibi bir sonuç çıkıyordu. O yüzden ilk olarak tankın davranışlarını belirledim. 3 temel davranış belirledim. Ve State Machine(Durum Makinesi), bu davranışlara göre çizildi. Bunlar sırası ile Think(Düşün), Walk(Yürü) ve Attack(Saldır). Think aşaması, tankımın ne yapması gerektiği, uygulaması gereken stratejinin ne olduğunun belirlendiği bir aşama. Söz gelimi, eğer tankımın silaha ihtiyacı varsa, Think aşaması ona silah alması gerektiğini söylerken zırh sorunum varsa zırh aramam gerektiği bildiriliyor veya silah ya da zırh sorunumuz yok artık düşmanla karşılaşmanın vaktinin geldiğini belirtiliyor. Ve bize yürümemiz gereken hedefin pozisyonunu döndürüyor. Aldığımız pozisyon girdisi ile Walk aşamasına geçiyoruz. Ve bulunduğumuz konumdan gidilmesi gereken noktaya en kısa yolu A* algoritması(A Yıldız Arama Algoritması) ile bulup rotayı takip ediyoruz. Eğer düşmanla karşılaşmadan hedef noktasına varmışsak tekrar Think aşamasına geçiyoruz bir sonraki stratejimiz belirleniyor. Ve oyun bu şekilde devam ediyor. Şayet yolda ilerlerken düşman ile karşılaşırsak, Attack aşamasına geçiş yapıp düşmanla savaşmaya başlıyoruz. Yazdığım State Machine genel olarak bu şekilde çalışıyor. Ancak burada önemli olan bir kısım var oda Think aşaması. Yani tankım ne yapması gerektiğine nasıl karar veriyor? Stratejisini belirleyen şeyler neler?


Strateji belirlerken kullandığımız yöntemlerden birisi discontentment(hoşnutsuzluk) hesaplama yöntemi. Bu yönteme göre hoşnutsuzluğumuzu belirleyen birkaç faktör bulunmaktadır. Bu faktörlerin azaltılması için uygulanacak stratejiler mevcuttur. Anlık hoşnutsuzluğumuzu totalde minimize edecek strateji hangisi ise bulunup seçilir ve uygulamaya konur. Öncelikle benim yazdığım yapay zeka için kullandığım faktörleri belirteyim. Bu faktörler sırası ile Point(Puan), Armour(Zırh) ve Ammo(Mermi) olmakla birlikte başlangıç için her birinin değeri 1, 0, 5 idir. Hoşnutsuzluk hesaplaması ise aşağıdaki gibi basit bir şekilde hesaplanır.
Discontentment = Point^2 + Armour^2 + Ammo^2
Discontentment = 1^2 + 0^2 + 5^2
Discontentment = 1 + 0 + 25
Discontentment = 26
Yukarıda yapılan hesaplamaya göre tankın toplam hoşnutsuzluğu 26 birimdir. Bunu mümkün mertebede azaltacak olan stratejiyi belirlemekte Think(Düşün) davranışı içinde çağrılan "TaskManager" sınıfının görevidir. Bu sınıf içerisinde varolan görevler ve bu görevlerin faktörler üzerine olan etkisine göre en uygun stratejiyi belirler. Aşağıdaki tablo benim belirlediğim görevler ve bu görevlerin faktörler üzerine etkisini göstermektedir. Sözgelimi eğer, "TaskCannon" görevini uygulamak istiyorsak, bu görev Point faktörü üzerinde +2 artışa, Armour faktörü üzerinde etkisiz, Ammo faktörü üzerinde -4 azalışa sebep olacaktır. Bu stratejinin uygulanmasının sonucunda yapılan yeni hesaplamaya göre hoşnutsuzluk birimimiz 26'dan, 10'a düşecektir. Bu hesaplamayı her bir strateji için uygulayıp, en küçük değeri veren strateji uygulamak ise tankımız için uygulanabilecek en iyi stratejidir.
 --------------------------------------------------
| Task                    | Point  | Armour | Ammo |
 --------------------------------------------------
| TaskHeavyMachineGun     |   1    |    0   |  -1  |
| TaskGrenade             |   1    |    0   |  -2  |
| TaskMissile             |   1    |    0   |  -3  |
| TaskCannon              |   2    |    0   |  -4  |
| TaskBFG                 |   3    |    1   |  -6  |
| TaskRepairKit           |   2    |   -2   |   1  |
| TaskArmour              |   2    |   -2   |   1  |
| TaskInvulnerability     |  -3    |   -1   |   2  |
| TaskHexDamage           |  -3    |    2   |  -2  |
 --------------------------------------------------

New Calculation

Point  = 1 + 2 = 3
Armour = 0 + 0 = 0
Ammo   = 5 - 4 = 1

Discontentment = Point^2 + Armour^2 + Ammo^2
Discontentment = 3^2 + 0^2 + 1^2
Discontentment = 9 + 0 + 1
Discontentment = 10

Calculations for each Tasks

 ---------------------------------------------------------------------
| Task                    |                Calculation                |
 ---------------------------------------------------------------------
| TaskHeavyMachineGun     | (2^2) + (0^2) + (4^2) =  4 +  0 + 16 = 20 |
| TaskGrenade             | (2^2) + (0^2) + (3^2) =  4 +  0 +  9 = 13 | 
| TaskMissile             | (2^2) + (0^2) + (2^2) =  4 +  0 +  4 =  8 |
| TaskCannon              | (3^2) + (0^2) + (1^2) =  9 +  0 +  1 = 10 |
| TaskBFG                 | (4^2) + (1^2) + (0^2) = 16 +  1 +  0 = 17 |
| TaskRepairKit           | (3^2) + (0^2) + (6^2) =  9 +  0 + 36 = 45 |
| TaskArmour              | (3^2) + (0^2) + (6^2) =  9 +  0 + 36 = 45 |
| TaskInvulnerability     | (0^2) + (0^2) + (7^2) =  0 +  0 + 49 = 49 |
| TaskHexDamage           | (0^2) + (2^2) + (3^2) =  0 +  4 +  9 = 13 |
 ---------------------------------------------------------------------

According to calculations, best strategy is TaskMissile.
Tabii, yukarıda belirlediğim 3 faktör benim kendi seçimim. Siz daha farklı şeyleri de düşünebilirsiniz. Mesela o stratejiyi uygulamak için gidilmesi gereken mesafa, bu mesafe boyunca düşmanla karşılaşıp savaşma olasılığımız vs. gibi ayrıntılı durumlar koda eklemlenebilir. Ayrıca aşağıda proje sunumlarını gerçekleştirdiğimiz gün 3 tank arasında yapılan mücadelenin görüntülerini izleyebilirsiniz.


Proje genel hatları ile bu kadar. Benim yazdığım kodları indirmek için bu linke tıklayınız. Oyun için Kaya Oğuz hocamız tarafından sağlanan interface için ise projenin anasayfası ve/ya github linkine bakabilirsiniz.

1 comment:

  1. merhaba. ben de yapay zekaya yeni başladım. ben prolog ile başladım programlamaya. verebileceğiniz öğütler var mıdır? ileride de bu alanda ilerlemek isteyen birine neler tavsiye edersiniz? teşekkürler.

    ReplyDelete