Hearthstone, 2013 yılında Blizzard firması tarafından geliştirilmiş, internet üzerinden oynanabilen ve çeşitli kartların kullanılması ile yapılan bir savaş oyundur. Klasik kart oyunundan farklı olarak, bu oyunda zafer kazanmak sadece kartların özelliklerine değil, oyuncunun stratejisine de önemli miktarda bağlıdır.
Bu kart oyununda her oyuncu kendi stratejisine göre aynı karttan en fazla iki tane olacak şekilde 30 karttan oluşan bir deste oluşturur. Oyun başlangıcında her oyuncunun 30 can puanı ve 3 kartı bulunur Oyuncu, kendi sırasında (turunda) savaş alanına (board) yeni kartlar oynayabilir veya hâlihazırda savaş alanında bulunan kartları ile saldırı yapabilir. Ancak, oyuna yeni kartlar kullanılabilmesi için mana adı verilen, kartlar için farklı değerde bir bedel ödenmesi gerekir. Oyun her oyuncu için 1 mana ile başlar ve en fazla 10 olacak şekilde her el mana sayısı 1 artar. Her tur başlangıcında mana değeri sıfırlanır ve kullanılmayan manalar sonraki tura aktarılmaz ancak oynanmayan kartlar sonraki turlarda oynanabilir.
Oyundaki kartlar çeşitli canavarlardan (yeşil renk) ve büyülerden (mavi renk) oluşur. Savaş alanında sürülen canavar kartları hayatta oldukları sürece savaşabilirken, büyü kartları ise sadece bir kez ve doğrudan kullanılabilir. Bu da oyuna farklı bir dinamik yapı kazandırmakta, tercihler için farklı bir öncelik yapısı oluşturmaktadır.
Oynanan canavar kartlarının rakibe hasar puanı ve kendi can puanı vardır. Örnek bir oyun kartı üstte görüldüğü gibidir. Canavarlar aksi bir özellik verilmemişse oyuna sürüldükten 1 el sonra kullanılabilir hale gelir. Bu kartlar ile rakip oyuncuya ya da rakip oyuncunun canavar kartlarına hasar verebilir. Ancak oyuncunun canavarına saldırılması durumunda saldırıyı yapan kişi de hasar alır.
Kartların saldırı, can ve mana değerlerinden hariç, kendilerine özel özellikleri olabilir. Bu özelliklerden önemli olanları aşağıda belirtilmiştir.
Taunt (Sarı Renk): Rakibin diğer düşmanlara hasar verebilmesi için öncelikle bu kartı yok etmesi gerekir.
Charge (Kırmızı Renk): Oyuna sürüldüğü anda saldırabilir.
Battlecry: Kart oyuna sürüldüğü anda detay bilgisinde belirtilen ek özelliği tetikler.
Deathrattle: Kart savaş meydanında öldüğü sırada detay bilgisinde yazan özelliği tetikler.
Bu çalışma kapsamında, bahsi geçen özellikleri dikkate alarak, elindeki kartları ve kendi manasını uygun şekilde kullanan, böylece rakibin can puanını bitirerek yenmek için oynayan bir yapay zekâ programlanması amaçlanmıştır. Yapılan çalışmalar MSSQL ve C# programlama dilleri ile ve “.NET” kütüphanesi kullanılarak yazılmıştır. Oyunun örnek bir ekran görüntüsü ise aşağıdaki gibidir.
Bu çalışmada ise, eldeki ve savaş alanındaki kartların farklı stratejiler, sezgisel yaklaşımlara göre oynanması ve rakibin can puanlarının bitirilmesi için bir yapay zekâ sistemi oluşturulmuştur. Oluşturulan bu sistem ile farklı gerçek oyunculara karşı oynanan oyunlarda %63 oranında yapay zekâ oyuncusu ile zafer elde edilmiştir.
Kullanılan Stratejiler, Sezgisel Yaklaşımlar ve Algoritmalar
Hearthstone oyunu sırasında bir oyuncu kendi sırası boyunca yeni kartlar oynayabilir veya savaş meydanındaki canavarları ile saldırı yapabilir. Dolayısı ile yapay zekânın oynayacağı tur bölümü kart oynama ve saldırı bölümü olarak iki parça halinde ele alınabilir. Bu yapı ve alt basamaklarının şematik gösterimi aşağıda gösterildiği gibidir.
Bu çalışmada, yapay zekâ tarafından seçilen yaklaşımlara göre 3 farklı seviyeye ayrılmıştır. Seviyeler arasında zorluk derecesi oynanacak kartların ve yapılacak saldırıların seçimlerine bağlı olarak oluşturulmuştur.
- Rastgele (Random): Bu seviyedeki yapay zekâ elindeki kartları manası el verdiği şekilde ve manası bitene kadar rastgele oynamakta, saldırı sırasında ise düşmanın taunt özelliğine sahip kartları mecburi öncelikli olmak üzere rastgele hedeflere yapmaktadır. Bu zorluk seviyesinde saldırıyı yapan canavar düşmanda ölümcül bir hasar oluşturamadan sadece kendinin ölümüne de sebebiyet verebilmektedir.
- Yeni Başlayan (Beginner): Bu seviyede yapay zekâ elindeki kartlar arasından manasının izin verdiği şekilde saldırı, can puanı ve diğer temel özelliklerin birleşimine göre hesaplanan bir güç değerine göre seçmekte, saldırı sırasında ise mümkün olduğunca doğrudan oyuncuya saldıran, agresif bir strateji izlemektedir. Bu strateji, hızlıca sonuca ulaşabildiği gibi, karşı oyuncunun kendisine vereceği hasarı göz ardı etmesi nedeniyle savaşta zayıf düşebilmektedir. Ayrıca bu seviyede yapay zekâ oyuncusu strateji gereği öncelikle oynayabiliyorsa canavar kartlarını tercih etmekte, tek seferde daha çok hasar verme yetisine sahip büyü kartlarını oyunun ilerleyen bölümlerine saklamayı tercih etmektedir. Böylelikle kazanmaya yaklaştığı durumda güçlü bir hasar verebilmekte, hem de düşmanının savunmasından etkilenmemektedir.
- Yetenekli (Skilled): Bu seviyedeki yapay zekâ oyuncusu vereceği hasarı arttırmak isterken, alacağı hasarı da azaltmaya çalışmaktadır. Bu nedenle, kart oynama ve saldırı turları farklı olasılıklar nedeniyle ufak ama çok sayıda arama uzaylarına dönüşmektedir.
Bu seviyedeki yapay zekâ oyuncusu öncelikle sırasında elindeki kartları temel seviyeye benzer şekilde fakat daha karmaşık bir sezgisel hesaplama ile sıralamaktadır. Bu hesap yapılırken kartların birbirlerine göre üstünlük sağlayabilecekleri her öznitelik yapıya eklenmiştir. Daha sonra eldeki büyü kartları arasından düşmanın güçlü canavarlarını doğrudan öldürebilen kartlar için bir arama yapılmaktadır. Böylece düşman kartlarının hem kendisine hem de savaş alanındaki canavarlara hasar vermesi engellenmiş olmaktadır. Eğer böyle bir durum yoksa büyü kartları yerine canavarların oynanması tercih edilerek kartların açılmasına devam edilmektedir.
Saldırı sırası için ise saldırıya uygun haldeki canavarlar için düşman canavarlar arasında öldürüp hayatta kalacak şekilde veya kendisinden güçlü fakat öldürebileceği kadar az canı kalan canavarlar için arama yapılmaktadır. Bu arama sonucunda yapılan saldırılar oyuncunun yapay zekâ oyuncusuna vereceği hasarı mümkün olduğunca azaltmaktadır. Aksi halde yapay zekâ oyuncusunun vereceği hasardan daha çok hasar alacağı ve önündeki kartları da kaybedeceği aşikârdır. Arama uzayının bu sezgisel seçim uzayına küçültülmesi hem karmaşıklığı önemli ölçüde azaltmakta, hem de getirinin her zaman pozitif olacağını garanti etmektedir. Ek olarak, Hearthstone oyununun yapısı gereği her zaman yeni kartlar oyuna eklenmekte, savaşta ölen kartlar ise oyunu terk etmektedir. Dolayısı ile oyun her tur yeniden başlamış gibi olmaktadır. Oyuncuların karşı oyuncunun destesi hakkında hiçbir bilgiye sahip olmaması da gelecek hamleleri hakkında tahmin yapılmasını imkânsız kılmaktadır. Dolayısı ile yapay zekâ oyuncusu sadece kendi elindeki, önündeki ve düşmanının önündeki kartlar arasında arama ve seçim yapabilmektedir. Kullanılan stratejiler ve sezgisel yaklaşımlar arama uzayını daha da daraltsa da, başarılı seçim ve zafere giden yol için etkili olabilecek doğru kararlar zincirinin bulunmasını sağlamaktadır.
Yapay zekânın kart çekme ve saldırı turlarında kullanılan algoritmalar şematik olarak ve kullanılan algoritmaların sözde kod hali takip eden bölümde verilmiştir.
function AIPlayCard() Player.Board: List of minions in player’s board AIPlayer.Hand: List of minions in AI player’s hand if (gameDifficulty == GameDifficulty.Random) foreach card in AIPlayer.Hand if (card.Cost <= AIPlayer.Mana) playableCards.Add(card); while AIPlayer.Mana > 0 and playableCards.Count > 0 AIPlayCard(playableCards[rand]); UpdateMana(AIPlayer); Update(playableCards); if (gameDifficulty == GameDifficulty.Beginner) AIPlayer.Hand.OrderBy(card.Cost, card.Attack, card.Health, card.BasicAbilities); while AIPlayer.Mana > 0 and playableCards.Count > 0 AIPlayCard(AIPlayer.Hand.First()); UpdateMana(AIPlayer); UpdateHand(AIPlayer); if no other playableCards(AIPlayer.Hand) < AIPlayer.Mana break; if (gameDifficulty == GameDifficulty.Skilled) foreach card in AIPlayer.Hand if (card.Type == CardTypes.Spell) foreach pcard in Player.Board if (card.Damage >= pcard.Health && pcard.Attack >= card.Damage) AIPlayCard(card, pcard); UpdateMana(AIPlayer); AIPlayer.Hand.OrderBy(card.Cost, card.Attack, card.Health, card.AllAbilities); while AIPlayer.Mana > 0 and playableCards.Count > 0 AIPlayCard(AIPlayer.Hand.First()); UpdateMana(AIPlayer); UpdateHand(AIPlayer); if no other playableCards(AIPlayer.Hand) < AIPlayer.Mana break;
function AIAttack() Player.Board: List of minions in player’s board AIPlayer.Board: List of minions in AI player’s board if (gameDifficulty == GameDifficulty.Random) minionsWithTaunt = GetList(Player.Board, “Taunt”); while (minionsWithTaunt.Count > 0) foreach card in AIPlayer.Board if (card.HasAttacked != false) AIAttack(card, minionsWithTaunt[rand]); foreach card in AIPlayer.Board if (card.HasAttacked != false) AIAttack(card, RandTarget(Player.Hand, Player)); if (gameDifficulty == GameDifficulty.Beginner) minionsWithTaunt = GetList(Player.Board, “Taunt”).OrderBy(“Attack”); aiMinions = AIPlayer.Board.OrderBy(card.Attack); while (minionsWithTaunt.Count > 0) foreach card in aiMinions if (card.HasAttacked != false) AIAttack(card, minionsWithTaunt.First); foreach card in AIPlayer.Board if (card.HasAttacked != false) AIAttack(card, Player); if (gameDifficulty == GameDifficulty.Skilled) minionsWithTaunt = GetList(Player.Board, “Taunt”).OrderBy(“Attack”, “Health”); minionsWithoutTaunt = GetList(Player.Board, not “Taunt”); aiMinions = AIPlayer.Board.OrderBy(card.Attack, card.Health); while (minionsWithTaunt.Count > 0) foreach card in aiMinions if (card.HasAttacked != false) AIAttack(card, minionsWithTaunt); foreach card in aiMinions if (card.HasAttacked != false) // if kill and survive or do less harm then enemy target = SelectMinion(minionsWithoutTaunt.Health <= card.Attack && (card.Health >= minionsWithoutTaunt.Attack || minionsWithoutTaunt.Attack >= card.Attack)) if(target != null) AIAttack(card, target); else AIAttack(card, Player);
Performans
Bu iki kişilik kart oyununda amaç rakibin canını kendi canın bitmeden bitirebilmektir. Oyun süresince bir oyuncu destesinde en fazla 30 kart bulundurabilir. Oyun ilerledikçe desteden kartlar alınarak, elinde en fazla 10 ve oyunda ise en fazla 7 kart bulundurabilir. Dolayısı ile bir oyuncu oyunun ilerleyen bir anında, düşman kartları ile beraber, en fazla 37 kart hakkında bilgi sahibi olabilir. Ancak yapılan saldırılar ve kullanılan kartlar sonucunda bu sayı oldukça düşer.
Oyuncu bir tur içinde yapabileceği en iyi hamleye karar verirken, mevcut savaş alanındaki durum ve elindeki kartların birleşimine göre en iyi seçim dizisini yapmaya çalışır. Bir turda yapılan seçimler, diğer tura başlangıç durumları farklı olan yeni bir oyun gibi yansır. Oyuncu kendi kartları ve saldırıları için farklı hamle seçimleri oluşturabilirken, düşmanı için sadece önündeki açık kartların hamlelerini tahmin edebilmektedir. Bu durumda yapılabilecek en iyi seçenek, kendi getirisini en üst düzeyde tutacak şekilde, düşmanının hamle sayısını kısıtlamak ve mümkün olduğunca saldırı yapmasına engel olmaktır. Dolayısıyla yapay zekâ için problem olasılık uzayının büyük olmasından ziyade, akıllı seçim yapılabilmesidir.
Sonuçlar
Hearthstone oyun uzayı, kartların seçilmesi ve oynanması açısından çok karmaşık bir oyun olmasa da, oynanan kartların sıralaması, birbirine göre üstün olan kartların seçilmesi, düşman stratejisine karşı koyabilme ve kendi stratejisine göre üstün gelebilme gibi durumların birleşimi ile karmaşık ve eğlenceli hale gelen bir oyundur. Bu yapısından ötürü, oluşturulan yapay zekânın da insan gibi karar vermeye çalışan, mantıklı seçimler ve saldırılar yapan bir yapı olması üzerine yoğunlaşılmıştır. Aynı desteler kullanılarak oynanan farklı oyunlar ile gerçek oyunculara karşı edilen başarılar aşağıdaki tabloda detaylandırılmıştır.
Yapay Zekâ Seviyesi | YZ Zafer Oranı |
Random | %12 (2/16) |
Beginner | %34 (8/23) |
Skilled | %63 (18/29) |
Hearthstone Projesini indirmek için bu bağlantıyı kullanabilirsiniz.
Oyunda kullanılan database (MSSQL2014) dosyası için bu bağlantıyı kullanabilirsiniz.