2 poin oleh GN⁺ 2024-10-19 | 1 komentar | Bagikan ke WhatsApp
  • Ini adalah tutorial yang memperluas arsitektur ECS dan pemrograman metalinguistik di atas lingkungan pengembangan game Common Lisp ke contoh dungeon crawler nyata
  • Setelah membaca peta XML Tiled dengan cl-tiled, objek CLOS tidak digunakan langsung, melainkan dipindahkan ke komponen ECS untuk memisahkan rendering, collision, dan manajemen memori
  • Dengan mengombinasikan prefab tile, pointer gambar, indeks parent-child, dan finalizer, tutorial ini menghindari pemuatan duplikat dan double free, serta memanfaatkan properti kustom Tiled layaknya data
  • Pemain dan musuh melakukan pergerakan, transisi animasi, dan penanganan collision melalui sistem ECS; musuh melacak target sambil menghindari dinding dengan pencarian jalur A* berbasis cl-astar
  • Dengan menambahkan UI berbasis Nuklear, objek naratif, pause, hingga kondisi kemenangan, tutorial ini menyelesaikan contoh dungeon crawler kecil berukuran sekitar 500 baris

Memulai proyek dan eksekusi dasar

  • Membuat dungeon crawler kecil dengan UI menggunakan arsitektur Entity-Component-System dan teknik pemrograman metalinguistik yang dibahas di Part 1
  • Binary demo yang dapat dijalankan dan kode sumber tersedia di repositori GitHub ecs-tutorial-2
  • Lingkungan pengembangan mengasumsikan lingkungan pengembangan game Common Lisp dari Part 1, dan distribusi Quicklisp diperbarui dari REPL SBCL
    • (ql-util:without-prompting (ql:update-all-dists))
  • Membuat proyek baru ecs-tutorial-2 dengan templat cookiecutter-lisp-game, dan pada contoh ini memilih liballegro sebagai backend
  • Setelah menautkan direktori proyek ke local-projects Quicklisp, ukuran jendela di src/main.lisp diubah menjadi 1280×800
  • Jika menjalankan (ql:quickload :ecs-tutorial-2) dan (ecs-tutorial-2:main), akan tampil jendela hitam dengan resolusi yang ditentukan serta penghitung FPS

Peta Tiled dan penyimpanan ECS

  • Untuk membuat peta dungeon digunakan editor peta open-source Tiled
    • Tiled adalah alat lintas platform dan lintas engine, serta menyimpan data peta sebagai XML
    • Di Common Lisp, cl-tiled memuat file Tiled sebagai objek Lisp
  • Tileset contoh menggunakan Dungeon Tileset II - Extended
    • Karena tile asli berukuran 16×16 terlalu kecil, tile diperbesar 200% dengan ImageMagick dan digunakan sebagai tile 32×32
    • level1.tmx dan file tileset dapat diperoleh dari Resources.zip yang disediakan dalam tutorial
  • Menambahkan dependensi cl-tiled ke ecs-tutorial-2.asd, lalu membuat src/map.lisp baru untuk memisahkan kode pemuatan dan tampilan peta
  • Di src/package.lisp, cl-tiled didaftarkan dengan nickname lokal tiled

Alasan memindahkan objek CLOS ke komponen ECS

  • cl-tiled mengembalikan data peta sebagai objek CLOS, sehingga mudah dieksplorasi di REPL
  • Jika objek ini digunakan langsung dalam game loop, biaya dispatch saat runtime dapat membesar
    • Untuk mengisi jendela 1280×800 dengan tile 32×32, diperlukan minimal 40×25 = 1000 tile
    • Dalam demo terpisah, pada Ryzen 5 3600 12-core, FPS turun dari 20.000 menjadi 600 saat rendering peta diaktifkan
    • Tambahannya sekitar 1/600 - 1/20000 = 0.0016 detik per frame, yakni lebih dari 1,5 ms
  • Memindahkan data yang dibaca cl-tiled ke storage cl-fast-ecs dapat mengurangi dispatch dan meningkatkan pemanfaatan cache CPU
  • Menambahkan dependensi cl-fast-ecs, lalu memanggil ecs:make-storage di init dan ecs:run-systems di update

