Kiat untuk bermain golf di QBasic

13

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

Tips yang berkaitan dengan emulator QB64 juga diterima. Ini memiliki beberapa fitur tambahan yang tidak ada di Microsoft QBasic.

DLosc
sumber
Saya ingin tahu tentang motivasi Anda. Saya belum pernah menggunakan QBASIC sejak kelas pemrograman kelas 10 saya. Luar biasa bagaimana saya menyimpan langsung ke 1,44 floppy disk tanpa bentuk kontrol versi apa pun dan (biasanya) terhindar dari kegagalan besar.
Andrew Brēza
5
@ Motivasi AndrewBrēza? Sama seperti motivasi saya bermain golf dalam bahasa apa pun: untuk bersenang-senang! Saya menikmati menulis program kecil dalam QBasic (meskipun saya tidak ingin menggunakannya untuk hal yang serius). Ada juga bonus tambahan yang menghasilkan suara dan grafik (baik teks maupun piksel), yang tidak disukai oleh bahasa "nyata" saya, Python.
DLosc
Jauh lebih mudah untuk menulis game grafis dalam QBasic daripada di python.
Anush
Jika ada yang ingin mencoba aplikasi grafis QBasic langsung di browser, mereka dapat menggunakan ini: github.com/nfriend/origins-host
mbomb007

Jawaban:

10

Ketahui konstruksi perulangan Anda

QBasic memiliki beberapa konstruksi perulangan: FOR ... NEXT, WHILE ... WEND, dan DO ... LOOP. Anda juga dapat menggunakan GOTOatau (dalam beberapa situasi) RUNuntuk mengulang.

  • FOR ... NEXTcukup bagus dalam apa yang dilakukannya. Tidak seperti di Python, itu hampir selalu lebih pendek dari yang setara WHILEatau GOTOloop, bahkan ketika itu menjadi sedikit lebih menarik:

    FOR i=1TO 19STEP 2:?i:NEXT
    i=1:WHILE i<20:?i:i=i+2:WEND
    i=1:9?i:i=i+2:IF i<20GOTO 9
    

    Perhatikan bahwa Anda tidak perlu mengulang nama variabel setelahnya NEXT, dan Anda dapat menghilangkan spasi di antara angka dan sebagian besar kata kunci berikut.

  • WHILE ... WENDbagus untuk ketika Anda memiliki loop yang mungkin perlu dijalankan 0 kali. Tetapi jika Anda tahu loop akan mengeksekusi setidaknya sekali, GOTOmungkin satu byte lebih pendek:

    WHILE n>1:n=n\2:WEND
    1n=n\2:IF n>1GOTO 1
    
  • Saya hanya menggunakan DO ... LOOPuntuk loop tak terbatas (kecuali di mana RUNdapat digunakan sebagai gantinya). Walaupun biayanya sama dengan jumlah karakter sebagai tanpa syarat GOTO, itu sedikit lebih intuitif untuk dibaca. (Perhatikan bahwa "loop tak terbatas" dapat mencakup loop yang Anda keluar dari menggunakan GOTO.) The DO WHILE/ DO UNTIL/ LOOP WHILE/ LOOP UNTILsintaks terlalu verbose; Anda lebih baik menggunakan WHILEatau GOTOjika perlu.
  • GOTOadalah, seperti yang disebutkan di atas, cara umum terpendek untuk menulis do / while. Gunakan nomor baris satu digit, bukan label. Perhatikan bahwa ketika a GOTOadalah satu-satunya hal di THENbagian IFpernyataan, ada dua sintaks shortcut yang sama pendeknya yang tersedia:

    IF x>y GOTO 1
    IF x>y THEN 1
    

    GOTOjuga dapat digunakan untuk membuat aliran kontrol yang lebih rumit . Para penentang menyebut ini sebagai "kode spageti," tetapi ini adalah kode golf: tidak terbaca hampir merupakan suatu kebajikan! GOTOkebanggaan!

  • RUNberguna ketika Anda harus melompat ke tempat yang tetap dalam program dan Anda tidak perlu menyimpan nilai variabel apa pun. RUNdengan sendirinya akan memulai kembali program dari atas; dengan label atau nomor baris, itu akan dimulai lagi di baris itu. Saya terutama menggunakannya untuk membuat loop tanpa batas stateless .
