3 puan yazan GN⁺ 3 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Pokémon savaş kuralları, tip eşleşmeleri, hareketler, istatistikler ve yeteneklerin iç içe geçtiği bir kural motoruna daha yakın olduğundan Prolog’un ilişki ve kural modeliyle özlü biçimde ifade edilebilir
  • Prolog, pokemon/1, type/2 gibi yüklemlerle olguları tanımlar; büyük harfle başlayan değişkenler ve birleştirme sayesinde tip ve hareket koşullarına uyan Pokémon’ları bulur
  • Freeze-Dry öğrenen, Ice tipinde olan ve Special Attack değeri 120’den büyük Pokémon’ları bulmak için Prolog sorgusu, SQL’de birden fazla EXISTS kullanmaktan daha kısadır
  • Draft takımları alex/1, morry/1 gibi yüklemlerle ifade edilir ve öncelik hareketi kurallarına hariç tutma koşulları ile Prankster etkisi katman katman eklenebilir
  • Techno's Prep Doc gibi e-tablolar güçlüdür, ancak Prolog veritabanı rastgele kombinasyon sorgularında daha esnektir ve prologdex ile Scryer Prolog kullanılarak uygulanmıştır

Pokémon savaş kuralları neden mantıksal programlamaya uygun?

  • Pokémon savaşları, birçok kuralın karmaşık biçimde birbirine geçtiği bir kural motoruna daha yakındır ve Prolog gibi mantıksal programlama dilleri bu ilişkileri özlü biçimde ifade etmek için uygundur
  • Pokémon, tür adı taşıyan karakterlerdir ve Bulbasaur #1) ile Pecharunt #1025) arasında 1.000’den fazla tür vardır
  • Ana seri savaşlarda, 6 üyeden oluşan takımlar karşılaşır; her Pokémon genellikle rakibe hasar veren 4 hareketten birini seçer ve rakip takımın tüm HP’sini 0’a indirirse kazanır
  • Savaştaki performans; temel istatistiklere, öğrenebildiği hareket listesine, yeteneklere ve tiplere bağlı olarak değişir; kombinasyon sayısı çok fazla olduğu için bunları yazılımla takip etmek değerlidir
  • Tipler hem hareketlere hem de Pokémon’lara atanır; bir hareket tipi rakibin tipine karşı güçlüyse 2 kat, zayıfsa 1/2 kat hasar verir
  • Tip çarpanları birikir
    • Scizor) Bug/Steel tipindedir ve her ikisi de Fire’a zayıf olduğu için Fire hareketlerinden 4 kat hasar alır
    • Water/Ground tipi Swampert) üzerine Electric hareketi kullanılırsa, Ground bağışıklığı nedeniyle hasar 0 olur

Prolog’un temel modeli

  • Prolog’da ilişkiler yüklem (predicate) ile tanımlanır
pokemon(bulbasaur).
pokemon(ivysaur).
pokemon(venusaur).
pokemon(charmander).
pokemon(charmeleon).
pokemon(charizard).
pokemon(squirtle).
pokemon(wartortle).
pokemon(blastoise).
  • pokemon/1, adı pokemon olan ve tek argüman alan bir yüklemdir; pokemon(squirtle). gibi bir sorgu, ilgili ifadenin doğru yapılıp yapılamayacağını denetler
?- pokemon(squirtle).
   true.

?- pokemon(alex).
   false.
  • Pokémon tipleri type/2 gibi iki argümanlı ilişkilerle ifade edilebilir; iki tipe sahip bir Pokémon için aynı Pokémon hakkında iki type olgusu tanımlanır
type(bulbasaur, grass).
type(bulbasaur, poison).
type(charmander, fire).
type(charizard, fire).
type(charizard, flying).
type(squirtle, water).
  • Büyük harfle başlayan adlar değişkentir ve Prolog, değişken içeren sorguları mümkün olan tüm değerlerle birleştirmeyi (unify) dener
?- type(squirtle, Type).
   Type = water.

?- type(venusaur, Type).
   Type = grass
;  Type = poison.
  • İlk argümanı type(Pokemon, grass). örneğindeki gibi değişken yaparsanız, tüm Grass tipi Pokémon’ları bulabilirsiniz; gerçek veride 164 sonuç döner
  • Virgül, birden fazla yüklemin aynı anda sağlanması gerektiği anlamına gelir; aynı değişken adı da sorgu içinde aynı değeri almak zorundadır
?- type(Pokemon, water), type(Pokemon, ice).
   Pokemon = dewgong