Komponen peta, tile, dan prefab

  • map adalah komponen tag yang menandai entitas peta yang sudah dimuat
  • map-tile merepresentasikan tile individual, dan memiliki slot Boolean obstacle untuk menandai apakah tile tersebut merupakan rintangan seperti dinding atau pintu tertutup
  • Komponen parent menunjukkan entitas peta mana yang menjadi induk dari tile dan objek terkait peta
    • Dengan menetapkan :index children pada slot entity, entitas anak dari parent tertentu dapat ditemukan dengan cepat
    • Indeks berbasis hash table open-addressing sehingga menyediakan pencarian rata-rata O(1), tetapi ada biaya pembaruan saat pembuatan dan penghapusan
  • Menambahkan hook ke ecs:*entity-deleting-hook* agar saat entitas parent dihapus, entitas anak yang ditemukan melalui indeks children juga ikut dihapus
  • Komponen image hanya menyimpan pointer C ke ALLEGRO_BITMAP
    • Gambar tileset dipotong menjadi bagian 32×32 dengan al_create_sub_bitmap, lalu pointernya disimpan
  • map-tile-prefab adalah prefab tile yang memiliki ID global gid milik tile Tiled
    • Pada gid, :index map-tile-prefab :unique t ditetapkan agar satu entitas prefab dapat ditemukan berdasarkan ID
    • Tile nyata di atas peta menyalin image dan komponen lain dari prefab, tetapi memiliki posisi lewat komponen position terpisah
  • Finalizer image hanya memanggil al_destroy_bitmap ketika entitas adalah map-tile-prefab
    • Tujuannya menghindari double free karena beberapa tile peta berbagi pointer ALLEGRO_BITMAP yang sama
  • position dan size menyimpan koordinat serta ukuran layar sebagai single-float
    • Karena liballegro menangani koordinat layar sebagai floating point presisi tunggal demi kompatibilitas OpenGL, pendekatan yang sama diikuti

Rendering gambar dan pemuatan peta

  • Sistem render-images merender entitas yang memiliki position dan image
    • Mengaktifkan dan menonaktifkan sprite batching dengan al_hold_bitmap_drawing
    • Menggambar gambar pada koordinat tertentu dengan al_draw_bitmap
    • Karena prefab tidak memiliki position, prefab tidak diproses oleh sistem ini
  • load-bitmap adalah fungsi pemuatan gambar yang membungkus al_load_bitmap dengan al:ensure-loaded
  • tile->spec membuat spesifikasi objek ECS untuk pembuatan prefab tile
    • Entitas peta parent
    • Potongan gambar tile
    • ID tile global Tiled
    • Ukuran tile
  • load-tile-prefab memeriksa lewat indeks map-tile-prefab apakah prefab sudah dimuat; jika belum, prefab dibuat dengan make-object
  • load-tile menyalin komponen dari prefab dan menambahkan position saat membuat entitas tile peta nyata
  • load-map menelusuri tileset dan layer dari objek CLOS yang dibaca dengan tiled:load-map
    • Memuat gambar tileset dan membuat setiap tile sebagai prefab
    • Membuat setiap sel pada layer tile sebagai entitas dan menyalin data prefab
  • Urutan layer Tiled dipertahankan sesuai urutan di editor, dan make-entity menjamin nomor entitas yang meningkat
    • Karena sistem memproses entitas dari yang lebih lama, tile pada layer atas digambar belakangan sehingga menutupi layer di bawahnya
  • Menyimpan semua tile sebagai entitas terpisah bukan satu-satunya jawaban; peta statis juga dapat dirender terlebih dahulu ke buffer