DLosc
sumber
5

Gunakan pintasan untuk PRINTdanREM

Anda dapat menggunakan ?sebagai ganti PRINT, dan 'bukannya REM(komentar).

'mungkin juga berguna ketika melakukan polyglot dengan bahasa yang mendukung 'sebagai bagian dari sintaksis char atau string.

Uriel
sumber
5

Pengujian keterbagian

Dalam program yang mengharuskan Anda menguji apakah satu bilangan bulat dapat dibagi oleh yang lain, cara yang jelas adalah menggunakan MOD:

x MOD 3=0

Tetapi cara yang lebih singkat adalah menggunakan pembagian integer:

x\3=x/3

Artinya, xint-div 3sama dengan xfloat-div 3.

Perhatikan bahwa kedua pendekatan ini akan kembali 0untuk falsey dan -1untuk truey, jadi Anda mungkin perlu meniadakan hasilnya, atau mengurangi itu alih-alih menambahkan.


Jika Anda membutuhkan kondisi yang berlawanan (yaitu xadalah tidak habis dibagi 3), pendekatan yang jelas adalah dengan menggunakan tidak-sama Operator:

x\3<>x/3

Tetapi jika xdijamin tidak negatif, kita bisa menghemat satu byte. Divisi integer memotong hasilnya, sehingga akan selalu kurang dari atau sama dengan divisi float. Oleh karena itu, kita dapat menulis kondisinya sebagai:

x\3<x/3

Demikian pula, jika xdijamin negatif, pemotongan akan meningkatkan hasilnya, dan kita bisa menulis x\3>x/3. Jika Anda tidak tahu tandanya x, Anda harus patuh <>.

DLosc
sumber
5

Penyalahgunaan scanner

Seperti dalam banyak bahasa, mengetahui karakter mana yang bisa dan tidak bisa dihapus itu penting.

  • Setiap ruang di sebelah simbol dapat dihapus: IF""=a$THEN?0
  • Ruang biasanya dapat dihilangkan antara angka dan huruf yang terjadi agar : FOR i=1TO 10STEP 2. Ada beberapa perbedaan antara QBasic 1.1 (tersedia di archive.org ) dan QB64 :
    • QBasic 1.1 memungkinkan penghapusan ruang antara angka dan huruf berikut. Selanjutnya, dalam pernyataan cetak, itu akan menyimpulkan titik koma antara nilai berturut-turut: ?123xmenjadi PRINT 123; x. Pengecualian untuk di atas adalah urutan seperti 1e2dan 1d+3, yang diperlakukan sebagai notasi ilmiah dan diperluas ke 100!dan 1000#(presisi tunggal dan ganda, masing-masing).
    • QB64 umumnya sama, namun angka tidak bisa diikuti oleh d, eatau fsama sekali kecuali mereka adalah bagian dari notasi literal ilmiah well-formed. (Misalnya, Anda tidak bisa menghilangkan spasi setelah nomor baris dalam 1 FORatau 9 END, seperti yang Anda bisa dalam QBasic.) Ini hanya menyimpulkan tanda koma dalam pernyataan cetak jika salah satu ekspresi adalah string: ?123"abc"berfungsi, tetapi tidak ?TAB(5)123atau ?123x.
  • Berbicara tentang titik koma, QBasic 1.1 menambahkan tanda titik koma ke PRINTpernyataan yang diakhiri dengan panggilan ke TABatau SPC. (QB64 tidak.)
  • 0dapat dihilangkan sebelum atau setelah titik desimal ( .1atau 1.), tetapi tidak keduanya ( .).
  • ENDIFsetara dengan END IF.
  • Kutipan ganda penutupan suatu string dapat dihilangkan pada akhir baris.
DLosc
sumber
endifsebenarnya berfungsi di QB64, lihat jawaban ini
wastl
@Wastl Begitu. Ketika saya pertama kali mengujinya di QB64, saya menggunakan versi yang lebih lama di mana itu adalah kesalahan sintaksis. Terima kasih telah menyebutkan!
DLosc
4

Kombinasikan NextPernyataan

Next:Next:Next

