T: tema · Z: kodu büyüt · S: konuşmacı notları · F: tam ekran · Esc: genel bakış · ⌂ ders haritası
OYUNLARDA YAPAY ZEKÂ · VERİ YAPILARI
Yığınlar ve Kuyruklar
Stacks & Queues — geri al, menüler, waypoint’ler, kombo girdileri
PRESS ▶ TO START
Bu derste oyunun haritası 🗺️
Kurallar: yığın ve kuyruk ne yapar? API · LIFO · FIFO
Gerçekleme: bağlı liste ile, dizi ile, dairesel tampon ile implementation
Klasik uygulamalar: parantez kontrolü, hesap makinesi, çağrı yığını
Oyunlarda: geri al/yinele, menü yığını, RTS hedef kuyruğu, olay kuyruğu, kombo girdileri
Yığın mı kuyruk mu? Boya kovası ile DFS ve BFS
⚙️ Perde arkası: yığın belleği ve öbek belleği stack vs heap
🎮 Bu dersin sonunda…
…Ctrl+Z’nin, “geri” düğmesinin ve bir RTS biriminin hedef listesinin içinde ne olduğunu bilecek, yol bulma algoritmalarının temelini atmış olacaksınız.
🤔 ISINMA
🍽️ Tabak yığını
Yıkanan tabaklar üst üste konuyor. Hangi tabağı ilk alırsınız?
🎟️ Sinema kuyruğu
Gişeye sıra var. Bilet ilk kime verilir?
Tabak: en son konan ilk alınır → yığınstack: LIFO (Last In, First Out)
Sinema: en önce gelen ilk alınır → kuyrukqueue: FIFO (First In, First Out)
İkisi de bir “koleksiyon”: eleman ekler, eleman çıkarırız. Tek fark: hangisini çıkardığımız. Ama bu küçük fark bambaşka algoritmalar doğurur!
Oyunlarda yığın ve kuyruk
↶
Geri al / yineleSeviye editörü, bulmaca oyunları (Sokoban)
📋
Menü yığınıDuraklat → Ayarlar → Ses… “Geri” = pop
🪖
Hedef kuyruğuRTS’te Shift+tık ile sıralı waypoint’ler
📨
Olay kuyruğuSes, başarım, ağ mesajları sırayla işlenir
🕹️
Girdi tamponuDövüş oyunlarında ↓↘→👊 kombo tespiti
🧭
Yol bulmaDFS yığın, BFS kuyruk kullanır — oyun YZ’sinin temeli
Mini sözlük (1/2)
stack
yığın
En son ekleneni ilk veren koleksiyon (LIFO)
push / pop
it / çek
Yığına ekle / yığından çıkar
top / peek
tepe / bak
En üstteki eleman / çıkarmadan ona bakmak
queue
kuyruk
En önce ekleneni ilk veren koleksiyon (FIFO)
enqueue / dequeue
kuyruğa ekle / kuyruktan çıkar
Sona ekle / baştan çıkar
front / back
baş / son
Kuyruğun çıkış ve giriş uçları
LIFO / FIFO
son giren ilk çıkar / ilk giren ilk çıkar
Last In First Out / First In First Out
Mini sözlük (2/2)
deque
çift uçlu kuyruk
İki uçtan da ekleme/çıkarma (“dek” diye okunur)
bag
torba
Sadece ekleme ve gezme; sıra önemsiz
ring buffer
dairesel tampon
Sonu başa bağlanan sabit boyutlu dizi
overflow / underflow
taşma / boşalma
Dolu yapıya ekleme / boş yapıdan çıkarma
call stack
çağrı yığını
Çalışan fonksiyonların listesi (bilgisayarın kendi yığını)
generic
genel (tür parametreli)
Stack<T>: her türle çalışan yapı
DFS / BFS
derinlik / genişlik öncelikli arama
Yığınla / kuyrukla dolaşma
LEVEL 0
Kurallar
API — ne yapar? (nasıl yaptığı sonra)
kurallar: API
gerçekleme
klasik uygulamalar
oyunlarda
yığın mı kuyruk mu?
perde arkası
İki yapının sözleşmesi API
📚 Yığın (stack)
push(x)
x’i tepeye koy
pop()
tepedekini çıkar ve döndür
peek()
tepedekine bak (çıkarma)
isEmpty() · size()
boş mu? · kaç eleman?
🎟️ Kuyruk (queue)
enqueue(x)
x’i sona ekle
dequeue()
baştakini çıkar ve döndür
peek()
baştakine bak
isEmpty() · size()
boş mu? · kaç eleman?
💡 API = sözleşmeKullanan kişi sadece bu işlemleri bilir; içeride dizi mi, bağlı liste mi kullanıldığını bilmek zorunda değildir. Gerçeklemeyi değiştirsek de onun kodu çalışmaya devam eder. Buna soyutlamaabstraction denir.
🔗 Başka gözle: etkileşimli anlatımlar ve yazılar
Aynı konuyu başka bir anlatımla, kendi elinizle oynayarak görmek çok işe yarar. Kartlar yeni sekmede açılır; çoğu İngilizce ama görsel ve etkileşimli.
Boş bir yığına sırayla 1, 2, 3 push ediliyor, sonra bir pop, sonra 4 push, sonra iki pop. Çıkan sayılar sırasıyla?
push 1, 2, 3 → [1, 2, 3]. pop → 3. push 4 → [1, 2, 4]. pop → 4, pop → 2. Kuyruk olsaydı: 1, 2, 3 (her zaman giriş sırası).
🎒 Ön bilgi: Stack<T> — “T” ne demek? generics
Bir yığın her şeyi tutabilir: sayıları, hamleleri, menü ekranlarını… Her tür için ayrı sınıf yazmak yerine türü bir parametre yaparız:
Stack<int> → tamsayı yığını
Stack<Move> → hamle yığını
Queue<Vector3> → konum kuyruğu
<T> = “T yerine sonra bir tür yazılacak”. Buna generic (tür parametreli) sınıf denir.
Python ve JavaScript’te türler yazılmadığı için bu sorun yok: aynı liste her şeyi tutar (Python’da isteğe bağlı tür ipucu: list[int]).
Quiz 2 · kurallar
Python’da bir kuyruğu list ile tutup liste.pop(0) ile baştan çıkarırsak, N elemanlı kuyrukta tek bir dequeue ne kadar sürer?
Python list’i bir dinamik dizidir (bağlı listeler dersi!). Baştan çıkarmak = kalan elemanların hepsini bir sola kaydırmak. Çözüm: collections.deque (baştan çıkarma O(1)). JavaScript’te shift() ve C#’ta List.RemoveAt(0) için de aynı uyarı geçerli; C#’ın Queue<T>’i ise dairesel tampon kullanır.
LEVEL 1
Gerçekleme
implementation — bağlı liste, dinamik dizi, dairesel tampon
kurallar: API
gerçekleme
klasik uygulamalar
oyunlarda
yığın mı kuyruk mu?
perde arkası
Yığın = bağlı listenin başı
Bağlı listeler dersinde iki O(1) işlem gördük: başa ekle ve baştan sil. İşte yığın!
push = başa ekle
pop = baştan sil
Her işlem en kötü durumda bile sabit zaman. Bedeli: her eleman için bir düğüm nesnesi (bellek + çöp toplayıcı).
Yığın = dinamik dizinin sonu
push = sona ekle (a[n++] = x)
pop = sondan çıkar (a[--n])
Dolunca iki katına, dörtte bire düşünce yarıya (bağlı listeler dersindeki dinamik dizi)
Her işlem amortize O(1); ara sıra bir kopyalama. Ama bellek az ve önbellek dostu → pratikte çoğu zaman daha hızlı.
Python list, JS Array ve C# Stack<T> tam olarak böyle çalışır.
Kuyruk = bağlı liste (başı + sonu)
enqueue = sona ekle (last ile O(1))
dequeue = baştan sil (first ile O(1))
⚠️ Klasik hataKuyruk boşalınca last’ı da null yapmayı unutmak. Sonraki enqueue, artık listede olmayan eski düğüme bağlanır ve eleman kaybolur!
Kuyruk = dairesel tampon ring buffer · circular array
Dizi ile kuyruk: baştan çıkarınca herkesi kaydırmak istemiyoruz. Çözüm: başın indeksini tut ve ilerlet; sona gelince başa sar: (i + 1) % N. Dizinin sonu başına bağlanmış gibi davranır. 🎒 % = bölümden kalan: 7 % 8 = 7, 8 % 8 = 0, 9 % 8 = 1: saat gibi döner.
Dairesel tampon — kod
🎒 throw / raise: hata fırlat (programı “burada bir sorun var” diye durdur). C#’ın Queue<T>’i içeride dairesel tampon kullanır ve dolunca iki katına büyür.
🧪 Canlı ölçüm: kuyruğu nasıl gerçeklemeli?
🔬 Ne bekliyoruz?shift() her dequeue’da kalanları kaydırırsa toplam iş ~N²/2 olur: N 4 katına çıkınca süre ~16 katına. Baş indeksi ve dairesel tampon ise doğrusal: N 4 katına → süre ~4 katına.
Hangi gerçekleme? 📋
gerçekleme
işlem başına zaman
bellek
sınır
bağlı liste
her zaman sabit (en kötü durumda bile)
eleman başına bir düğüm (+ GC)
yok
dinamik dizi
ortalama sabit (arada bir kopyalama)
az, önbellek dostu
yok
dairesel tampon (sabit)
her zaman sabit
en az, sıfır çöp
kapasite sabit
🎮 OyunlardaKare süresi önemli: “ortalama hızlı ama arada bir yavaş” işlemler takılmaya yol açabilir. Kapasiteyi baştan bildiğiniz yerlerde (girdi tamponu, son 5 saniyenin kaydı) sabit dairesel tampon ideal.
NotBir uçak kontrol sistemi gibi “her işlem garanti hızlı olmalı” durumunda bağlı liste; toplam süre önemliyse dinamik dizi.
LEVEL 2
Klasik Uygulamalar
parantezler · hesap makinesi · çağrı yığını
kurallar: API
gerçekleme
klasik uygulamalar
oyunlarda
yığın mı kuyruk mu?
perde arkası
Parantezler dengeli mi? balanced brackets
🎮 Her kod editörü, her JSON/seviye dosyası okuyucusu, her diyalog betiği ayrıştırıcısı bunu yapar. Unity bir sahne dosyasını okurken de!
🧪 Kod laboratuvarı: parantez kontrolü
Görev: Kod sadece ( )’yi biliyor. ] ve } durumlarını ekleyip tüm testleri geçir. Bonus: bir “eşler” sözlüğü/nesnesi ({')': '(', ...}) kullanarak kodu kısalt.
Dijkstra’nın iki yığınlı hesap makinesi
🎬 Ara: yığın ve ters Lehçe gösterimi
Reverse Polish Notation and The Stack Computerphile · 13 dk 32 sn · YouTube’da aç
Dijkstra’nın iki yığınlı hesap makinesinin akrabası: parantezsiz yazılan ifadeyi tek bir yığınla hesaplamak. Eski HP hesap makinelerinden bugünkü derleyicilere kadar.
🤔 İzlerkenVideodaki “3 4 + 2 ×” ifadesi yığında adım adım nasıl hesaplanıyor? Bizim iki yığınlı yöntemde parantezler hangi işi görüyordu, burada neden gerek kalmıyor?
Tersine Lehçe gösterimi postfix · reverse Polish notation
İşlemi sayılardan sonra yazarsak parantez gerekmez!
alışık olduğumuz (infix)
postfix
3 + 4
3 4 +
(2 + 3) * 4
2 3 + 4 *
2 + 3 * 4
2 3 4 * +
Algoritma: sayıyı yığına it; işlem görünce iki sayı çek, sonucu it. Tek yığın yeter!
📜 Hesap makineleri ve sanal makineler1970’lerin HP hesap makineleri postfix kullanırdı. Bugün Java, C# (.NET) ve Python’un sanal makineleri de yığın makinesidir: kodunuz önce postfix benzeri komutlara çevrilir.
Şu an sadece + çalışıyor. -, *, / işlemlerini ekle. Dikkat: çıkarma ve bölmede sıra önemli — önce çekilen ikinci sayıdır (a - b, b - a değil)!
⚙️ Bilgisayarın kendi yığını: çağrı yığını call stack
Quiz 3 · uygulamalar
a() fonksiyonu b()’yi, b() de c()’yi çağırıyor. c() çalışırken çağrı yığınının tepesinden tabanına doğru sıra nedir?
Çağrılan her fonksiyon tepeye konur: önce a (taban), sonra b, en son c (tepe). c bitince pop edilir ve b kaldığı yerden devam eder. Hata ayıklayıcıların gösterdiği “call stack” penceresi de tam olarak budur.
LEVEL 3
Oyunlarda
geri al · menü yığını · hedef kuyruğu · olay kuyruğu · kombo tamponu
kurallar: API
gerçekleme
klasik uygulamalar
oyunlarda
yığın mı kuyruk mu?
perde arkası
↶ Geri al / yinele: iki yığın
Komut deseni ile geri al command pattern
🎒 interface ICommand: “Do ve Undo metotları olan her şey” — hareket, silme, renk değiştirme… hepsi aynı yığına girebilir. Ayrıntılar: Game Programming Patterns · Command (Kaynaklar).
📋 Menü yığını: “Geri” düğmesi = pop
Oyun → Duraklat → Ayarlar → Ses ayarları…
Yeni ekran açmak = push
“Geri” / Esc = pop → bir önceki ekran tekrar görünür
Ekranda sadece tepedeki ekran girdi alır
Tarayıcıların geri/ileri düğmeleri, mobil uygulamalardaki ekran geçişleri de aynı fikir (iki yığın: geri al/yinele gibi).
🪖 RTS: hedef kuyruğu waypoint queue
StarCraft, Age of Empires… Shift basılıyken tıklanan hedefler bir kuyruğa eklenir; birim onları sırayla gezer. Yığın kullansaydık? En son tıklanan yere önce giderdi! 😅
📨 Olay kuyruğu event queue
Oyunun bir yerinde “düşman öldü” olur. Ses çalınmalı, başarım kontrol edilmeli, skor artmalı, ağ üzerinden diğer oyunculara bildirilmeli…
Hepsini o anda yapmak yerine bir olayı kuyruğa ekle
Karenin uygun bir yerinde kuyruktaki olayları sırayla işle
Faydası: kodun parçaları birbirini tanımak zorunda kalmaz (gevşek bağlılıkdecoupling), ağır işler kareye yayılır, olaylar geldiği sırayla işlenir.
🕹️ Dövüş oyunu girdi tamponu: dairesel tampon
📜 Zamanı geri sarmak: Prince of Persia ve Braid
Prince of Persia: The Sands of Time (2003) ve Braid (2008): oyuncu zamanı geri sarabilir.
Her karede oyunun durumu (konumlar, hızlar…) kaydedilir.
Sınırlı bellek: sadece son k saniye → dairesel tampon: yeni kayıt en eskinin üzerine yazar.
Geri sarma: tampondan en yeniden en eskiye doğru oku → bu kez bir yığın gibi kullanılır (LIFO)!
💡 Mühendislik notuBraid’in yapımcısı Jonathan Blow, sınırsız geri sarma için tüm kareleri değil, sadece değişen bilgileri sıkıştırarak sakladığını anlatmıştır. Veri yapısı seçimi bir oyun mekaniğini mümkün kılıyor!
LEVEL 4
Yığın mı, Kuyruk mu?
aynı algoritma, farklı kap → DFS ve BFS
kurallar: API
gerçekleme
klasik uygulamalar
oyunlarda
yığın mı kuyruk mu?
perde arkası
🪣 Boya kovası: bekleyen hücreleri nerede tutalım?
Tek satırlık fark
🎒 (int x, int y): C# demeti tuple — iki değeri tek paket olarak taşır. var (x, y) = … paketi açar.
DFS ve BFS: oyun YZ’sinin kapısı 🚪
📚 Yığın → DFSdepth-first search Bir yolu sonuna kadar dener, çıkmaza girince geri döner. Labirent oluşturma (rastgele DFS ile mükemmel labirentler!), bulmaca çözme, “ulaşılabilir mi?” sorusu.
🎟️ Kuyruk → BFSbreadth-first search Başlangıçtan halka halka yayılır → her hücreye en az adımla ulaşır. Izgarada en kısa yol, “3 hamlede nerelere gidebilirim?” (taktik oyunlarda hareket alanı).
Graflar ve yol bulma derslerinde ikisini de ayrıntılı göreceğiz. Bir adım ötesi: kuyruk yerine öncelik kuyruğu → Dijkstra ve A*. 🎯
Quiz 4 · DFS · BFS
Taktik bir oyunda bir birimin en fazla 3 adımda ulaşabileceği kareleri, en az adım sayılarıyla bulmak istiyoruz. Bekleyen kareleri nerede tutmalıyız?
Kuyruk, kareleri başlangıca uzaklık sırasıyla çıkarır: önce 1 adımdakiler, sonra 2, sonra 3. Bir kare ilk görüldüğünde en kısa mesafesi bilinir. DFS de tüm erişilebilir kareleri bulur ama bir kareye ilk ulaştığı yol en kısa olmayabilir.
Akrabalar: çift uçlu kuyruk, torba, rastgele kuyruk
yapı
ne yapar?
🎮 örnek
hazır
Çift uçlu kuyrukdeque
iki uçtan ekle/çıkar
Yılan oyunu (başa ekle, sondan çıkar), “kayan pencere”
collections.deque · LinkedList<T>
Torbabag
sadece ekle ve gez, sıra önemsiz
Sahnedeki tüm mermiler, envanter
List<T> · list
Rastgele kuyrukrandomized queue
rastgele bir elemanı çıkar
Sunumdan kart çekmek, loot torbası
(kendiniz: swap & pop + rastgele indeks)
Öncelik kuyruğupriority queue
en önemliyi çıkar
A* açık listesi, olay zamanlayıcı
→ ileriki ders (heap)
LEVEL 5
Perde Arkası
yığın belleği ve öbek belleği · stack overflow
kurallar: API
gerçekleme
klasik uygulamalar
oyunlarda
yığın mı kuyruk mu?
perde arkası
⚙️ İki bellek bölgesi: yığın ve öbek stack vs heap
📚 Yığın belleği (stack)
Çağrı yığınının yaşadığı yer: parametreler, yerel değişkenler
Ayırma/serbest bırakma = yığın işaretçisini oynatmak → çok hızlı
Küçük (genelde birkaç MB) → çok derin özyineleme: stack overflow
C#’ta int, float, Vector3 (struct) yerel değişkenleri burada
🧱 Öbek belleği (heap)
new ile oluşturulan nesneler: düğümler, listeler, class örnekleri
Uyarı: “öbek” heap bellek bölgesinin adı; ileride göreceğimiz heap veri yapısı (öncelik kuyruğu) ile isim benzerliği dışında ilgisi yok!
📜 “Yığın” fikrinin kısa tarihi
1946 · Alan TuringAlt programlardan dönüş adreslerini saklamak için “gömme” bury ve “çıkarma” unbury işlemlerini tanımladı.
1957 · Bauer & SamelsonAlmanya’da “Kellerprinzip” (kiler ilkesi: en son konan en önce alınır) için patent aldılar; yığın kavramını formül hesaplamaya uyguladılar.
BugünHer işlemcide bir yığın işaretçisi yazmacı (stack pointer) ve PUSH/POP makine komutları var. Programcıların en büyük soru-cevap sitesinin adı da bu yığının taşmasından geliyor: Stack Overflow. 😄
Derin hikâye: Bir mahkeme salonunda yığın taşması (2013)
Ekim 2013, Oklahoma: bir Toyota Camry’nin istemsiz hızlanmasıyla ilgili ölümlü bir kaza davasında (Bookout - Toyota), jüri Toyota’yı sorumlu buldu. Davanın merkezinde aracın elektronik gaz kontrol sisteminin (ETCS) yazılımı vardı.
Davacıların uzman tanığı gömülü sistem uzmanı Michael Barr, yaklaşık 15 ay boyunca kaynak kodunu inceledi. Raporundaki bulgulardan biri: yazılımın en kötü durumdaki yığın kullanımı, ayrılan alanı aşabilecek kadar yüksekti; yığın taşarsa hemen yanındaki bellekte duran, gaz kontrolü için kritik değişkenlerin üzerine yazılabilirdi. Barr’a göre bu, gazın kontrolünü kaybettirebilecek olası mekanizmalardan biriydi.
Rapor, bekçi zamanlayıcısının (watchdog) zayıf kullanımı ve kod kalitesi sorunları gibi başka bulgular da içeriyordu.
DersYığın soyut bir veri yapısı olduğu kadar fiziksel bir bellek bölgesidir ve sınırlıdır. Güvenlik açısından kritik gömülü sistemlerde özyineleme çoğu zaman tamamen yasaklanır (ör. MISRA C) ve en kötü yığın kullanımı statik olarak hesaplanır. Oyunlarda sonuç daha masum (çökme), ama ders aynı: derin özyineleme yerine açık bir yığın kullanın.
JS’te kuyruk için shift(), Python’da list.pop(0): O(n).
Çok iş parçacıklı kodda kilitsiz Queue<T>.
Sınırsız büyüyen geri alma yığını (bellek): kapasite sınırı koyun.
Özyinelemenin derinliğini hiç düşünmemek.
🤖 Bu konuyu bir YZ kod asistanıyla yazarken
Sık yapılan hatalar — kontrol edin
JS’te shift(), Python’da list.pop(0) ile kuyruk
Çok iş parçacıklı kuyrukta kilit yok
Sınırsız geri alma yığını
Derin özyineleme
İyi bir istek (prompt) örneği
“Unity 6, C#. Bir seviye editörü için komut deseniyle geri al/yinele sistemi yaz: ICommand (Do, Undo), PlaceTileCommand, RemoveTileCommand, MoveObjectCommand; CommandHistory iki yığınla (en fazla 200 adım, eskiler düşer); yeni komut gelince yinele yığını temizlenir; ardışık küçük hareketleri tek komutta birleştir. NUnit testleri ekle.”
Kapasite sınırı ve taşma davranışını sorun
Özyinelemeli çözümün açık yığınlı sürümünü isteyin
İş parçacığı güvenliği gereksinimini yazın
Komut desenini test edilebilir isteyin
🚀 Proje ve araştırma önerileri
[kolay]Geri al / yinele — Basit bir çizim ya da karo editörüne komut deseniyle ekleyin.
[orta]Kombo algılayıcı — Dövüş oyunu girdi tamponu: son 0,5 saniyedeki tuşlardan “↓ ↘ → + Yumruk” gibi komboları tanıyın.
[orta]Durum yığını — Oyun, duraklatma, ayarlar ve diyalog ekranlarını yığınla yöneten bir oyun durumu yöneticisi.
[araştırma]Özyineleme ve derinlik — Flood fill’in özyinelemeli ve açık yığınlı sürümlerini büyük haritada çalıştırıp en büyük güvenli boyutu ölçün.
API ile gerçekleme ayrı: bağlı liste, dinamik dizi, dairesel tampon.
Tuzaklar: list.pop(0) ve shift() O(N); kuyruk boşalınca last = null.
Klasikler: parantez kontrolü, iki yığınlı hesap, postfix, çağrı yığını.
🎮 Geri al/yinele (komut deseni), menü yığını, RTS hedef kuyruğu, olay kuyruğu, kombo tamponu, zamanı geri sarma.
Yığın → DFS, kuyruk → BFS: aynı algoritma, farklı kap.
⚙️ Yığın belleği ve öbek belleği; stack overflow.
Alıştırmalar 🏋️
Kâğıt-kalem: Boş bir kuyrukta enqueue 1, 2, 3; dequeue; enqueue 4; dequeue; dequeue → çıkanlar? Aynısını yığınla yapın.
İki yığınla kuyruk yazın: enqueue → giriş yığınına; dequeue → çıkış yığını boşsa girişteki her şeyi ona aktar. Neden amortize O(1)?
p5.js’te bir çizim programına geri al ekleyin: her fırça darbesi bir komut olsun.
Unity’de Esc ile çalışan bir menü yığını yapın (Duraklat → Ayarlar → Ses).
Boya kovası demosundaki haritayı kâğıda çizin; DFS ve BFS’nin ilk 10 hücresini numaralayın.
🤖 Bir YZ asistanından “C#’ta dairesel tampon” isteyin. Dolu ve boş durumlarını, başa sarmayı test edin. head == tail hem boş hem dolu anlamına gelebilir mi?
🎮 Bu sunumdaki oyunlar
Adı geçen oyunu tanımıyorsanız: oyuncuların kullandığı wiki sayfası ve oyunu kısaca gösteren bir video. ▶ video slaydın üstünde açılır (Esc ile kapanır); 🎯 işaretli bağlantılar doğrudan bu sunumdaki konuya gider.
StarCraft1998 / 2010 · gerçek zamanlı strateji
Üç ırkın savaştığı, e-sporu başlatan strateji serisi; StarCraft II, DeepMind’ın AlphaStar’ının sahnesi oldu.