1 puan yazan GN⁺ 2023-10-09 | 1 yorum | WhatsApp'ta paylaş
  • Flappy Dird, macOS Finder dosya listesini ekran, dosya seçme ve açma işlemlerini ise girdi olarak kullanarak Flappy Bird çalıştıran deneysel bir uygulamadır
  • Temel fikir, yalnızca Finder içinde güncellenen bir dizinin Date Last Opened değerini düğme gibi okuyup, sembolik bağlantı dosya adlarını değiştirerek ekranı çizmekten yola çıkıyor
  • Basit dosya adı değiştirme tabanlı render yavaştı ve ciddi ekran yırtılmasına yol açıyordu; bu yüzden AppleScript ile Finder penceresinin hedef dizinini değiştiren bir çift tamponlama yöntemiyle geliştirildi
  • Çift tıklama girdisi, saniye düzeyindeki zaman damgası sınırı nedeniyle 2 FPS üzerinde kararsızdı; daha sonra Finder'ın seçili öğelerini okuma yöntemine geçildi
  • Nihai yapıda döngüyü AppleScript çalıştırıyor, oyun durumu ve render işini Python üstleniyor; sistem 4 FPS'de çalışıyor ama girdi kaçırma ve Finder'a özgü kısıtlar sürüyor

Finder'ı oyun ekranı olarak kullanmak

  • Flappy Dird, koduna GitHub deposu üzerinden bakılabilen ve doğrudan çalıştırılabilen Finder tabanlı bir oyundur
  • Oyunda kullanım talimatları, en yüksek skor takibi ve kayan banner reklam bulunuyor
  • Kullanıcı oyunu çift tıklamayla başlatıyor ve Finder penceresi içindeki herhangi bir dosyayı seçerek kuşu zıplatıyor
  • Çalışma hızı saniyede 4 kare düzeyinde; bundan çok daha hızlı çalıştırmak zor ve girdiler de zaman zaman kaçıyor

Date Last Opened değerini düğme gibi kullanmak

  • Çıkış noktası, Finder'da dizinler için Date Last Opened alanının bulunmasıydı
  • Gözlemlenen davranış üç maddeden oluşuyordu
    • cd ile dizine girildiğinde zaman damgası güncellenmiyor
    • Finder içinde dizinin sembolik bağlantısı çift tıklandığında zaman damgası güncelleniyor
    • Bu değer mdls ile saniye hassasiyetinde okunabiliyor
  • Bu özellikten yararlanılarak Finder içinde bir düğme oluşturulabildi
    • dir dizini içinde, dir'i işaret eden button adlı bir sembolik bağlantı oluşturuluyor
    • Başlangıçta dir için son açılma zamanı okunuyor
    • Zaman damgası düzenli olarak sorgulanıyor ve değer değiştiğinde girdi olarak kabul ediliyor
    • Finder konumu değiştirilmeden button açılarak dir için son açılma zamanı değiştiriliyor

Emoji dosya adlarıyla 15x15 ekran çizmek

  • Finder'ın varsayılan yazı tipiyle ASCII art hizalamak zordu, ancak dosya adlarına konan emojilerin sabit genişlikte gösterildiği fark edildi
  • Prototipte, dir içinde yine dir'i işaret eden 15 alt sembolik bağlantı oluşturuluyor ve her bağlantının adı bir emoji satırına dönüştürülerek ekran kuruluyordu
  • Render fonksiyonu bird_y_pos, pipe_locations, frame değerlerini alıp bir 15x15 emoji ızgarası üretiyor
  • Her karede dizin içindeki tüm sembolik bağlantı adları, emoji ızgarasının ilgili satırlarıyla değiştiriliyor
  • Finder'ın Date Modified ölçütüne göre sıralaması için ad değiştirme sırasını ayarlayan bir hack de gerekiyordu
  • Bu yöntem çalıştı ama çok yavaştı ve ekran yırtılması belirgindi