Dapat dipadatkan menjadi

Next k,j,i

dimana iterator untuk Forloop i, jdan k- dalam urutan itu.

Misalnya di bawah ini (69 Bytes)

Input n,m,o
For i=0To n
For j=0To m
For k=0To o
?i;j;k
Next
Next
Next

Dapat dipadatkan hingga 65 Bytes

Input n,m,o
For i=0To n
For j=0To m
For k=0To o
?i;j;k
Next k,j,i

Dan sejauh bagaimana hal ini memengaruhi pemformatan dan lekukan, saya pikir pendekatan terbaik untuk menangani ini dibiarkan menyelaraskan pernyataan berikutnya dengan yang paling luar untuk pernyataan. Misalnya.

Input n,m,o
For i=0To n
    For j=0To m
        For k=0To o
            ?i;j;k
Next k,j,i
Taylor Scott
sumber
4

Ketahui metode input Anda

QBasic memiliki beberapa cara untuk mendapatkan input keyboard pengguna: INPUT, LINE INPUT, INPUT$, dan INKEY$.

  • INPUTadalah pernyataan input multiguna standar Anda. Program menghentikan apa yang dilakukannya, menampilkan kursor, dan memungkinkan pengguna mengetik beberapa input, diakhiri oleh Enter. INPUTdapat membaca angka atau string, dan dapat membaca beberapa nilai yang dipisahkan koma. Anda dapat menentukan string sebagai prompt, Anda bisa pergi dengan prompt tanda-tanya default, dan Anda bahkan dapat (saya baru saja belajar ini malam ini) menekan prompt sama sekali. Beberapa contoh doa:
    • INPUT x$,y
      Menggunakan ? prompt default dan membaca string dan angka, dipisahkan koma.
    • INPUT"Name";n$
      Meminta Name? dan membaca string.
    • INPUT"x=",x
      Meminta dengan x=(tidak ada tanda tanya! Catat koma di sintaks) dan membaca nomor.
    • INPUT;"",s$
      Menekan prompt (menggunakan sintaks koma di atas dengan string prompt kosong), membaca string, dan tidak pindah ke baris berikutnya ketika pengguna menekan masuk (itulah yang titik koma setelahnya INPUTtidak). Misalnya, jika Anda PRINT s$segera setelah ini, layar Anda akan terlihat seperti User_inputUser_input.
  • Salah satu kelemahannya INPUTadalah bahwa Anda tidak dapat membaca string dengan koma di dalamnya, karena INPUTmenggunakan koma sebagai pemisah bidang. Untuk membaca satu baris karakter arbitrer (dapat dicetak ASCII), gunakan LINE INPUT. Itu punya opsi sintaks yang sama seperti INPUT, kecuali dibutuhkan tepat satu variabel yang harus menjadi variabel string. Perbedaan lainnya adalah LINE INPUTtidak menampilkan prompt secara default; jika Anda menginginkannya, Anda harus menentukannya secara eksplisit.
  • INPUT$(n)tidak menampilkan prompt atau kursor tetapi hanya menunggu sampai pengguna memasukkan nkarakter, dan kemudian mengembalikan string yang mengandung karakter tersebut. Tidak seperti INPUTatau LINE INPUT, pengguna tidak perlu menekan Entersetelah itu, dan pada kenyataannya Enterbisa menjadi salah satu karakter (itu akan memberi ASCII karakter 13, dikenal sebagai bahasa seperti C \r).

    Paling sering, ini berguna karena INPUT$(1), biasanya dalam satu lingkaran. INPUT$bagus dalam program interaktif di mana penekanan tombol tunggal melakukan sesuatu . Sayangnya, ini hanya berfungsi dengan kunci yang memiliki kode ASCII; ini termasuk hal-hal seperti Escdan Backspace, tetapi bukan tombol panah, Insertdan Delete, dan lainnya.

  • Di situlah INKEY$masuk. Ini serupa dengan INPUT$(1)di mana ia mengembalikan hasil penekanan tombol tunggal 1 , tetapi berbeda dalam hal itu:

    • INKEY$ tidak membutuhkan argumen.
    • Saat INPUT$(n)menghentikan eksekusi hingga pengguna memasukkan nkarakter, INKEY$tidak menghentikan eksekusi. Jika pengguna saat ini menekan tombol, INKEY$mengembalikan string yang mewakili kunci itu; jika tidak, ia kembali "". Ini berarti bahwa jika Anda ingin menggunakan INKEY$untuk mendapatkan penekanan tombol berikutnya, Anda harus membungkusnya dalam loop menunggu-sibuk : 2

      k$=""
      WHILE""=k$
      k$=INKEY$
      WEND
      
    • Keduanya INPUT$dan INKEY$mengembalikan karakter ASCII untuk kunci yang sesuai dengan karakter ASCII (termasuk karakter kontrol seperti escape, tab, dan backspace). Namun, INKEY$dapat juga menangani beberapa kunci yang tidak memiliki kode ASCII. Untuk ini (kata file bantuan), "INKEY $ mengembalikan string 2-byte yang terdiri dari karakter null (ASCII 0) dan kode pindai keyboard."

      Jelas seperti lumpur? Inilah beberapa contohnya. Jika Anda menggunakan INKEY$loop di atas untuk menangkap penekanan tombol tombol panah kiri, k$akan berisi string "␀K"(dengan Kkode pindaian yang mewakili 75). Untuk panah kanan, ini "␀M"(77). Halaman ke bawah adalah "␀Q"(81). F5 adalah "␀?"(63).

      Masih bening seperti lumpur? Ya. Itu bukan hal yang paling intuitif di dunia. File bantuan memiliki tabel kode pindaian, tetapi saya selalu hanya menulis program kecil untuk mencetak hasil INKEY$dan tekan banyak tombol untuk mencari tahu apa nilai yang benar. Setelah Anda tahu karakter mana yang sesuai dengan tombol mana, Anda dapat menggunakan RIGHT$(k$,1)dan LEN(k$)untuk membedakan antara semua kasus yang berbeda yang mungkin Anda temui.

    Intinya? INKEY$itu aneh, tapi itu satu-satunya cara untuk pergi jika program Anda membutuhkan input non-pemblokiran atau perlu menggunakan tombol panah .