;  Pokemon = cloyster
;  Pokemon = lapras
;  Pokemon = laprasgmax
;  Pokemon = spheal
;  Pokemon = sealeo
;  Pokemon = walrein
;  Pokemon = arctovish
;  Pokemon = ironbundle
;  false.
  • Iron Bundle) örneğinde olduğu gibi istatistikler ve öğrenilebilen hareketler de ilişkiler üzerinden sorgulanabilir
?- pokemon_spa(ironbundle, SpA).
   SpA = 124.

?- learns(ironbundle, Move), move_category(Move, special).
   Move = aircutter
;  Move = blizzard
;  Move = chillingwater
;  Move = freezedry
;  Move = hydropump
;  Move = hyperbeam
;  Move = icebeam
;  Move = icywind
;  Move = powdersnow
;  Move = swift
;  Move = terablast
;  Move = waterpulse
;  Move = whirlpool.
  • SpA #> 120 gibi kısıtlar eklenirse, Special Attack değeri 120’den büyük olan, Freeze-Dry öğrenen ve Ice tipindeki Pokémon’lar doğrudan bulunabilir
?- pokemon_spa(Pokemon, SpA), SpA #> 120, learns(Pokemon, freezedry), type(Pokemon, ice).
   Pokemon = glaceon, SpA = 130
;  Pokemon = kyurem, SpA = 130
;  Pokemon = kyuremwhite, SpA = 170
;  Pokemon = ironbundle, SpA = 124
;  false.
  • Prolog’daki kural (rule) baş ve gövdeden oluşur; gövde doğruysa baş da birleştirilir
damaging_move(Move) :-
  move_category(Move, physical)
; move_category(Move, special).
  • Bu kural, Physical veya Special hareketleri doğrudan hasar veren hareketler olarak sınıflandırır
?- damaging_move(tackle).
   true.
?- damaging_move(rest).
   false.

SQL ile karşılaştırılan sorgu ifadeleri

  • Şimdiye kadarki örnekler mantıksal olarak basit and ve or birleşimleri olsa da, Prolog’da ilişki sorguları SQL’e göre daha kısa ve değiştirmesi daha kolay bir biçime dönüşür
  • Aynı veriyi SQL ile kurarsanız Pokémon, tip ve hareketleri ayrı tablolarda tutabilirsiniz
CREATE TABLE pokemon (pokemon_name TEXT, special_attack INTEGER);
CREATE TABLE pokemon_types(pokemon_name TEXT, type TEXT);
CREATE TABLE pokemon_moves(pokemon_name TEXT, move TEXT, category TEXT);
  • Freeze-Dry öğrenen, Ice tipinde olan ve Special Attack değeri 120’den büyük Pokémon’ları SQL ile bulmak için EXISTS ifadesini birkaç kez kullanmanız gerekir
SELECT DISTINCT pokmeon, special_attack
FROM pokemon as p
WHERE
  p.special_attack > 120
  AND EXISTS (
    SELECT 1
    FROM pokemon_moves as pm
    WHERE p.pokemon_name = pm.pokemon_name AND move = 'freezedry'
  )
  AND EXISTS (
    SELECT 1
    FROM pokemon_types as pt
    WHERE p.pokemon_name = pt.pokemon_name AND type = 'ice'
  );
  • Aynı Prolog sorgusu ise gerekli ilişkileri olduğu gibi sıralar
?- pokemon_spa(Pokemon, SpA),
SpA #> 120,
learns(Pokemon, freezedry),
type(Pokemon, ice).
  • Koşullar eklenmeye devam ettikçe SQL sorguları kolayca karmaşıklaşabilir, ancak Prolog sorguları değişkenlerin nasıl çalıştığına alışınca okunması ve düzenlenmesi kolay bir yapıyı korur

Savaş kurallarını katman katman inşa etme yöntemi

  • Pokémon savaşlarında ıskalama, stat artışı ve düşüşü, eşya etkileri, hasar aralığı, durum etkileri, hava, arazi ve Trick Room gibi alan etkileri, yetenekler, savaş öncesi stat dağılımı gibi birçok etkileşim kuralı bulunur
  • Pokémon için yazılım geliştirirken bu karmaşıklığı ele alırken modeli yönetilebilir bir biçimde tutmak gerekir
  • Prolog, anlık kombinasyonları betimleyen sorgu modeli ve tutarlı kural katmanlaması konusunda güçlüdür
  • damage calculator ile bu karmaşıklığı doğrudan görebilirsiniz