Animasi Tile

  • Tiled mendukung tile animasi, sehingga elemen seperti obor atau air mancur ajaib dapat direpresentasikan
  • Menambahkan common.lisp dan animation.lisp untuk memisahkan komponen umum serta komponen dan sistem terkait animasi
  • Komponen animation-frame merepresentasikan satu frame dari animasi
    • sequence adalah nama animasi dan disimpan sebagai tipe keyword
    • Frame untuk animasi tertentu ditemukan melalui indeks sequence-frames
    • duration adalah durasi frame dalam satuan detik
  • animation-state menyimpan status saat ini dari tile animasi di atas map sebenarnya
    • sequence saat ini
    • frame saat ini
    • duration frame saat ini
    • Waktu elapsed selama frame saat ini ditampilkan
  • Menambahkan dependensi let-plus agar kode pergantian frame dapat ditulis lebih ringkas
  • Sistem update-animations menambah elapsed sebesar dt, lalu beralih ke frame berikutnya jika durasinya terlampaui
    • Karena waktu frame bisa lebih kecil daripada dt yang besar, floor digunakan untuk menghitung berapa frame yang harus dilewati
    • Dengan truncate, jika nomor frame melewati panjang daftar, frame akan berputar kembali ke awal
    • Pointer bitmap pada image diganti dengan bitmap dari prefab frame berikutnya
  • Durasi animasi disimpan di Tiled dalam milidetik, sehingga animation->spec mengonversinya ke satuan detik
  • instantiate-animation membuat animation-state pada entitas tile sebenarnya, dan menginisialisasi elapsed dengan angka acak antara 0 dan duration agar animasi yang sama tidak tersinkronisasi sepenuhnya
  • Tile animasi harus memiliki properti Tiled "sequence"
    • Jika properti ini tidak ada, tile akan dimuat dengan nama NIL, tidak dapat ditemukan dengan nama animasi yang diharapkan, dan dapat menyebabkan error tipe

Karakter Pemain dan Kontrol

  • Menambahkan character.lisp dan mendefinisikan komponen character untuk karakter yang dapat bergerak
    • speed adalah kecepatan dalam piksel per detik
    • target-x, target-y adalah koordinat tujuan gerak
    • Nilai target awal diisi dengan single-float-nan agar karakter baru tidak bergerak ke kiri atas tanpa alasan
  • Komponen tag player menggunakan slot bit dan :index player-entity :unique t
    • Ini adalah struktur untuk menemukan entitas pemain dalam O(1) dengan (player-entity 1)
    • Entitas pemain tidak disimpan dalam variabel global
  • Pada implementasi awal, gambar orc dipotong dari tileset menjadi player.png, lalu pemain dibuat secara hardcode dengan load-player
    • Posisinya (64.0, 64.0)
    • Ukurannya 32×32
    • Kecepatannya 100.0
  • Sistem move-characters menggerakkan karakter menuju titik tujuan
    • Jika ada koordinat target NaN, koordinat tersebut diinisialisasi ke posisi saat ini
    • Menggunakan approx-equal, bukan perbandingan langsung bilangan floating-point
    • Koordinat baru dihitung dengan atan, cos, sin, kecepatan, dan dt
  • Sistem control-player membaca input tombol W, A, S, D untuk memperbarui koordinat target
    • Menggunakan al:with-current-keyboard-state dan al:key-down
    • Menggunakan clamp agar tidak keluar dari batas layar
    • Dijalankan setelah sistem gerak dengan :after (move-characters) untuk menghindari masalah inisialisasi NaN