1 Tidak termasuk Shift, Ctrl, Alt, PrntScr, Caps Lock, dan yang sejenis. Itu tidak masuk hitungan. : ^ P

2 The WHILE ... WENDidiom di sini adalah apa yang saya pelajari dalam buku-buku QBasic saya. Untuk bermain golf tujuan, bagaimanapun, sebuah GOTOlingkaran yang lebih pendek .

DLosc
sumber
3

LOKASI bisa sangat kuat

The LOCATEpernyataan memungkinkan Anda untuk menempatkan di mana saja kursor pada layar (dalam biasa 80x40 batas ruang karakter) dan mencetak sesuatu di lokasi itu. Jawaban untuk tantangan ini benar-benar menunjukkan hal ini (dan juga dikombinasikan dengan banyak tips lain dari topik ini).

Tantangannya meminta kami untuk menampilkan setiap karakter yang ditekan pengguna dalam kotak 16x6. Dengan LOCATEini hanyalah masalah div dan mod atas kode ASCII ( adalam kode ini):

LOCATE a\16-1,1+2*(a MOD 16)

Dan kemudian mencetak karakter:

?CHR$(a)
steenbergh
sumber
3

Di QBasic, sudah biasa menggunakan DIMpernyataan untuk membuat variabel, memberi mereka nama dan tipe. Namun, ini tidak wajib, QBasic juga bisa mendapatkan tipe dengan akhiran nama variabel. Karena Anda tidak dapat mendeklarasikan dan menginisialisasi variabel pada saat yang bersamaan, sering kali bijaksana untuk melewatkan DIMcodegolf. Dua cuplikan yang identik secara fungsional *:

DIM a AS STRING: a = "example"
a$ = "example"

* Perhatikan bahwa ini membuat dua nama variabel yang berbeda.

Kita dapat menentukan jenis variabel dengan menambahkan $ke akhir nama variabel untuk string, !untuk angka presisi tunggal dan %untuk ganda. Single diasumsikan ketika tidak ada tipe yang ditentukan.

a$ = "Definitely a string"
b! = "Error!"