Draft ligi ve öncelikli hareket sorguları

  • Pokémon draft formatında her Pokémon’un bir değeri vardır ve oyuncular belirli puan sınırı içinde Pokémon seçerek yaklaşık 8 ila 11 üyeli bir takım kurar
  • Gerçek savaşlar 6v6 olduğu için, rakibin getirebileceği altı Pokémon’luk kombinasyonlara hazırlanmak ve buna karşı çıkarılacak altı Pokémon’u seçmek önemlidir
  • Kendi draft ettiğiniz Pokémon’ları alex/1 gibi bir yüklemle doğrudan ifade edebilirsiniz
alex(meowscarada).
alex(weezinggalar).
alex(swampertmega).
alex(latios).
alex(volcarona).
alex(tornadus).
alex(politoed).
alex(archaludon).
alex(beartic).
alex(dusclops).
  • Bu takımda Freeze-Dry öğrenen Pokémon’u bulma sorgusu basittir, ancak sonuç yoktur
?- alex(Pokemon), learns(Pokemon, freezedry).
   false.
  • Savaş sırası temelde Speed tarafından belirlenir, ancak hareketlerin öncelik (priority) değeri vardır ve daha yüksek öncelikli hareket önce gerçekleşir
  • Çoğu hareketin önceliği 0’dır, ancak Accelerock gibi önceliği 1 olan hareketler, daha hızlı bir Pokémon’un önceliği 0 olan hareketinden önce gider
  • Belirli bir Pokémon’un öğrendiği pozitif öncelikli hareketler learns/2, move_priority/2 ve öncelik koşulu birleştirilerek bulunabilir
  • Basit sorgu, Double Battles’ta daha anlamlı olan Helping Hand ve Ally Switch gibi hareketleri ya da pratikte anlamı düşük Bide gibi hareketleri de içerir
  • \+/1, hedef başarısız olduğunda doğru olur ve dif/2 iki terimin farklı olduğunu ifade eder; bu nedenle Double Battles için olan hareketleri ve Bide’ı dışlayan bir kural ekleyebilirsiniz
learns_priority(Mon, Move, Priority) :-
  learns(Mon, Move),
  \+ doubles_move(Move),
  dif(Move, bide),
  move_priority(Move, Priority),
  Priority #> 0.
  • Protect, Detect, Endure ve Magic Coat gibi savunma amaçlı hareketler de çıkarılırsa, geriye pratikte rakibe hasar ya da olumsuz etki verebilen öncelikli hareketler kalır
?- alex(Pokemon), learns_priority(Pokemon, Move, Priority).
   Pokemon = meowscarada, Move = quickattack, Priority = 1
;  Pokemon = meowscarada, Move = suckerpunch, Priority = 1
;  Pokemon = beartic, Move = aquajet, Priority = 1
;  Pokemon = dusclops, Move = shadowsneak, Priority = 1
;  Pokemon = dusclops, Move = snatch, Priority = 4
;  Pokemon = dusclops, Move = suckerpunch, Priority = 1
;  false.
  • Aynı kural rakip takım yüklemine uygulanırsa, rakibin sahip olduğu öncelikli hareketler de hemen bulunabilir
?- morry(Pokemon), learns_priority(Pokemon, Move, Priority).
   Pokemon = mawilemega, Move = snatch, Priority = 4
;  Pokemon = mawilemega, Move = suckerpunch, Priority = 1
;  Pokemon = walkingwake, Move = aquajet, Priority = 1
;  Pokemon = ursaluna, Move = babydolleyes, Priority = 1
;  Pokemon = lokix, Move = feint, Priority = 2
;  Pokemon = lokix, Move = firstimpression, Priority = 2
;  Pokemon = lokix, Move = suckerpunch, Priority = 1
;  Pokemon = alakazam, Move = snatch, Priority = 4
;  Pokemon = skarmory, Move = feint, Priority = 2
;  Pokemon = froslass, Move = iceshard, Priority = 1
;  Pokemon = froslass, Move = snatch, Priority = 4
;  Pokemon = froslass, Move = suckerpunch, Priority = 1
;  Pokemon = dipplin, Move = suckerpunch, Priority = 1.

Prankster yeteneğinin genişletilmesi

  • Prankster yeteneğine sahip Pokémon’ların durum hareketleri için öncelik değeri ek olarak +1 olur; bu etki mevcut learns_priority/3 kuralına da eklenebilir
  • Takım içinde Tornadus, Prankster yeteneğine sahiptir
?- alex(Pokemon), pokemon_ability(Pokemon, prankster).
   Pokemon = tornadus
;  false.
  • Prolog’un ->/2 if/then sözdizimi kullanılarak, Pokémon Prankster ise ve hareket kategorisi status ise temel önceliğe 1 eklenmesi, aksi halde temel önceliğin olduğu gibi kullanılması sağlanabilir
