QBasic打高尔夫球的技巧


13

您在QBasic中打高尔夫球有哪些一般提示?我正在寻找可以应用于编码高尔夫球问题的想法,这些想法至少在某些方面是QBasic特有的(例如,“删除评论”不是答案)。

还欢迎提供有关QB64仿真器的提示。它具有Microsoft QBasic所没有的一些额外功能。


我很好奇你的动机。自从我10年级编程课程开始,我就没有使用过QBASIC。如何将我直接保存到1.44软盘而不用任何形式的版本控制,以及(通常)避免了灾难性的故障,真是令人惊讶。
安德鲁·布雷扎(AndrewBrēza)'17

5
@AndrewBrēza的动机?和我用任何语言打高尔夫球的动机一样:好玩!我喜欢用QBasic编写小型程序(尽管我不想在严肃的事情上使用它)。还有一个额外的好处,就是它内置了声音和图形(文本和像素),而我偏爱的“真实”语言Python却没有。
DLosc

用QBasic编写图形游戏要比用Python容易得多。
阿努什

如果有人想直接在浏览器中尝试图形化QBasic应用程序,则可以使用以下代码:github.com/nfriend/origins-host
mbomb007 '18

Answers:


10

了解您的循环结构

QBasic中有几个循环结构:FOR ... NEXTWHILE ... WEND,和DO ... LOOP。您也可以使用GOTO或(在某些情况下)RUN循环播放。

  • FOR ... NEXT非常擅长 与Python不同的是,它甚至总是比等效WHILEGOTO循环短,即使它变得更奇特:

    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
    

    请注意,您无需在后面重复变量名NEXT,并且可以消除数字和大多数后续关键字之间的空格。

  • WHILE ... WEND当您有一个可能需要执行0次的循环时非常有用。但是,如果您知道循环将至少执行一次,则GOTO可能会缩短一个字节:

    WHILE n>1:n=n\2:WEND
    1n=n\2:IF n>1GOTO 1
    
  • 我仅DO ... LOOP用于无限循环(除非RUN可以改为使用)。尽管它的字符数与无条件字符数相同GOTO,但阅读起来却更加直观。(请注意,“无限循环”可以包括环,你打破使用出来GOTO。)的DO WHILE/ DO UNTIL/ LOOP WHILE/ LOOP UNTIL语法过于冗长; 最好WHILE还是使用或GOTO适当使用。
  • GOTO如上所述,它是编写do / while循环的最短通用方法。使用一位数字的行号代替标签。请注意,当a GOTO是语句THEN部分中唯一的内容时IF,可以使用两种同样简洁的快捷方式语法:

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

    GOTO也可以用于创建更复杂的控制流。反对者称其为“意大利面条式代码”,但这就是代码式的高尔夫:不可读取性几乎是一种优点!GOTO自豪!

  • RUN当您需要跳转到程序中的固定位置并且不需要保留任何变量的值时,此功能很有用。RUN本身将从顶部重新启动程序;标签或行号,它将在该行重新启动。我主要使用它来创建无状态无限循环

5

PRINT和使用快捷方式REM

您可以使用?代替PRINT'来代替REM(注释)。

'与支持'作为char或string语法一部分的语言进行多语化时,也可能会很有用。


5

可分性测试

在要求您测试一个整数是否可被另一个整数整除的程序中,显而易见的方法是使用MOD

x MOD 3=0

但是更简单的方法是使用整数除法:

x\3=x/3

也就是说,xint-div 3等于xfloat-div 3

请注意,这两种方法都会0以错误和-1真实返回,因此您可能需要取反或减去结果而不是相加。


如果您需要相反的条件(即x不是整除3),明显的方法是使用不-等于操作:

x\3<>x/3

但是如果x保证是非负数,我们可以保存一个字节。整数除法会截断结果,因此它将始终小于或等于浮点除法。因此,我们可以将条件写为:

x\3<x/3

同样,如果x保证为负,则截断会增加结果,我们可以编写x\3>x/3。如果您不知道的迹象x,您必须坚持<>


5