Perhatikan bahwa ini juga berlaku untuk array. Biasanya, sebuah array didefinisikan sebagai:

DIM a(20) AS STRING

Tetapi array juga tidak perlu DIMmed:

a$(2) = "QBasic 4 FUN!"

a$sekarang merupakan array untuk string dengan 11 slot: dari indeks 0 hingga dan termasuk indeks 10. Ini dilakukan karena QBasic memiliki opsi yang memungkinkan pengindeksan berbasis-0 dan berbasis-1 untuk array. Jenis array default mendukung keduanya dengan cara ini.

Ingat array dua puluh slot yang kita DIMbuat di atas? Itu sebenarnya memiliki 21 slot, karena prinsip yang sama berlaku untuk array redup dan non-redup.

steenbergh
sumber
Saya tidak pernah menyadari ini berlaku untuk array juga. Menarik.
trichoplax
3

IFPernyataan pemendekan

IF pernyataan agak mahal, dan menurunkannya bisa menghemat banyak byte.

Pertimbangkan hal berikut (diadaptasi dari jawaban oleh Erik the Outgolfer):

IF RND<.5THEN
x=x-1
a(i)=1
ELSE
y=y-1
a(i)=0
ENDIF

Hal pertama yang dapat kita lakukan adalah menyimpan ENDIFdengan menggunakan pernyataan satu baris IF:

IF RND<.5THEN x=x-1:a(i)=1ELSE y=y-1:a(i)=0

Ini berfungsi selama Anda tidak mencoba untuk meletakkannya di baris yang sama dengan yang lainnya. Khususnya, jika Anda memiliki IFpernyataan bersarang , hanya yang paling dalam yang dapat dibuat satu baris.

Tetapi dalam hal ini, kita bisa menghilangkan IFmatematika sepenuhnya menggunakan. Pertimbangkan apa yang sebenarnya kita inginkan:

  • Jika RND<.5benar ( -1), kami ingin:
    • x berkurang 1
    • y untuk tetap sama
    • a(i) menjadi 1
  • Kalau tidak, jika RND<.5salah ( 0), kami ingin:
    • x untuk tetap sama
    • y berkurang 1
    • a(i) menjadi 0

Sekarang jika kita menyimpan hasil dari kondisional dalam variabel ( r=RND<.5), kita dapat menghitung nilai-nilai baru x, ydan a(i):

  • Ketika radalah -1, x=x-1; saat rini 0, x=x+0.
  • Ketika radalah -1, y=y+0; saat rini 0, y=y-1.
  • Ketika radalah -1, a(i)=1; saat rini 0, a(i)=0.

Jadi kode akhir kita terlihat seperti:

r=RND<.5
x=x+r
y=y-1-r
a(i)=-r

menghemat 20 byte (40%) kekalahan dari versi aslinya.


Pendekatan matematika sering dapat diterapkan secara mengejutkan, tetapi ketika ada perbedaan dalam logika antara kedua kasus (misalnya ketika Anda perlu memasukkan sesuatu dalam satu kasus tetapi tidak dalam yang lain), Anda masih harus menggunakan IF.

DLosc
sumber
3

Terkadang, Anda harus menghindari array

Array dalam QBasic, ketika dipakai tanpa DIMhanya memiliki 11 slot. Jika tantangan membutuhkan lebih dari 11 slot (atau N slot, di mana N bisa lebih besar dari 11), Anda harus DIMmenggunakan array. Juga, mari kita asumsikan kita ingin mengisi array ini dengan data:

DIM a$(12)
a$(0) = "Value 1"
a$(1) = "Value 2"
...

Bahkan bermain golf, ini bisa memakan banyak ruang. Pada kesempatan seperti itu, mungkin lebih murah dalam byte untuk melakukan ini:

a$ = "value 1value 2"

Di sini, kami menempatkan semuanya dalam 1 string gabungan. Kemudian, kami mengaksesnya seperti ini:

?MID$(a$,i*7,7)

Untuk pendekatan ini, penting bahwa semua nilai memiliki panjang yang sama. Ambil nilai terpanjang dan padukan yang lainnya:

a$="one  two  threefour "