Collision dan Pemuatan Objek dengan Properti Tiled

  • Awalnya, dinding hanyalah gambar biasa seperti tile lantai, sehingga pemain dapat menembus dinding
  • Membuat kelas map-tile sebagai custom type di Tiled dan menambahkan anggota Boolean obstacle
    • Menambahkan map-tile sebagai properti pada tile dinding dan mencentang obstacle
  • Fungsi properties->spec mengubah hash table properti Tiled menjadi spesifikasi objek ECS
    • Custom class Tiled diperlakukan sebagai komponen
    • Anggota class diperlakukan sebagai slot komponen
    • Contohnya berbentuk ((:map-tile :obstacle t))
  • load-tile-prefab memasukkan hasil properties->spec ke dalam spesifikasi prefab
    • Jika tidak ada properti, komponen map-tile default ditambahkan dengan spec-adjoin, dan obstacle memiliki nilai default nil
  • Menambahkan slot tile-hash dan indeks tiles pada komponen position
    • tile-hash membulatkan x dan y ke integer, lalu mengemasnya menjadi satu integer 64-bit
    • Semua entitas pada koordinat kiri atas tile tertentu ditemukan melalui indeks tiles
  • tile-start mengembalikan koordinat kiri atas tile grid yang memuat koordinat sembarang
  • tile-obstacle-p memeriksa apakah di antara entitas pada koordinat yang sama ada tile map-tile dengan obstacle bernilai benar
  • obstaclep memeriksa apakah tile untuk koordinat sembarang merupakan obstacle
  • control-player memeriksa tile sudut relevan dari persegi karakter sesuai arah gerak, dan jika ada obstacle, koordinat target dikembalikan ke posisi saat ini
  • Metode collision ini tidak sempurna
    • Jika dirancang berdasarkan koordinat pusat karakter, matematika dan kodenya bisa menjadi lebih sederhana, tetapi contoh ini mempertahankan cara saat ini untuk menghindari kompleksitas

Memuat Pemain dan Karakter Animasi dari Map

  • Menambahkan custom class character dan player di Tiled
    • character hanya memiliki anggota float speed
    • target-x dan target-y dihilangkan agar memakai nilai default
    • player memiliki anggota int player dengan nilai default 1
  • Menempatkan karakter pemain sebagai objek tile pada layer objek Tiled, lalu memberikan properti character dan player
  • load-map diperluas agar juga memproses tiled:object-layer
    • Properti objek diubah menjadi komponen ECS dengan properties->spec
    • tiled:tile-object menyalin data tile dan animasi dengan load-tile, lalu mengatur posisinya
    • Karena koordinat objek Tiled berbasis kiri bawah, tinggi objek dikurangkan dari y agar sesuai dengan basis kiri atas
  • Pemanggilan dan fungsi load-player yang di-hardcode dihapus
  • Struktur ini membaca data map Tiled langsung sebagai objek ECS, sehingga lebih mendekati data-driven programming
  • Animasi karakter menggunakan sequence orc-idle dan orc-run milik orc yang didefinisikan di tileset
  • change-animation-sequence mengubah animasi saat ini pada entitas
    • Jika sudah berada pada sequence yang sama, tidak melakukan apa pun
    • Frame pertama dari sequence baru dicari melalui indeks sequence-frames, lalu animation-state dan image-bitmap diperbarui
  • move-characters beralih ke :orc-idle saat karakter berhenti, dan ke :orc-run saat bergerak

