1 poin oleh GN⁺ 2023-10-09 | 1 komentar | Bagikan ke WhatsApp
  • Flappy Dird adalah implementasi eksperimental yang menjalankan Flappy Bird dengan memanfaatkan daftar file di Finder macOS sebagai layar, serta aksi memilih dan membuka file sebagai input
  • Ide intinya berawal dari membaca nilai Date Last Opened pada direktori, yang hanya diperbarui di Finder, seperti sebuah tombol, lalu menggambar layar dengan mengubah nama file symbolic link
  • Rendering sederhana dengan hanya mengganti nama file lambat dan menimbulkan tearing yang parah, sehingga diperbaiki dengan metode double buffering yang mengubah direktori target jendela Finder melalui AppleScript
  • Input double-click tidak stabil di atas 2 FPS karena keterbatasan timestamp hingga satuan detik, lalu diganti ke metode membaca selected items di Finder
  • Struktur akhirnya menempatkan AppleScript untuk menjalankan loop dan Python untuk menangani status game serta rendering; game berjalan pada 4 FPS, tetapi masih ada input yang kadang terlewat dan keterbatasan khas Finder

Menggunakan Finder sebagai layar game

  • Flappy Dird adalah game berbasis Finder yang kodenya bisa dilihat dan dijalankan langsung dari repositori GitHub
  • Game ini mencakup petunjuk penggunaan, pelacakan skor tertinggi, dan iklan banner bergulir
  • Pengguna memulai game dengan double-click, lalu memilih file apa pun di dalam jendela Finder untuk membuat burung melompat
  • Kecepatan jalannya berada di kisaran 4 frame per detik, dan sulit dibuat berjalan jauh lebih cepat, sementara input juga kadang terlewat

Menggunakan Date Last Opened seperti tombol

  • Titik awalnya adalah adanya field Date Last Opened untuk direktori di Finder
  • Ada tiga perilaku yang diamati
    • Timestamp tidak diperbarui meskipun masuk ke direktori dengan cd
    • Timestamp diperbarui jika symbolic link ke direktori di-double-click di Finder
    • Nilai tersebut bisa dibaca dengan presisi detik melalui mdls
  • Sifat ini memungkinkan pembuatan tombol di dalam Finder
    • Membuat symbolic link button yang menunjuk ke dir di dalam direktori dir
    • Membaca waktu terakhir dibuka dari dir saat memulai
    • Melakukan polling timestamp secara berulang, lalu memprosesnya sebagai input saat nilainya berubah
    • Membuka button tanpa mengubah lokasi Finder untuk mengubah waktu terakhir dibuka dari dir

Menggambar layar 15x15 dengan nama file emoji

  • Menyusun ASCII art dengan font bawaan Finder sulit dilakukan, tetapi emoji yang dimasukkan ke nama file ternyata ditampilkan dengan lebar yang konsisten
  • Prototipe membuat 15 symbolic link turunan yang lagi-lagi menunjuk ke dir di dalam dir, lalu membentuk layar dengan mengubah nama tiap link menjadi satu baris emoji
  • Fungsi rendering menerima nilai bird_y_pos, pipe_locations, dan frame, lalu membentuk grid emoji 15x15
  • Pada setiap frame, nama semua symbolic link di dalam direktori diubah menjadi masing-masing baris dari grid emoji
  • Bahkan diperlukan hack untuk mengatur urutan penggantian nama agar Finder tetap mengurutkan berdasarkan Date Modified
  • Pendekatan ini berhasil berjalan, tetapi sangat lambat dan tearing-nya parah

AppleScript dan double buffering

  • Saat mencari cara menyegarkan daftar file Finder untuk mengurangi tearing, digunakan perintah AppleScript tell application "Finder" to tell front window to update every item
  • Perintah ini sedikit membantu, tetapi tearing tetap ada, dan dari situ arahnya berkembang ke kontrol Finder secara langsung lewat AppleScript
  • Ada beberapa kandidat untuk double buffering
    • Metode mengganti isi direktori secara atomik dengan symbolic link tidak cocok karena Finder tidak bisa menampilkan isi direktori symbolic link dengan cara itu
    • Metode memakai dua direktori yang saling menunjuk bisa mengurangi tearing, tetapi frame hanya bisa maju saat pengguna mengklik, sehingga tidak cocok untuk game
    • Dibutuhkan cara mengubah direktori yang ditampilkan Finder tanpa mengubah “waktu terakhir dibuka”
  • Solusinya adalah mengubah direktori target jendela Finder dengan AppleScript tell application "Finder" to set target of front Finder window to ("PATH" POSIX file)
  • Setelah double buffering diterapkan dengan cara ini, game bisa berjalan mulus di 1 FPS, tetapi tetap lambat

