Kiat untuk bermain golf di brainfuck

23

Apa tips umum yang Anda miliki untuk bermain golf di brainfuck? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk brainfuck (mis. "Hapus komentar" bukan jawaban). Silakan kirim satu tip per jawaban.

RAM
sumber

Jawaban:

25

Menempatkan satu tip per jawaban akan menjadi terlalu banyak jawaban.

  • Belajar berpikir di Brainfuck. Ini sangat berbeda dari yang lainnya. Baca dan tulis, dan tulis ulang, dan tulis ulang banyak program brainfuck. Bahasa ini tidak memberi Anda banyak hal untuk dikerjakan, jadi penting untuk menggunakan apa yang diberikannya secara fleksibel dan efisien. Jangan biarkan abstraksi apa pun terjadi antara Anda dan bahasa - masuklah ke sana dan bergulat dengannya.

  • Dapatkan sangat nyaman dengan kontrol aliran tidak rusak. Untuk keluar dari loop keputusan, daripada mem-nolkan sel awal dengan menyalinnya di tempat lain dan kemudian menyalinnya kembali setelah meninggalkan loop, sering kali lebih baik untuk memindahkan pointer ke nol yang sudah ada di dekatnya. Ya, ini berarti pointer akan berada di tempat yang berbeda tergantung pada apakah Anda melewati loop, tetapi itu juga berarti tempat-tempat itu mungkin memiliki pengaturan yang berbeda dari nol di dekatnya dan nonzeros, yang dapat Anda gunakan untuk menyinkronkan ulang lokasi pointer menggunakan loop lain. Teknik ini sangat mendasar untuk pemrograman Brainfuck yang baik, dan berbagai bentuknya akan terus terbukti bermanfaat.

  • Itu dan fakta bahwa setiap >atau <biaya berarti bahwa rincian tata letak memori itu penting. Cobalah variasi tata letak sebanyak yang Anda sabar. Dan ingat, tata letak memori Anda tidak harus menjadi pemetaan data yang kaku ke lokasi. Itu bisa berubah selama eksekusi.

  • Pada skala yang lebih besar, pertimbangkan dan bahkan coba terapkan berbagai algoritma yang berbeda. Awalnya tidak akan jelas algoritma apa yang terbaik; bahkan mungkin tidak jelas pendekatan dasar apa yang terbaik, dan mungkin akan menjadi sesuatu yang berbeda dari apa yang terbaik dalam bahasa normal.

  • Jika Anda berurusan dengan data berukuran besar atau variabel, lihat apakah ada cara Anda dapat mengatasinya secara lokal, tanpa harus melacak seberapa besar itu atau lokasi numerik Anda di dalamnya.

  • Data yang sama dapat menjadi dua hal yang berbeda. (Paling sering, angka atau karakter dan juga penanda posisi bukan nol. Tapi lihat acak.b , di mana penghitung bit berfungsi ganda sebagai nilai satu sel otomat seluler).

  • Kode yang sama dapat melakukan dua hal yang berbeda, dan jauh lebih mudah untuk membuatnya dalam bahasa di mana kode tersebut sama generiknya <+<. Waspadai kemungkinan seperti itu. Bahkan, kadang-kadang Anda mungkin memperhatikan, bahkan dalam apa yang tampaknya merupakan program yang ditulis dengan baik, bahwa ada bagian-bagian kecil yang dapat dihapus seluruhnya, tidak ada yang ditambahkan, dan masalahnya, secara kebetulan, masih berjalan dengan sempurna.

  • Dalam sebagian besar bahasa, Anda sering menggunakan kompiler atau juru bahasa untuk memeriksa perilaku program Anda. Bahasa Brainfuck menuntut kontrol konseptual yang lebih besar; jika Anda memerlukan kompiler untuk memberi tahu Anda apa yang dilakukan program Anda, Anda tidak memiliki pemahaman yang cukup tentang program Anda, dan Anda mungkin perlu menatapnya lagi - setidaknya jika Anda ingin memiliki gambar yang cukup jelas tentang halo konseptual dari program serupa untuk menjadi pandai golf. Dengan latihan, Anda akan menghasilkan selusin versi program Anda sebelum Anda mencoba menjalankannya, dan pada saat itu Anda akan 95% yakin bahwa versi terpendek Anda akan berjalan dengan benar.

  • Semoga berhasil! Sangat sedikit orang yang bersusah payah menulis Brainfuck secara ringkas, tetapi saya pikir itulah satu-satunya cara bahasa tersebut dapat membenarkan perhatian yang berkelanjutan - sebagai bentuk seni yang sangat tidak dikenal.