AppleScript ve çift tamponlama

  • Ekran yırtılmasını azaltmak için Finder dosya listesini yenilemenin yolu aranırken AppleScript komutu tell application "Finder" to tell front window to update every item kullanıldı
  • Bu komut biraz yardımcı oldu ama yırtılma devam etti; sonrasında yön, AppleScript ile Finder'ı doğrudan kontrol etmeye kaydı
  • Çift tamponlama için birkaç aday vardı
    • Sembolik bağlantılarla dizin içeriğini atomik biçimde değiştirme yöntemi, Finder sembolik bağlantı dizinlerinin içeriğini genişletemediği için uygun değildi
    • Birbirini işaret eden iki dizin kullanma yöntemi yırtılmayı azaltabiliyordu, ancak kare ancak kullanıcı tıkladığında ilerleyebildiği için oyun için uygun değildi
    • Finder'ın gösterdiği dizini değiştirirken “son açılma zamanı”nı değiştirmeyen bir yol gerekiyordu
  • Çözüm, AppleScript içindeki tell application "Finder" to set target of front Finder window to ("PATH" POSIX file) komutuyla Finder penceresinin hedef dizinini değiştirmek oldu
  • Bu yöntemle çift tamponlama uygulanınca oyun 1 FPS'te akıcı çalıştı, ancak hız hâlâ düşüktü

Girdi yöntemini değiştirmek: çift tıklamadan dosya seçimine

  • Çift tıklama girdisi, zaman damgası hassasiyeti saniye düzeyinde olduğu için 2 FPS üzerinde sınırına ulaşıyordu
  • AppleScript ile Finder penceresindeki seçili öğeler okunabildiği için girdi yöntemi “herhangi bir dosya seçimi” olarak değiştirildi
  • Yeni oyun döngüsü şu yapıdaydı
    • Kullanıcı çift tıklayana kadar bekleniyor
    • Her karede AppleScript ile mevcut pencerede bir dosya seçilip seçilmediği kontrol ediliyor
    • Seçim varsa veya son açılma zamanı değişmişse kuş zıplıyor
    • AppleScript ile Finder'ın gösterdiği dizin değiştiriliyor
    • Hedef kare hızını tutturmak için kalan süre kadar bekleniyor
  • Bu yöntem 2 FPS seviyesine bir ölçüde ulaştı, ancak AppleScript çağrısı yaklaşık 0.2 saniye sürdüğü için sabit maliyet yüksekti
  • 2 FPS'de bile girdi kontrolünden sonra kullanıcı bir dosya seçerse bu girdi kaçırılabiliyordu

Ana döngüyü AppleScript'e taşımak

  • Profilleme sırasında, 1 sayısını log'a yazıp çıkan bir AppleScript'in yaklaşık 0.14 saniye sürdüğü görüldü
  • Daha sonra bu ölçümün adil bir AppleScript başlangıç süresi ölçümü olmadığı ve boş bir AppleScript için sürenin yaklaşık 0.06 saniyeye daha yakın olabileceği düzeltmesi eklendi
  • Her karede AppleScript başlatma maliyetini ödememek için ana döngü AppleScript'e taşındı, Python ise oyun mantığı ve render işini üstlendi
  • Temel yapı şöyleydi
    • game.py await ile kullanıcının çift tıklayarak başlatması bekleniyor
    • game.py start-frame ile karenin başlangıç zamanı kaydediliyor
    • Finder seçim öğesi sayısı okunup game.py tick komutuna aktarılıyor ve kare render ediliyor
    • tick tarafından hazırlanan dizin adı alınıp Finder penceresinin hedefi bu dizine çevriliyor
    • game.py sleep ile hedef kare hızı tutuluyor ve oyuncu kaybetmediyse döngü sürüyor
  • Sonrasında start-frame ve sleep birleştirilebildi, ayrıca öldükten sonra yeniden başlatma için bir döngü eklendi