learns_priority(Mon, Move, Priority) :-
  learns(Mon, Move),
  \+ doubles_move(Move),
  \+ protection_move(Move),
  Move \= bide,
  move_priority(Move, BasePriority),
  (
    pokemon_ability(Mon, prankster), move_category(Move, status) ->
      Priority #= BasePriority + 1
    ; Priority #= BasePriority
  ),
  Priority #> 0.
  • Bu kuraldan sonra aynı sorgu, Tornadus’un Agility, Defog, Nasty Plot, Rain Dance, Tailwind, Taunt, Toxic gibi durum hareketlerini öncelik 1 ile içerir
  • Tek bir kuralı genişleterek yetenek etkilerini de yansıtabilmek, Prolog’un katmanlı yapı avantajını gösterir

E-tablo tabanlı araçlarla karşılaştırma

  • Pokémon topluluğunda rakip takımın öncelikli hareketleri gibi bilgileri bulmaya yarayan kaynaklar zaten var; bunların başında “Techno’s Prep Doc” gibi gelişmiş Google Sheets dosyaları geliyor
  • Bu e-tablo, takımları girdiğinizde çok sayıda eşleşme bilgisi üretir ve çeşitli format desteği, hızlı taranabilen görsel materyaller ve otomatik tamamlama sunar
  • Öncelikli hareketleri bulan formül, FILTER, VLOOKUP, INDIRECT birleşimini kullanır; INDIRECT ise hücre referansı döndürür
={IFERROR(ARRAYFORMULA(VLOOKUP(FILTER(INDIRECT(Matchup!$S$3&"!$AV$4:$AV"),INDIRECT(Matchup!$S$3&"!$AT$4:$AT")="X"),{Backend!$L$2:$L,Backend!$F$2:$F},2,FALSE))),IFERROR(FILTER(INDIRECT(Matchup!$S$3&"!$AW$4:$AW"),INDIRECT(Matchup!$S$3&"!$AT$4:$AT")="X"))}
  • Backend sayfasında tüm hareketler listelenmiştir ve bu yapı, Prolog sorgularının hardcode edildiği bir sürüme daha yakındır
  • Prolog veritabanı, dikkate değer hareket listesini hardcode etme yaklaşımına göre daha ölçeklenebilirdir ve herhangi bir hareket sorgulanabilir
  • Örneğin Tornadus’un öğrenebildiği Special hareketler arasında Justin’in takım üyelerine karşı süper etkili olanları bulmak gibi, mevcut araçlarda yer almayan bileşik sorular da kısa biçimde ifade edilebilir
?- justin(Target), learns(tornadus, Move), super_effective_move(Move, Target), move_category(Move, special).
   Target = charizardmegay, Move = chillingwater
;  Target = terapagosterastal, Move = focusblast
;  Target = alomomola, Move = grassknot
;  Target = scizor, Move = heatwave
;  Target = scizor, Move = incinerate
;  Target = runerigus, Move = chillingwater
;  Target = runerigus, Move = darkpulse
;  Target = runerigus, Move = grassknot
;  Target = runerigus, Move = icywind
;  Target = screamtail, Move = sludgebomb
;  Target = screamtail, Move = sludgewave
;  Target = trapinch, Move = chillingwater
;  Target = trapinch, Move = grassknot
;  Target = trapinch, Move = icywind
;  false.

Uygulama notları ve sınırlamalar