Daniel Cristofani
sumber
3
Membaca ini membuat saya ingin mencoba pemrograman di Brainfuck sekarang ..
Claudiu
Sial, pikir aku mengenali namamu. Penggemar program brainfuck Anda, terutama penerjemah mandiri super pendek Anda!
Jo King
"Kadang-kadang Anda mungkin memperhatikan ... bahwa ada bagian-bagian kecil yang dapat dihapus seluruhnya, tidak ada yang ditambahkan, dan masalahnya, secara kebetulan, masih berjalan dengan sempurna." Ini sangat benar, terutama untuk pemula. Saya hanya pernah menulis satu jawaban dalam BF sejauh yang saya ingat, namun riwayat revisinya mengandung setidaknya 3 contoh di mana saya bermain dengan program dan secara acak pergi, "hei, program masih berfungsi jika saya menghapus bit ini! "
ETHproduk
"Cobalah sebanyak mungkin variasi tata letak yang Anda sabar," atau dapat dengan kasar : P
Esolanging Fruit
9

Beberapa tips di sini:

Konstanta:

Halaman konstanta The Esolangs memiliki daftar yang sangat berguna satu cara terpendek untuk membuat nilai-nilai tertentu. Saya mendapati diri saya membaca halaman ini setidaknya dua kali per program.

Awal dari segalanya:

+++[[<+>>++<-]>]

Ini mengatur rekaman dalam format 3 * n ^ 2, yang terlihat seperti

3 6 12 24 48 96 192 128 0 0 '

Mengapa ini sangat penting?

Mari kita lihat daftarnya:

  • 3 dan 6 membosankan
  • 12: Dekat dengan 10 (baris baru) atau 13 (carriage return). Dapat juga digunakan untuk penghitung untuk 0-9
  • 24: Dekat dengan 26, jumlah huruf dalam alfabet
  • 48: ASCII untuk 0
  • 96: Dekat dengan 97, ASCII untuk a
  • 196 dan 128: 196-128 = 64, mendekati 65, ASCII untuk A.

Dari algoritma yang satu ini, kita berada di awal hampir setiap urutan dalam rentang ASCII, bersama dengan penghitung untuk masing-masing dan baris baru yang mudah dijangkau.

Contoh praktis:

Mencetak semua huruf besar dan kecil dan angka.

Dengan algoritma:

+++[[<+>>++<-]>]<<[-<->]<<<<++[->>+.>+.<<<]<--[>>.+<<-]

Tanpa:

+++++++++++++[->+++++++>++>+++++>++++>+<<<<<]>+++++>[-<+.>>.+<]>>---->---[-<.+>]

Kami menghabiskan sebagian besar byte hanya menginisialisasi rekaman pada contoh kedua. Beberapa di antaranya diimbangi oleh gerakan ekstra pada contoh pertama, tetapi metode ini jelas memiliki keunggulan.

Beberapa algoritma menarik lainnya dalam nada yang sama:

3 * 2 ^ n + 1:

+++[[<+>>++<-]+>]
Tape: 4 7 13 25 49 65 197 129 1 0'

Ini mengimbangi nilai dengan 1, yang mencapai beberapa hal. Itu membuat 12 carriage return, 64 awal yang sebenarnya dari huruf besar, dan 24 yang lebih dekat ke 26.

2 ^ n:

+[[<+>>++<-]>]
Tape: 1 2 4 8 16 32 64 128

Karena 64 baik untuk huruf besar, 32 adalah ASCII untuk spasi, dan 128 dapat digunakan sebagai penghitung untuk 26 (130/5 = 26). Ini dapat menghemat byte dalam situasi tertentu di mana angka dan huruf kecil tidak diperlukan.