扫描仪滥用

与许多语言一样,知道哪些字符可以删除也不能删除是很重要的。

  • 可以删除符号旁边的任何空格: IF""=a$THEN?0
  • 空间通常可以一个数字和发生的信之间被移除的顺序FOR i=1TO 10STEP 2。QBasic 1.1(可从archive.org获得)和QB64之间有一些区别:
    • QBasic 1.1允许删除任何数字和下一个字母之间的空格。此外,在print语句中,它将推断连续值之间的分号:?123x变为PRINT 123; x。上面的例外是像1e2和一样的序列1d+3,它们被当作科学记数法并扩展为100!1000#(分别为单精度和双精度)。
    • QB64通常是相同的,但数字不能跟着def水平,除非他们是一个结构良好的科学记数法文本的一部分。(例如,您不能像1 FOR9 ENDQBasic中那样在或中省略行号之后的空格。)如果表达式之一是字符串,则只能在print语句中推断分号:?123"abc"Works,而不能是?TAB(5)123or ?123x
  • 说到分号,QBasic 1.1将尾随分号添加到PRINTTAB或结束的语句中SPC。(QB64没有。)
  • 0可以在小数点(.11.)之前或之后省略,但不能全部省略(.)。
  • ENDIF等同于END IF
  • 字符串的结尾双引号可以在行尾省略。

endif实际在QB64中有效,请参见此答案
wastl

@wastl确实如此。当我在QB64中首次对其进行测试时,我使用的是语法错误的旧版本。感谢您的提及!
DLosc

4

合并Next报表

Next:Next:Next

可能会浓缩为

Next k,j,i

其中对于迭代器For环路ij以及k-按照这个顺序。

例如以下(69字节)

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

可以压缩到65字节

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

至于这如何影响格式和缩进,我认为处理此问题的最佳方法是将下一条语句与最外层的for语句对齐。例如。

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

4

了解您的输入法

QBasic中有几种方法来获取用户键盘输入:INPUTLINE INPUTINPUT$,和INKEY$

  • INPUT是您的标准多用途输入语句。该程序将停止正在执行的操作,显示一个光标,并让用户键入一些输入,以终止EnterINPUT可以读取数字或字符串,并且可以读取多个以逗号分隔的值。您可以指定一个字符串作为提示,可以使用默认的问号提示,甚至可以(我今晚才刚刚学到)完全取消该提示。一些示例调用:
    • INPUT x$,y
      使用默认? 提示,并读取以逗号分隔的字符串和数字。
    • INPUT"Name";n$
      提示Name? 并读取字符串。
    • INPUT"x=",x
      提示x=(无问号!注意语法中的逗号)并读取数字。
    • INPUT;"",s$
      禁止提示(使用上面的逗号语法和空的提示字符串),读取字符串,并且在用户按下Enter键时不移至下一行(这是分号之后的INPUT操作)。例如,如果PRINT s$紧接在此之后,屏幕将显示为User_inputUser_input
  • 的一个缺点INPUT是您无法读取其中包含逗号的字符串,因为它INPUT使用逗号作为字段分隔符。要读取一行任意(可打印ASCII)字符,请使用LINE INPUT。它具有与相同的语法选项INPUT,只是它只需要一个变量,该变量必须是字符串变量。另一个区别是LINE INPUT默认情况下不显示提示。如果需要,则必须明确指定。
  • INPUT$(n)不显示任何提示或光标,而只是等待用户输入n字符,然后返回包含这些字符的字符串。与INPUT或不同LINE INPUT,用户不需要Enter事后按下,而实际上Enter可以是以下字符之一(它将给出ASCII字符13,类似于C的语言称为\r)。

    通常,这INPUT$(1)通常在循环中用作。在单个按键可以做事的交互式程序中INPUT$很好。不幸的是,它仅适用于具有ASCII码的键。其中包括诸如和,但不包括箭头键和以及其他。EscBackspaceInsertDelete

  • 这是INKEY$从哪里来的。类似于INPUT$(1)它返回单个按键1的结果,但不同之处在于:

    • INKEY$ 不用争论。
    • INPUT$(n)用户停止输入n字符之前停止执行时,INKEY$不会停止执行。如果用户当前正在按某个键,则INKEY$返回表示该键的字符串;否则,返回一个字符串。如果不是,则返回""。这意味着,如果您想用来INKEY$获取下一个按键,则必须将其包装在一个繁忙的等待循环中:2

      k$=""
      WHILE""=k$
      k$=INKEY$
      WEND
      
    • 两者INPUT$和都INKEY$返回与ASCII字符(包括转义符,制表符和退格键等控制字符)相对应的键的ASCII字符。但是,INKEY$也可以处理一些没有ASCII码的键。对于这些文件(即帮助文件),“ INKEY $返回一个2字节的字符串,该字符串由空字符(ASCII 0)和键盘扫描代码组成。”

      像泥一样清澈?这是一些例子。如果使用INKEY$上面的循环捕获向左箭头键的按键,k$它将包含字符串"␀K"K代表扫描代码为75)。对于右箭头,为"␀M"(77)。向下翻页是"␀Q"(81)。F5为"␀?"(63)。

      还是像泥一样清澈?是的 这不是世界上最直观的事情。帮助文件中有一张扫描代码表,但是我总是只写一个小程序来打印结果,INKEY$然后按一堆键找出正确的值。一旦知道哪些字符对应于哪些键,就可以使用RIGHT$(k$,1)LEN(k$)区分可能遇到的所有不同情况。

    底线?INKEY$很奇怪,但这是您的程序需要非阻塞输入或需要使用箭头键的唯一方法。