Musuh, Game Over, dan Pencarian Jalur A*

  • Komponen enemy memiliki dua slot yang dibutuhkan untuk perilaku musuh
    • vision-range: jarak saat musuh mulai melihat dan bereaksi terhadap pemain
    • attack-range: jangkauan serangan
  • Untuk mengakhiri game, variabel global *should-quit* ditambahkan, dan loop utama berhenti jika nilai ini benar
  • Sistem handle-enemies mengambil koordinat pemain lalu membandingkannya dengan musuh
    • Jika pemain berada dalam jarak pandang, koordinat tujuan musuh diatur ke posisi pemain
    • Jika pemain berada dalam jangkauan serangan, *should-quit* diatur menjadi benar dan kotak pesan native You died ditampilkan
  • Animasi musuh menggunakan sekuens demon-idle dan demon-run
    • move-characters memilih animasi orc untuk pemain dan animasi demon untuk musuh berdasarkan hasil has-player-p
  • Dengan metode pengejaran langsung, musuh juga menembus dinding, sehingga pencarian jalur A* ditambahkan
  • cl-astar ditambahkan sebagai dependensi
    • Library ini menghasilkan fungsi pencarian jalur yang dioptimalkan untuk masalah tertentu melalui makro
  • Jalur tidak disimpan sebagai array di dalam slot komponen; setiap titik jalur direpresentasikan sebagai entitas terpisah
    • path-point memiliki x, y, dan traveller, sementara traveller memiliki indeks path-points
    • path menyimpan tujuan akhir destination-x dan destination-y
    • Koordinat tujuan character menunjukkan titik jalur berikutnya, sedangkan path menunjukkan tujuan akhir
  • Sistem follow-path mengambil titik jalur pertama dan memindahkan karakter ke titik tersebut
    • Saat titik dicapai, entitas path-point terkait dihapus
    • Jika tidak ada titik lagi, komponen path dihapus
  • find-path didefinisikan dengan a*:define-path-finder
    • Ukuran dunia dihitung dengan membagi ukuran jendela dengan ukuran tile
    • Menggunakan pengindeks row-major
    • Ketercapaian tujuan ditentukan berdasarkan apakah koordinat tile sama
    • Tetangga dienumerasi dalam 8 arah
    • Rintangan atau gerakan diagonal yang melintasi rintangan dibuat praktis mustahil dengan biaya most-positive-single-float
    • Heuristik menggunakan octile distance
    • Jika sudah ada jalur, titik-titik jalur dihapus lalu path baru dialokasikan
    • Setiap titik pada jalur hasil dibuat sebagai entitas yang memiliki path-point dan parent
  • handle-enemies memanggil find-path ketika musuh melihat pemain dan tujuan jalur yang ada berbeda dari posisi pemain
  • Setelah perubahan ini, musuh mengejar pemain sambil menghindari rintangan

UI Game Berbasis Nuklear

  • GUI diperlukan untuk elemen naratif, tetapi library GUI tradisional seperti Qt atau GTK tidak cocok untuk UI game yang digambar ke konteks grafis liballegro
  • Nuklear digunakan sebagai library UI
    • Ada binding Common Lisp cl-liballegro-nuklear untuk digunakan bersama liballegro
    • Binding ini juga menyediakan DSL untuk antarmuka deklaratif
  • Dependensi cl-liballegro-nuklear/declarative ditambahkan, dan src/narrative.lisp ditambahkan sebagai file baru
  • Nickname lokal ui didaftarkan pada package untuk merujuk singkat ke cl-liballegro-nuklear/declarative
  • Alegreya dari Google Fonts digunakan sebagai font UI, dan nama file diubah menjadi alegreya-sc.ttf
  • ui:defwindow narrative mendefinisikan fungsi jendela naratif
    • Posisi jendela dihitung agar berada di area tengah layar
    • ui:label-wrap menampilkan teks dengan line wrap otomatis
    • ui:button-label "Ok" mengembalikan benar saat diklik
  • Nuklear adalah library UI immediate mode
    • Alih-alih retained mode yang mempertahankan objek widget di memori, rendering dan pemrosesan dilakukan setiap frame
    • Klik tombol diproses bukan melalui callback, melainkan melalui nilai balik dan kondisi di setiap frame
  • main.lisp memuat font UI dan menginisialisasi konteks UI dengan nk:allegro-init
    • Dalam event loop, nk:input-begin, nk:allegro-handle-event, dan nk:input-end dipanggil
    • Saat rendering, nk:allegro-render dipanggil
    • Saat keluar, nk:allegro-shutdown dan nk:allegro-font-del dipanggil