Oyunu tamamlamak için gereken ayrıntılar

  • Oyun durumu, her karede okunup yazılan state.json dosyası oluşturularak tutuluyor
  • Kullanılan metin karakterlerinin genişliği emoji'lerden dar olduğu için hizalamayı korumak adına çok sayıda hardcode edilmiş boşluk kullanıldı
  • Üstte kayan banner metni özellikle zorluydu; bir seferde kaç karakter gösterileceğini tahmin eden read_n_ad_chars fonksiyonu yazıldı
  • Çalışma dizinini AppleScript'e aktarmak yerine, first-time-setup Python çağrısında şablon değişkenleri yerleştirildi
  • AppleScript Python çıktısını yuttuğu için log'lar bir dosyaya append edilip daha sonra cat ile kontrol edildi
  • Arka arkaya iki karede tıklanırsa bir satır daha yukarı zıplamayı sağlayarak oyun hissi iyileştirildi

Oyun motoru olmadan yapılan Finder oyununun kısıtları

  • Prototip yaklaşık 90 satırdı, nihai kod ise yaklaşık 550 satır, ancak bunun yaklaşık üçte biri boilerplate ya da sabitlerden oluşuyor
  • Oyun motoru kullanmadan küçük bir 2D dizi içinde kare durumunu tutmak, tüm oyunu zihinde taşımayı kolaylaştırdı
  • 4 FPS ve sınırlı girdi büyük kısıtlar olsa da, aynı yöntemle başka oyunlar yapmak da mümkün görünüyor
  • Finder içinde Tetris yapma fikri de ortaya atıldı ve zor ama mümkün olabilecek bir örnek olarak kaldı