Pilih implementasi yang sesuai dengan pertanyaan:

  • Sel negatif hampir selalu bermanfaat, dan tidak ada alasan untuk menghindarinya (kecuali jika itu tidak mengubah bytecount Anda)
  • Hampir sama persis dengan pembungkus sel, terlebih lagi karena banyak konstanta menggunakan pembungkus.
  • Ukuran sel sewenang-wenang berguna untuk urutan matematika yang tak terbatas, seperti menghitung urutan Fibonacci secara tak terbatas ( +[[-<+>>+>+<<]>]), atau memproses angka yang lebih besar / negatif. The downside adalah bahwa beberapa metode umum, seperti [-]dan [->+<]tidak dapat diandalkan, kalau-kalau jumlahnya negatif.
  • EOF sebagai 0, -1 atau tidak ada perubahan. 0 biasanya lebih disukai, karena Anda dapat mengulang seluruh input tanpa pemeriksaan tambahan. -1 berguna ketika melompati struktur array. Saya belum menemukan gunanya tanpa perubahan :(.

Pantau apa yang terjadi:

Setiap saat Anda harus memiliki komentar tentang di mana pointer harus berada dalam kaitannya dengan data di sekitarnya, dan pastikan bahwa Anda mengetahui kisaran nilai yang mungkin dari setiap sel. Ini sangat penting ketika Anda telah membagi pointer sebelum loop, karena Anda ingin menggabungkan dua kemungkinan kembali bersama setelahnya.

Pada titik mana pun, kode saya dipenuhi dengan komentar di setiap baris lain yang terlihat seperti ini:

*0 *dat a_1 ?  0' !0 0*
 or
*0 *dat 0' ap1 0  !0 0*

Beberapa saran tambahan adalah memberikan simbol makna khusus. Dalam contoh di atas, 'adalah di mana pointer berada, *berarti pengulangan dalam arah itu, ?berarti sel dengan nilai yang tidak diketahui, !0berarti sel non-nol, _adalah pengganti -dan pmerupakan pengganti +. ormenyiratkan bahwa rekaman itu bisa terlihat seperti representasi, dan perlu ditangani seperti itu.

Skema simbol Anda tidak harus sama dengan skema saya (yang memiliki beberapa kekurangan), tetapi harus konsisten. Ini juga sangat berguna ketika men-debug, karena Anda dapat menjalankannya sampai titik itu dan membandingkan rekaman aktual dengan apa yang seharusnya Anda miliki, yang dapat menunjukkan kelemahan potensial dalam kode Anda.

Jo King
sumber
5

Saran utama saya adalah jangan.

OKE, baiklah, Anda menginginkan sesuatu yang lebih berguna dari itu. BF sudah merupakan bahasa yang sangat singkat, tetapi yang benar-benar membunuh Anda adalah aritmatika, yang secara efektif perlu dilakukan secara unary. Sebaiknya baca halaman konstanta di Esolang untuk memilih cara menulis angka besar secara efisien, dan mengeksploitasi pembungkus jika memungkinkan.

Akses memori juga sangat mahal. Karena Anda membaca dari kaset, Anda harus mengingat di mana kepala Anda bergerak pada waktu tertentu. Tidak seperti bahasa lain di mana Anda bisa hanya menulis a, b, c, di bf Anda harus secara eksplisit memindahkan kepala beberapa jumlah byte kiri atau kanan, sehingga Anda harus memperhatikan di mana Anda menyimpan apa. Saya cukup yakin bahwa mengatur memori Anda secara optimal adalah NP-hard, jadi semoga sukses dengan yang itu.

ymbirtt
sumber
5

Dalam jawaban ini saya akan merujuk ke sel khusus pada rekaman itu berkali-kali. Tidak masalah sel yang mana, tetapi sel yang sama di seluruh jawaban. Untuk keperluan posting ini, saya akan memanggil sel itu "Todd".

Saat mencoba menyetel sel ke nilai konstan, terkadang membayar untuk tidak menyelesaikannya segera. Misalnya, Anda ingin Todd mengandung 30. Kemudian dalam kode Anda (yang dapat mengubah nilai Todd tetapi tidak pernah membacanya), Anda kembali ke Todd. Jika nilai Todd adalah 0, program keluar. Jika tidak, nilai Todd dicetak selamanya.

Menurut halaman esolangs.org dari konstanta brainfuck (yang mungkin bisa menjadi subjek tip sendiri!) Cara terpendek untuk mendapatkan 30 adalah >+[--[<]>>+<-]>+. Yang memimpin >hanya ada di sana untuk memastikan bahwa tidak ada di sebelah kiri pointer yang dimodifikasi, tetapi dalam hal ini kita akan menganggap kita tidak peduli tentang itu dan menjatuhkannya. Menggunakan kode itu, kode Anda akan terlihat seperti ini:

+[--[<]>>+<-]>+(MISC. CODE)(GO TO TODD)[.]

Anda dapat memikirkan potongan kode pertama seperti ini:

(SET TODD TO 30)(MISC. CODE)(GO TO TODD)[.]

Tapi ingat dua karakter terakhir dalam chunk bahwa: >+. Sama validnya untuk berpikir seperti ini:

(SET TODD TO 29)(GO TO TODD)(ADD 1 TO TODD)(MISC. CODE)(GO TO TODD)[.]

Perhatikan bahwa Anda (GO TO TODD)dua kali! Anda bisa menulis kode seperti ini:

(SET TODD TO 29)(MISC. CODE)(GO TO TODD)(ADD 1 TO TODD)[.]
+[--[<]>>+<-](MISC. CODE)(GO TO TODD)+[.]

Dengan asumsi bahwa jumlah byte yang diperlukan (GO TO TODD)sama dengan sebelumnya, satu langkah lebih sedikit == satu byte lebih sedikit! Terkadang fakta bahwa posisi awal Anda telah berubah memang mengambil manfaat itu, tetapi tidak selalu.

monmon bawah tanah
sumber
0

Tip kecil untuk tantangan tanpa input. Anda dapat menggunakan ,alih-alih [-], jika Anda perlu membersihkan sel dengan cepat, karena sebagian besar penafsir (termasuk TIO.run satu) akan mengatur konten sel ke representasi EOF menjadi nol. Ini membuat program-program sedikit tidak dapat ditonton, tetapi siapa yang peduli dengan kode golf itu?

Krzysztof Szewczyk
sumber