1 yorum

 
GN⁺ 3 시간 전
Lobste.rs görüşleri
  • Prolog’u gerçekten üretken şekilde kullanan biri var mı merak ediyorum. İş için ya da kişisel kullanım için olabilir; şimdiye kadar sadece böyle oyuncak örnekler gördüm

    • Prolog’u epey seviyorum ve muhtemelen en yaygın kullanılan Prolog language server uygulamasını yazan kişi olarak, onu küçük script işleri için çok kullanıyorum
      Teknik olarak üretimde çalışan bir veya daha fazla Prolog kodum da var; bunlardan biri dahili analiz panosu, eskiden de bir iOS uygulamasının backend sunucusunu Prolog ile yazmıştım. Bu sırada, harici bir servis olmadan APNS bildirimleri göndermek için Prolog için bir HTTP/2 istemci kütüphanesi de yazmış oldum
    • Tatmin edici bir cevap olur mu bilmiyorum ama bu blog yazısını yazdıktan sonra Prolog’u eski kodlarımdan tamamen farklı türde problemler için kullanmaya başladım
      Prolog topluluğunda onu web sunucusu gibi şeyler için kullanmayı sevenler elbette var, ama bana göre bu daha çok farklı bir skill tree açmak gibi hissettiriyor. Mesela, hangi problemlerde özel bir parser ya da DSL’in iyi uyacağını daha iyi fark etmeye başlıyorsunuz
      Bu yazıyı yazdıktan sonra edindiğim bilgiyle IRS Fact Graph’in vergi mantığı motorunun yararlı bir alt kümesini yeniden uyguladım. Prolog bu iş için şaşırtıcı derecede iyi uyuyor çünkü imperatif bir uygulamada gözden kaçabilecek dokümante edilmemiş köşeleri ortaya çıkarıyor ve bunları çözmeye zorluyor
      “Çalıştırma” kısmını henüz bitiremedim ama parsing yeterince tamamlandı ve bununla gayet iyi bir belge taslağı yazabildim. Büyük eksik parçalardan biri tarih aritmetiği; onu da ekleyince ayrıca paylaşmayı planlıyorum
      DCG kullanıldığında Prolog, karmaşık yapılandırılmış metinleri parse etmekte harika. Eskiden awk yetmediğinde Python ya da JS düşünürdüm, ama şimdi yapı ve disiplin gereken yerlerde Prolog’un iyi uyduğunu görüyorum. Tek bir dosyaya sığan karmaşık bir kod tabanı yazmanın eski usul hissi de tatmin edici; ayrıca APL kadar aşırı kısaltılmış da değil
      Örneğin kendisi küçük olabilir ama Pokémon örneği öyle değil. Küçük gibi görünen çoğu örnek de, çok kapsamlı biçimde uygulanmış gülünç derecede karmaşık savaş mekanikleri zaten mevcut olduğu için mümkün oldu. Mevcut araçların yaptığı işin bir kısmını yapan Prolog tabanlı bir kural motoru oluşturmaya ilgim var ve bunu yavaş yavaş deniyorum; avantajı, imperatif koda kıyasla olasılıkları depth-first search ile daha kolay ortaya çıkarması ve bakımının daha kolay olması
    • Bazen program analizi için kullandım. Epey fazla araç program bilgisini Datalog içinde saklayıp onun üzerinde sorgu yazmaya izin veriyor; bazen de backtracking ya da fonksiyonlardan yararlanmak için doğrudan SWI-Prolog sunan araçlar kullandım
    • Blogum Prolog ile üretiliyor ve kaynak kodu açık
      Scryer Prolog belgeleri de DocLog adını verdiğim bir Prolog programıyla üretiliyor. Bu programların kullandığı birkaç kütüphane de yazdım. SWI Prolog da kendi web sitesi, Swish adlı web IDE’si, ClioPatria sunucusu gibi yerlerde SWI’yi doğrudan kullanıyor
  • Bilgileri düzenlemek için SQLite’ı bir tür tip güvenli elektronik tablo gibi kullandığım oldu. Bunun üstünde hiç CRUD arayüzü olmasa da yapılabiliyordu
    Yine de bu iş için her zaman en hoş görünen dil değildi. Bir süre Datalog’a da baktım ama çoğu uygulama, bu yazıdaki gibi kolayca bilgi kaydetmeye yarayan araçlar olmaktan çok, daha büyük programların içine gömülmek için tasarlanmış gibiydi
    Belki de gerçekten kullanılması gereken araç Prolog’dur

    • Harici veritabanı biçimindeki Datalog için Mangle ve Datomic var; dolayısıyla uygun olabilir ama ben kendim kullanmadım
      Prolog, Datalog’un üst kümesi olduğu için Datalog’un yapabildiği her şeyi yapabilir, hatta daha fazlasını da. Ama bazen sorun o “daha fazlası” olur; çünkü artık Turing-complete bir dildir, yani hiç bitmeyebilir, akıl yürütmesi biraz daha zor olabilir ve güvenli olmayan şekillerde kullanılabilir
      Datalog ise kısıtlı olduğu için bazı kestirme yollar kullanabilir ve bu yüzden performansı da çoğu zaman daha iyi olur
      Son olarak, Prolog’da veri kodla aynıdır; yani homoiconicity vardır. Bu yüzden oluşturma/güncelleme/silme işlemleri aslında kaynak kodu değiştirmek anlamına gelir. Bunu dinamik olarak yapmak mümkün olsa da akışın pürüzsüz olmasını beklemek zor; dosyalarla yüklenmiş program arasında hâlâ bir miktar elle senkronizasyon gerekir
  • Prolog’u daha derinlemesine öğrenip bunu instruction set query için kullanmak istiyorum
    Ama mantığı, miktarlar ve bit düzeyinde görülen tamsayılar da dahil olacak şekilde modellemek oldukça zor