Skin UI dan Objek Naratif

  • Karena UI default terasa monoton, styling diterapkan menggunakan aset gambar fantasy-ui-borders dari Kenney
  • Gambar UI disimpan dalam variabel global *window-background*, *button-normal-background*, *button-hover-background*, dan *button-active-background*
  • load-ui memuat gambar dengan nk:allegro-create-image, sedangkan unload-ui melepaskan resource gambar di sisi C dengan nk:allegro-del-image
  • load-ui dipanggil di init, dan unload-ui dipanggil saat loop utama berakhir
  • Argumen :styles pada ui:defwindow menentukan latar belakang, gambar untuk tiap status tombol, dan warna teks
  • Komponen narrative merepresentasikan objek untuk environmental storytelling
    • text: teks yang akan ditampilkan
    • shown: apakah sudah pernah ditampilkan
    • active: apakah jendela saat ini aktif
    • active memiliki indeks active-narratives
  • Sistem show-narrative menampilkan jendela jika pemain berada di dekat objek naratif
    • Jarak interaksi dihitung dari +interact-distance-factor+ dan ukuran tile pemain
    • Jendela ditampilkan jika sudah aktif, belum pernah ditampilkan, atau tombol E ditekan
    • Jendela ditutup dengan salah satu dari tombol Ok, Esc, Space, atau Enter
  • Di Tiled, tipe kustom narrative dibuat dan anggota string text ditambahkan
  • Agar selaras dengan deteksi tabrakan, objek yang tidak dapat dilewati harus disejajarkan koordinatnya ke grid tile
  • Masalah dungeon yang terus bergerak saat jendela naratif tampil dicegah dengan kondisi eksekusi sistem
    • :when (null (active-narratives t)) ditambahkan ke move-characters dan control-player
    • Jika ada naratif aktif, sistem gerakan dan kontrol tidak dijalankan
  • Kondisi kemenangan ditambahkan sebagai komponen tag win
    • Jika jendela ditutup pada objek yang juga memiliki narrative, *should-quit* diatur menjadi benar dan game berakhir

Penutup dan Cakupan

  • Contoh akhir menggunakan cl-fast-ecs, cl-tiled, cl-astar, dan cl-liballegro-nuklear untuk membangun dungeon crawler Souls-like dengan environmental storytelling, AI musuh, dan GUI
  • Skala implementasinya sekitar 500 baris kode
  • Kode lengkap tersedia di repositori GitHub, dan selain kode tutorial juga mencakup deklarasi tipe opsional menggunakan declaim
  • Desain suara, cutscene, menu utama, perpindahan level, “door problem”, dan sejenisnya tidak dibahas
  • Autumn Lisp Game Jam 2024 berlangsung pada 25 Oktober 2024 di itch.io; dalam acara ini peserta membuat game selama 10 hari dengan dialek Lisp lalu saling menilai dan memberi umpan balik
  • Bagian ini didasarkan pada karya peserta Spring Lisp Game Jam 2023, Thoughtbound
  • Bagian berikutnya memberi pratinjau tantangan membuat game strategi real-time dengan memperbesar skala dan menambahkan AI yang lebih canggih