Mengubah metode input: dari double-click ke pemilihan file

  • Input double-click memiliki batas karena presisi timestamp hanya sampai detik, sehingga bermasalah di atas 2 FPS
  • Karena AppleScript bisa membaca selected items pada jendela Finder, input diubah menjadi “pilih file apa pun”
  • Loop game yang baru memiliki struktur berikut
    • Menunggu sampai pengguna melakukan double-click
    • Pada setiap frame, memeriksa lewat AppleScript apakah ada file yang dipilih di jendela saat ini
    • Jika ada pilihan atau waktu terakhir dibuka berubah, burung melompat
    • Mengubah direktori yang ditampilkan Finder lewat AppleScript
    • Menunggu selama sisa waktu untuk menyesuaikan target frame rate
  • Metode ini cukup mendekati 2 FPS, tetapi pemanggilan AppleScript memakan waktu tetap sekitar 0,2 detik
  • Bahkan pada 2 FPS, input masih bisa terlewat jika pengguna memilih file setelah pengecekan input dilakukan

Memindahkan loop utama ke AppleScript

  • Dalam profiling, AppleScript yang hanya mencatat angka 1 ke log lalu berhenti terlihat memakan waktu sekitar 0,14 detik
  • Belakangan ditambahkan koreksi bahwa pengukuran ini bukan ukuran waktu startup AppleScript yang adil, dan dengan AppleScript kosong angkanya bisa lebih dekat ke sekitar 0,06 detik
  • Untuk menghindari biaya startup AppleScript di setiap frame, loop utama dipindahkan ke AppleScript, sementara Python menangani logika game dan rendering
  • Struktur dasarnya seperti berikut
    • game.py await menunggu double-click pengguna untuk memulai
    • game.py start-frame mencatat waktu mulai frame
    • Jumlah item yang dipilih di Finder dibaca lalu diteruskan ke game.py tick untuk merender frame
    • Nama direktori yang disiapkan oleh tick diterima lalu target jendela Finder diarahkan ke direktori tersebut
    • game.py sleep menyesuaikan target frame rate, lalu proses diulang jika pemain belum kalah
  • Setelah itu, start-frame dan sleep bisa digabung, dan ditambahkan loop untuk restart setelah mati

Detail yang diperlukan untuk menyelesaikan game

  • Status game disimpan dengan membuat file state.json dan membacanya serta menulisnya pada setiap frame
  • Teks menggunakan karakter yang lebarnya lebih sempit daripada emoji, sehingga banyak spasi hardcoded diperlukan agar perataan tetap pas
  • Teks banner bergulir di bagian atas sangat sulit ditangani, sampai dibuat fungsi read_n_ad_chars untuk memperkirakan berapa banyak karakter yang bisa ditampilkan sekaligus
  • Alih-alih meneruskan working directory ke AppleScript, variabel template diganti saat pemanggilan Python first-time-setup
  • Karena AppleScript menelan output Python, log ditambahkan ke file lalu diperiksa belakangan dengan cat
  • Sensasi permainan diperbaiki dengan membuat ketukan pada dua frame berturut-turut menyebabkan lompatan satu baris lebih tinggi

Keterbatasan game Finder yang dibuat tanpa engine

  • Prototipe awal hanya sekitar 90 baris, sedangkan kode final sekitar 550 baris, tetapi sekitar sepertiganya adalah boilerplate atau konstanta
  • Tanpa game engine, menyimpan status frame dalam array 2D kecil membuat keseluruhan game tetap mudah dipahami di dalam kepala
  • 4 FPS dan input yang terbatas adalah kendala besar, tetapi pendekatan yang sama tampaknya masih bisa dipakai untuk membuat game lain
  • Bahkan muncul ide membuat Tetris di Finder, dan itu tetap menjadi contoh sesuatu yang sulit tetapi mungkin dilakukan