1不包括ShiftCtrlAltPrntScrCaps Lock,和类似的。那些不算在内。:^ P

2WHILE ... WEND是我从QBasic书籍中学到的成语。打高尔夫球的目的,但是,一个GOTO循环时间较短


3

LOCATE可能真的很强大

LOCATE语句允许您将光标放置在屏幕上的任何位置(通常在80x40字符空间限制内)并在该位置打印一些内容。这个挑战的答案确实显示了这一点(并且还结合了该主题的许多其他技巧)。

挑战要求我们输出用户在16x6网格中按下的每个字符。有了LOCATE这简直是DIV和MOD在ASCII码的问题(a在此代码):

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

然后打印字符:

?CHR$(a)

3

在QBasic中,习惯上使用该DIM语句创建变量,并为其指定名称和类型。但是,这不是强制性的,QBasic也可以通过变量名称的后缀来派生类型。由于您不能同时声明和初始化变量,因此跳过DIM代码高尔夫通常是明智的。功能上相同的两个摘要*:

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

*请注意,这确实会创建两个不同的变量名称。

我们可以通过$在字符串,!单精度数字和%双精度数的变量名的末尾添加变量来指定变量的类型。如果未指定类型,则假定为单打。

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

请注意,这也适用于数组。通常,数组定义为:

DIM a(20) AS STRING

但是数组也不需要DIM医学:

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

a$现在是一个包含11个插槽的字符串数组:从索引0到索引10,包括索引10。之所以这样做,是因为QBasic具有允许对数组进行基于0和基于1索引的选项。默认数组类型同时支持这两种方式。

还记得我们DIM上面插入的二十插槽阵列吗?实际上有21个插槽,因为相同的原理适用于变暗和非变暗阵列。


我从未意识到这也适用于数组。有趣。
trichoplax

3

缩短IF语句

IF 语句是相当昂贵的,打下来可以节省很多字节。

考虑以下(改编自一个答案由埃里克Outgolfer):

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

我们可以做的第一件事是ENDIF使用单行IF语句保存:

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

只要您不尝试将其与其他任何内容放在同一行,此方法就起作用。特别是,如果您有嵌套的IF语句,则仅最里面的语句可以是单行的。

但是在这种情况下,我们可以IF完全不用数学。考虑一下我们实际想要的:

  • 如果RND<.5为true(-1),我们希望:
    • x 减少1
    • y 保持不变
    • a(i) 成为1
  • 否则,如果RND<.5为false(0),则我们希望:
    • x 保持不变
    • y 减少1
    • a(i) 变成0