1 komentar

 
GN⁺ 2024-10-19
Opini Hacker News
  • Saya berharap semua tutorial teknis bentuknya seperti ini. Strukturnya bagus, hampir tidak ada kesalahan sintaks, setiap kali memperkenalkan topik baru penjelasannya pas porsinya, lengkap dengan contoh kode penuh dan visual yang menunjukkan apa yang sebenarnya dilakukan kode tersebut
    Materinya cukup panjang untuk dibahas secara mendalam, tetapi juga cukup mandiri sehingga tetap bisa diikuti meski belum membaca bagian 1 dan hanya pernah mencoba Common Lisp beberapa bulan beberapa tahun lalu. Saya sendiri cukup banyak memakai Clojure dan Emacs Lisp
    Bravo, awkravchuk/Andrew :^)
    (Crossposted from https://mxjn.me/2024/10/17/1)

    • Yang paling penting, ini tulisan, bukan video. Bisa disalin-tempel, dibaca dengan jelas, diikuti sesuai kecepatan sendiri, dan dikonsumsi dengan tenang
      Juga mudah disimpan untuk penggunaan offline atau arsip, bisa diberi anotasi, dan mudah dicari
  • Jarang ada hal di dunia teknis yang lebih menyentuh daripada proyek atau tulisan bagus tentang Common Lisp. Tulisan ini benar-benar seperti hadiah besar
    Saya membacanya saat bagian 1 terbit, dan sangat menantikan membaca bagian ini juga. Salut untuk penulisnya

  • package.sh dan keseluruhan manajemen build untuk 3 sistem operasi saja sudah merupakan masterclass tersendiri. Saya belajar banyak hanya dengan menelusuri repositori GitHub-nya
    Biasanya saya membangun aplikasi command-line Common Lisp dengan SBCL atau LispWorks, tetapi lain kali mungkin akan mencoba ECL. Keren bisa punya build untuk macOS dan Linux sekaligus, dan sepertinya menyenangkan juga mencoba hal baru

    • Katanya ia sudah beberapa tahun membangun konfigurasi CI itu di atas infrastruktur CL, tetapi masih terus rusak :D
  • Tulisan yang sangat bagus. Saya sedang mengembangkan shooter multiplayer third-person berbasis mantra dengan Lisp, tepatnya ClojureScript. Ini game 3D berbasis web, dan saya berencana menulis perjalanan ini di blog, termasuk tool dan abstraksi yang saya buat untuk proyek tersebut
    Kalau tertarik, demonya ada di sini: https://wizardmasters.io

    • Jon Blow juga pernah mencoba membuat game seperti ini dulu. Mungkin ada hal yang bisa dipelajari dari melihat bagaimana dan mengapa itu gagal
  • Tulisannya sendiri benar-benar solid, tetapi melihat proses setup di bagian 1 yang berlapis-lapis—Common Lisp itu sendiri, Python, C, dan seterusnya—rasanya menunjukkan mengapa CL tidak begitu populer, terutama di kalangan programmer muda
    Sayang sekali, dan semoga ada yang mau bersusah payah membuat bahasa ini lebih mudah diakses dari sisi instalasi

    • Tidak bisa dibilang menargetkan masalah yang persis sama, tetapi https://ciel-lang.org/ setidaknya merupakan upaya untuk menyelesaikan sebagian masalah terlalu banyak langkah
      Sejauh yang saya pahami, fokusnya lebih pada masalah terlalu banyak pilihan dan default lama yang terlihat usang
  • Event loop adalah contoh bagus yang menunjukkan betapa loop benar-benar merupakan domain-specific language untuk iterasi. Suka atau tidak ;)

    • Bukankah bisa memakai https://iterate.common-lisp.dev/ saja sebagai ganti loop? Tidak ada sintaks asing yang bukan S-expression, dan tidak perlu do untuk kembali ke sintaks Lisp
      Bisa memakai if/when biasa tanpa else/end yang mengganggu, dan secara umum juga menambahkan fitur-fitur berguna
    • Awalnya saya mencemoohnya, tetapi setelah beberapa tahun memprogram dengan Common Lisp, loop menjadi salah satu konstruksi CL favorit saya
  • Tulisan ini mengingatkan saya pada "Caves of Clojure": https://stevelosh.com/blog/2012/07/caves-of-clojure-01/

  • Kebetulan minggu ini saya mulai mengembangkan roguelike dengan Python, dan sepertinya keren juga kalau dicoba dengan Lisp

  • Rasanya seperti tertipu. Saya datang untuk belajar cara membuat game sederhana, tetapi malah belajar sangat banyak tentang komputasi secara umum
    Benar-benar bagus