1 komentar

 
GN⁺ 2023-10-09
Opini Hacker News
  • Luar biasa sampai membuat saya kehabisan kata-kata; bahkan meski saya menganggap diri saya orang yang berpikir di luar kotak, sebelum membaca tulisan ini mungkin saya akan bilang ini mustahil
    Saya sangat senang melihat seseorang mendorong proyek yang dibuat sebagai tantangan pribadi di akhir pekan untuk membuktikan apakah ini benar-benar mungkin, dan tulisannya juga sangat menyenangkan untuk dibaca

    • Karena itulah dalam software, jangan pernah bilang mustahil. Yang dibutuhkan cuma waktu dan kreativitas
  • Bob buru-buru mengambil jaket dan ranselnya untuk ikut tim minum bir pada Jumat sore, dan tidak sadar ia membiarkan Flappy Bird yang berjalan di MacOS tetap menyala
    Selama akhir pekan panjang dengan libur bank, masa pakai tulis SSD laptopnya terkuras pada laju 4 frame per detik, dan saat Bob kembali pada Selasa, ia mendapati SSD-nya tidak lagi berfungsi

    • Saya penasaran apakah orang yang sampai menjalankan Flappy Dird di kantor benar-benar mungkin terburu-buru seperti itu
    • Meski begitu, saya berharap beban sebesar itu tidak akan mematikan drive meski dijalankan bertahun-tahun
  • Saya malah jauh lebih suka pendekatan “Ini gambar. Sekarang saya tidak tahu cara menyematkan tweet”

    • Dengan begini tulisannya lebih awet. Embed bisa hilang atau rusak kapan saja
    • Setuju; sejak saya memblokir Twitter, sering kali di tempat embed tidak terlihat apa pun, atau hanya emoji yang dirender di tengah area kosong
  • Saya penasaran apakah OP bisa menyelesaikan masalah kecepatan startup AppleScript dengan menulis ulangnya dalam JavaScript
    Sejak beberapa tahun lalu, framework bawaan dan tool command-line osascript mendukung AppleScript dan JavaScript hampir setara, dan di JavaScript ada cukup banyak hal di dalam objek $

    • Saya sama sekali tidak tahu dan harus mencobanya. Keren juga, dalam 5 menit saya mendapat dua komentar yang sangat berguna soal AppleScript
  • Untuk memanggil event AppleScript secara native dari Python, bisa juga mencoba py-appscript
    https://github.com/hhas/appscript/tree/master/py-appscript
    Saya hanya pernah memakai Appscript versi Ruby, tetapi kalau versi Python bekerja dengan cara yang sama, ini tampaknya pilihan ideal sampai-sampai AppleScript itu sendiri tidak lagi diperlukan

    • Saya bahkan tidak terpikir untuk mengecek apakah hal seperti ini ada, dan pasti akan saya lihat. Namun ada juga sedikit kesenangan dari fakta bahwa loop intinya adalah AppleScript
  • Kalau suka game di tempat yang seharusnya tidak ada game, ada juga proyek-proyek yang lebih tidak masuk akal
    Fontemon: game di dalam font https://www.coderelay.io/fontemon.html
    Dungeons & Directories: petualangan teks di dalam file browser https://wheybags.com/dungeons_and_directories/ — yang ini saya buat

    • Terima kasih untuk kedua tautannya. Menarik juga bahwa yang dipilih adalah sistem file, dan saya ingin mengeceknya saat kembali ke mesin Windows beberapa minggu lagi
      Saya juga sempat mengutak-atik game font, dan membuat Hexagone[1] untuk melihat seperti apa rasanya menulis font. Saya belum jadi yakin game apa yang akan dibuat, tapi menyenangkan mendapat inspirasi seperti ini
      [1] http://eieio.games/nonsense/hexagone-converting-hex-to-rgb-w...
    • Ada juga Defender of the Favicon: http://www.p01.org/defender_of_the_favicon/
    • Cara implementasi yang kedua brilian. Tapi saya penasaran apakah sempat mempertimbangkan untuk mengimplementasikannya dengan FUSE
    • Untuk para penggila command line, ada juga rpg-cli
      https://github.com/facundoolano/rpg-cli
    • Entah ini sihir dari dimensi mana
  • Saya sangat suka sikap “ini bisa dibuat lebih baik” yang mengalir di seluruh tulisan. Saya jadi membayangkan bagaimana kalau semua developer punya pola pikir seperti ini
    Tentu saja kalau semua orang begitu, karena selalu ada yang bisa dibuat lebih baik, tidak akan ada yang selesai; kadang kita butuh seseorang yang mengatakan “ini sudah cukup baik”

    • Benar, memang sulit menjaga keseimbangan
      Dalam proyek hobi seperti ini, lebih mudah mempertahankan sisi “ini bisa dibuat lebih baik”. Di pekerjaan, biasanya kita harus memikirkan apakah nilai menghabiskan satu hari lagi untuk memolesnya lebih besar daripada semua hal lain yang ingin kita lakukan
      Tapi di Flappy Dird, saya ingin terus maju sampai saya puas dengan proyeknya, atau sampai saya percaya bahwa gamenya tidak bisa didorong lebih jauh lagi
  • Dari judulnya, awalnya saya mengira ini akan berupa cara menggerakkan ikon seperti sprite dengan AppleScript di tampilan ikon jendela Finder
    Ternyata implementasinya di tampilan daftar, dan awalnya dengan melakukan polling “tanggal terakhir dibuka” direktori; saya terkejut, dan itu jelas pendekatan yang kreatif

  • Saya sangat menikmati semangat tulisan ini yang membawa game kecil ke semua tempat yang memungkinkan
    Ini mengingatkan saya pada widget taskbar Fortune Teller fish yang dulu bisa dipakai di GNOME
    Terlepas dari itu, melihat kapitalisasi “MacOS Finder” awalnya membuat saya mengira ini ekstensi untuk MacOS 8 atau 9, tetapi hasil sebenarnya jauh lebih menyenangkan

  • Tantangan berikutnya adalah menjalankan Game of Life di dalam Finder, lalu mengemulasi Mac yang menjalankan Finder di dalamnya

    • Kalau tanpa emulasi MacOS dan hanya Game of Life, saya penasaran apakah ini mungkin dilakukan dengan file DS_Store
      Pertama, buat folder sesuai pola lalu klik kanan untuk mengurutkan, sehingga folder-folder itu ditempatkan di grid. Setelah itu, skrip membaca posisi folder dari file DS_Store, lalu memperbaruinya ke posisi baru untuk frame berikutnya, atau membuat dan menghapus folder bila perlu