现在,如果我们保存条件的结果的变量(r=RND<.5),我们可以计算出新的价值xy以及a(i)

  • 如果r-1x=x-1; 当r0x=x+0
  • 如果r-1y=y+0; 当r0y=y-1
  • 如果r-1a(i)=1; 当r0a(i)=0

因此,我们的最终代码如下所示:

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

与原始版本相比节省了多达20个字节(40%)。


数学方法经常可以令人惊讶地应用,但是当两种情况之间的逻辑存在差异时(例如,当您需要在一种情况下输入某些内容而不在另一种情况下输入某些内容时),您仍然需要使用IF


3

有时,您应该避免使用数组

在没有实例化的情况下,QBasic中的数组DIM只有11个插槽。如果挑战要求的插槽超过11个(或N个插槽,其中N可以大于11),则应DIM使用阵列。另外,假设我们要用数据填充此数组:

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

即使打高尔夫球,也可能占用很多空间。在这种情况下,这样做可能会更便宜(以字节为单位):

a$ = "value 1value 2"

在这里,我们将所有内容放在1个串联字符串中。以后,我们像这样访问它:

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

对于这种方法,重要的是所有值的长度相等。取最长的值并填充所有其他值:

a$="one  two  threefour "

您无需填充最后一个值,甚至可以跳过右引号!如果挑战指定答案中不允许使用空格,请使用RTRIM$()来解决该问题。

您可以在此处看到此技术的实际应用。


3

PRINT?)有一些怪癖

数字印有前导和尾随空格。

打印会增加换行符。可以通过在语句末尾添加逗号来代替制表符或使用分号来避免任何插入来更改此行为:

打印时,例如,不必在不同的操作之间使用&;在不同的操作之间使用。?1"x"s$应打印数字1,每边各有一个空格,字母x和内容s$

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

产出

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

只需使用以下命令即可打印换行符 ?


特别是在打印数字时:如果数字是非负数,则在数字之前打印空格;否则,将-在此处打印减号。在数字之后还会打印一个空格。我发现摆脱这些空间的最好方法是PRINT USING--dunno,如果您想将其添加到此答案中,或者它应该是一个单独的答案。
DLosc

2

WRITE 可能代替 PRINT

PRINT通常是您要执行输出的方式,因为它非常灵活并且具有?快捷方式。但是,该WRITE命令可以在特定情况下为您节省字节:

  • 输出字符串时,WRITE将其用双引号(")引起来。如果您需要使用双引号将其输出,WRITE s$则比短得多?CHR$(34);s$;CHR$(34)。参见,例如,已知最短的QBasic quine
  • 输出数字时,WRITE请勿像在数字之前或之后添加空格PRINTWRITE n比短得多?MID$(STR$(n),2)。参见例如QB64中的FizzBu​​zz
  • 输出多个值时,请WRITE用逗号隔开:WRITE 123,"abc"输出123,"abc"。我想不出一种有用的方案,但这并不意味着就没有这种方案。

的局限性WRITE

  • 没有像这样的分隔符,就无法输出多个值PRINT a;b
  • 没有办法在输出末尾取消换行符。(您可以使用来解决此问题LOCATE,但这会占用很多字节。)

1

有时,QBasic会操纵函数的输入。虐待!

有几个功能可以处理字符而不是字符串,但是charQBasic中没有数据类型,只有string ($)类型。以ASC()函数为例,该函数返回字符的ASCII密钥代码。如果我们进入

PRINT ASC("lala")

lQBasic 仅考虑第一个。这样,我们不必费心将字符串切成长度为1的字符串。

另一个例子来自这个问题,其中STRING$()一个答案中使用了函数。

STRING $函数采用两个参数,即数字n和字符串s $,并构造一个由s $第一个字符的n个副本组成的字符串

@DLosc,在这里

请注意,当QBasic提供一个多字符字符串并且只需要一个字符时,它会自动获取第一个字符而忽略其余字符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.