1 yorum

 
GN⁺ 2023-10-09
Hacker News yorumları
  • Sözün bittiği derecede şaşırtıcıydı; kendimi kalıpların dışında düşünen biri olarak görmeme rağmen, bu yazıyı okumadan önce bunun imkânsız olduğunu söylerdim sanırım.
    Birinin hafta sonu kendine meydan okumak için yaptığı bir projenin gerçekten mümkün olup olmadığını sonuna kadar zorladığını görmek harikaydı; yazıyı da büyük keyifle okudum.

    • Yazılımda bu yüzden asla imkânsız demem. Zaman ve yaratıcılık yeterli.
  • Bob, cuma öğleden sonra bira içmeye giden ekibe yetişmek için aceleyle ceketini ve sırt çantasını kapar, MacOS’ta çalışan Flappy Bird’ü açık bıraktığını fark etmez.
    Resmî tatilin de eklendiği hafta sonu boyunca dizüstünün SSD yazma ömrü saniyede 4 kare hızında tükenir; salı günü dönen Bob, SSD’nin artık çalışmadığını görür.

    • Şirkette Flappy Dird çalıştıracak kadar biri gerçekten bu kadar acele etme ihtimali taşır mı, merak ediyorum.
    • Yine de bu seviyede bir şeyin yıllarca çalışsa bile diski öldürmemesini umardım.
  • “Bu bir görsel. Artık tweet’i nasıl embed edeceğimi bilmiyorum” yaklaşımı aslında çok daha hoşuma gidiyor.

    • Böyle olunca yazı uzun süre korunuyor. Embed’ler her an kaybolabilir ya da bozulabilir.
    • Twitter’ı engellediğimden beri embed yerlerinde çoğu zaman hiçbir şey görünmüyor ya da boş bir alanın ortasında sadece emoji render ediliyor; o yüzden katılıyorum.
  • OP’nin AppleScript başlatma hızı sorununu JavaScript ile yeniden yazarak çözüp çözemeyeceğini merak ediyorum.
    Birkaç yıldır temel framework ve komut satırı aracı osascript, AppleScript ile JavaScript’i neredeyse eşdeğer biçimde destekliyor; JavaScript tarafında da $ nesnesinin içinde epey şey var.

    • Hiç bilmiyordum, denemem gerekecek. 5 dakika içinde AppleScript hakkında çok faydalı iki yorum almak harika.
  • Python’dan AppleScript event’lerini native olarak çağırmak için py-appscript kullanmayı da deneyebilirsiniz.
    https://github.com/hhas/appscript/tree/master/py-appscript
    Sadece Ruby sürümü Appscript’i kullandım ama Python sürümü de aynı şekilde çalışıyorsa, AppleScript’in kendisine gerek bırakmayacak kadar ideal bir seçenek gibi görünüyor.

    • Böyle bir şey var mı diye bakmak aklıma bile gelmemişti; kesinlikle inceleyeceğim. Yine de ana döngünün AppleScript olması fikrinden gelen küçük bir keyif de var.
  • Böyle olmaması gereken yerdeki oyunları seviyorsanız, daha da saçma projeler de var.
    Fontemon: fontun içindeki oyun https://www.coderelay.io/fontemon.html
    Dungeons & Directories: dosya tarayıcısının içindeki metin macerası https://wheybags.com/dungeons_and_directories/ — bunu ben yaptım.

    • İki bağlantı için de teşekkürler. Dosya sistemini seçmiş olman da ilginç; birkaç hafta sonra Windows makineye döndüğümde bakmak istiyorum.
      Font oyunuyla da biraz oynadım; font yazmak nasıl bir şey görmek için Hexagone[1]’u yaptım. Ne tür bir oyun yapacağımdan emin olamadım ama böyle ilhamların gelmesi eğlenceli.
      [1] http://eieio.games/nonsense/hexagone-converting-hex-to-rgb-w...
    • Defender of the Favicon da var: http://www.p01.org/defender_of_the_favicon/
    • İkinci uygulama yöntemi dahice. Bunun yerine FUSE ile yapmayı düşünüp düşünmediğini merak ediyorum.
    • Komut satırı meraklıları için rpg-cli de var.
      https://github.com/facundoolano/rpg-cli
    • Bunun ne türden başka âleme ait bir büyü olduğunu hiç bilmiyorum.
  • Yazının tamamına yayılan “daha iyisi yapılabilir” tavrını gerçekten sevdim. Tüm geliştiriciler böyle bir zihniyete sahip olsa nasıl olurdu diye düşündürüyor.
    Elbette herkes böyle olursa, her zaman daha iyisi yapılabileceği için hiçbir şey bitmez; bazen “bu kadarı yeterli” diyecek birine ihtiyaç var.

    • Doğru, dengeyi kurmak zor.
      Bu tür hobi projelerinde “daha iyisi yapılabilir” tarafını korumak daha kolay. İşte ise genelde bir gün daha harcayıp cilalamanın değerinin, yapmak istediğimiz diğer her şeyden büyük olup olmadığını düşünmek gerekiyor.
      Ama Flappy Dird’de projeden memnun kalana ya da oyunu artık daha fazla zorlayamayacağıma inanana kadar devam etmek istedim.
  • Başlığı görünce önce Finder penceresinin ikon görünümünde AppleScript ile ikonları sprite gibi hareket ettirme yöntemi sandım.
    Gerçekte liste görünümünde uygulanmış olması ve başlangıçta dizinin “son açılma tarihi”ni poll etme yöntemini kullanması şaşırttı; kesinlikle yaratıcı bir yaklaşım.

  • Küçük oyunları mümkün olan her yere yerleştirme ruhu bu yazıda gerçekten keyifli.
    Eski GNOME’da kullanılabilen Fortune Teller fish görev çubuğu widget’ını hatırlattı.
    Tamamen ayrı olarak, “MacOS Finder” şeklindeki büyük harf kullanımını görünce önce MacOS 8 ya da 9 için bir eklenti sandım; gerçek sonuç ise çok daha eğlenceliydi.

  • Sıradaki meydan okuma: Finder içinde Yaşam Oyunu çalıştırmak ve onun içinde Finder çalıştıran bir Mac’i emüle etmek.

    • MacOS emülasyonunu çıkarıp sadece Yaşam Oyunu olursa, bunun DS_Store dosyalarıyla mümkün olup olmayacağını merak ediyorum.
      Başta klasörleri desene göre oluşturup sağ tıklayarak sıralarsanız ızgaraya yerleşirler. Sonra script, DS_Store dosyasından klasör konumlarını okur; bir sonraki karenin yeni konumlarına günceller ya da gerekirse klasör oluşturup siler.