Anda tidak perlu menambahkan nilai terakhir, dan Anda bahkan dapat melewati kuotasi penutupan! Jika tantangan menentukan bahwa spasi putih tidak diizinkan dalam jawaban, gunakan RTRIM$()untuk memperbaikinya.

Anda dapat melihat teknik ini beraksi di sini .

steenbergh
sumber
3

PRINT( ?) memiliki beberapa kebiasaan

Angka dicetak dengan ruang terdepan dan belakang.

Mencetak menambah linebreak. Perilaku ini dapat diubah dengan menambahkan koma di akhir pernyataan untuk memasukkan tab, atau titik koma untuk menghindari penyisipan:

Tidak perlu menggunakan &atau di ;antara operasi yang berbeda saat mencetak, mis. ?1"x"s$harus mencetak nomor 1, dengan spasi di setiap sisi, surat xdan isis$

?"foo"
?"bar"
?10
?"foo",
?"bar"
?"foo"; 
?"bar"
?1CHR$(65)
?1" "CHR$(65)
?"A","B

Keluaran

foo
bar
 10
foo           bar
foobar
 1 A
 1  A
A             B

Mencetak linebreak dapat dilakukan dengan adil ?

steenbergh
sumber
Khususnya pada nomor pencetakan: spasi dicetak sebelum nomor jika tidak negatif; jika tidak, tanda minus -dicetak di sana. Spasi juga dicetak setelah nomor tersebut. Cara terbaik yang saya temukan untuk menghilangkan spasi ini adalah - tidak PRINT USINGtahu apakah Anda ingin menambahkannya ke jawaban ini atau jika itu harus menjadi jawaban yang terpisah.
DLosc
2

WRITE semoga bermanfaat di tempat PRINT

PRINTbiasanya cara Anda ingin melakukan output, karena cukup fleksibel dan memiliki jalan ?pintas. Namun, WRITEperintah itu dapat menyelamatkan Anda byte dalam situasi tertentu:

  • Saat mengeluarkan string, WRITEbungkus dengan tanda kutip ganda ( "). Jika Anda membutuhkan output dengan tanda kutip ganda, WRITE s$jauh lebih pendek daripada ?CHR$(34);s$;CHR$(34). Lihat, misalnya, QBasic quine terpendek yang diketahui .
  • Saat mengeluarkan nomor, WRITEjangan tambahkan spasi sebelum dan sesudahnya , jangan PRINT. WRITE njauh lebih pendek dari ?MID$(STR$(n),2). Lihat, misalnya, FizzBuzz di QB64 .
  • Saat mengeluarkan beberapa nilai, WRITEpisahkan dengan koma: WRITE 123,"abc"keluaran 123,"abc". Saya tidak bisa memikirkan skenario di mana ini akan berguna, tetapi itu tidak berarti tidak ada.

Keterbatasan WRITE:

  • Tidak ada cara untuk menghasilkan beberapa nilai tanpa pemisah seperti dengan PRINT a;b.
  • Tidak ada cara untuk menekan baris baru di akhir output. (Anda mungkin bisa menyelesaikannya dengan ini LOCATE, tetapi itu membutuhkan banyak byte.)
DLosc
sumber
1

Terkadang, QBasic mangles masuk ke fungsi. Abuse itu!

Ada beberapa fungsi yang berfungsi pada karakter alih-alih string, tetapi tidak ada chartipe data di QBasic, hanya ada string ($)tipe. Ambil contoh ASC()fungsi, yang mengembalikan kode kunci ASCII untuk sebuah karakter. Jika kita mau masuk

PRINT ASC("lala")

hanya yang pertama yang lakan dipertimbangkan oleh QBasic. Dengan cara ini, kita tidak perlu repot memotong talinya menjadi panjang 1.

Contoh lain datang dari pertanyaan ini di mana STRING$()fungsi tersebut digunakan dalam salah satu jawaban.

Fungsi STRING $ mengambil dua argumen, angka n dan string $, dan membangun string yang terdiri dari n salinan karakter pertama dari $

@Dosc, di sini

Perhatikan bahwa QBasic, ketika ditawari string multi-char dan hanya membutuhkan satu char, secara otomatis mengambil char pertama dan mengabaikan sisanya.

steenbergh
sumber