¿Qué consejos generales tienes para jugar al golf en QBasic? Estoy buscando ideas que puedan aplicarse a los problemas de código de golf en general que sean al menos algo específicos de QBasic (por ejemplo, "eliminar comentarios" no es una respuesta).
Consejos sobre el emulador QB64 también son bienvenidos. Tiene algunas características adicionales que no están en Microsoft QBasic.
Respuestas:
Conozca sus construcciones en bucle
QBasic tiene varias construcciones de bucle:
FOR ... NEXT
,WHILE ... WEND
, yDO ... LOOP
. También puede usarGOTO
o (en algunas situaciones)RUN
para hacer un bucle.FOR ... NEXT
es bastante bueno en lo que hace. A diferencia de Python, casi siempre es más corto que el equivalenteWHILE
o elGOTO
bucle, incluso cuando se vuelve un poco más elegante:Tenga en cuenta que no necesita repetir el nombre de la variable después
NEXT
, y puede eliminar el espacio entre los números y la mayoría de las siguientes palabras clave.WHILE ... WEND
es bueno para cuando tienes un ciclo que podría necesitar ejecutarse 0 veces. Pero si sabe que el bucle se ejecutará al menos una vez,GOTO
podría ser un byte más corto:DO ... LOOP
para bucles infinitos (excepto dondeRUN
se puede usar en su lugar). Si bien cuesta la misma cantidad de caracteres que un incondicionalGOTO
, es un poco más intuitivo de leer. (Tenga en cuenta que "bucle infinito" puede incluir bucles de los que se salga usando aGOTO
.) La sintaxisDO WHILE
/DO UNTIL
/LOOP WHILE
/LOOP UNTIL
es demasiado detallada; es mejor usarloWHILE
oGOTO
según corresponda.GOTO
es, como se mencionó anteriormente, la forma general más corta de escribir un bucle do / while. Use números de línea de un solo dígito en lugar de etiquetas. Tenga en cuenta que cuando aGOTO
es lo único en laTHEN
parte de unaIF
declaración, hay dos sintaxis de acceso directo igualmente concisas disponibles:GOTO
También se puede utilizar para crear flujos de control más complicados . Los detractores se refieren a esto como "código de espagueti", pero esto es golf de código: ¡la imposibilidad de leer es casi una virtud!GOTO
¡orgullo!RUN
es útil cuando necesita saltar a un lugar fijo en el programa y no necesita mantener ninguno de los valores de las variables.RUN
por sí solo reiniciará el programa desde arriba; con una etiqueta o número de línea, se reiniciará en esa línea. Lo he usado principalmente para crear bucles infinitos sin estado .fuente
Use atajos para
PRINT
yREM
Puede usar en
?
lugar dePRINT
, y en'
lugar deREM
(comentario).'
También puede ser útil cuando se poliglota con lenguajes que son compatibles'
como parte de la sintaxis char o string.fuente
Prueba de divisibilidad
En los programas que requieren que pruebe si un entero es divisible por otro, la forma obvia es usar
MOD
:Pero una forma más corta es usar la división de enteros:
Es decir,
x
int-div3
es igual ax
float-div3
.Tenga en cuenta que ambos enfoques volverán
0
para falsey y-1
para verdadero, por lo que es posible que deba negar el resultado o restarlo en lugar de sumar.Si necesita la condición opuesta (
x
es decir, no es divisible por3
), el enfoque obvio es utilizar el operador no igual:Pero si
x
se garantiza que no es negativo, podemos guardar un byte. La división entera trunca el resultado, por lo que siempre será menor o igual que la división flotante. Por lo tanto, podemos escribir la condición como:Del mismo modo, si
x
se garantiza que es negativo, el truncamiento aumenta el resultado y podemos escribirx\3>x/3
. Si no conoce el signo dex
, deberá atenerse<>
.fuente
Abuso del escáner
Como en muchos idiomas, es importante saber qué caracteres pueden y no pueden eliminarse.
IF""=a$THEN?0
FOR i=1TO 10STEP 2
. Existen algunas diferencias entre QBasic 1.1 (disponible en archive.org ) y QB64 :?123x
conviertePRINT 123; x
. Las excepciones a lo anterior son secuencias como1e2
y1d+3
, que se tratan como notación científica y se expanden a100!
y1000#
(precisión simple y doble, respectivamente).d
,e
of
en absoluto a menos que sean parte de un literal notación científica bien formada. (Por ejemplo, no puede omitir el espacio después del número de línea1 FOR
o9 END
, como puede hacerlo en QBasic propiamente dicho). Solo infiere punto y coma en las declaraciones de impresión si una de las expresiones es una cadena:?123"abc"
funciona, pero no?TAB(5)123
o?123x
.PRINT
declaración que termina con una llamada aTAB
oSPC
. (QB64 no lo hace)0
puede omitirse antes o después del punto decimal (.1
o1.
), pero no ambos (.
).ENDIF
es equivalente aEND IF
.fuente
endif
en realidad funciona en QB64, mira esta respuestaCombinar
Next
declaracionesPuede condensarse hasta
donde los iteradores para los
For
bucles soni
,j
yk
- en ese orden.Por ejemplo, el siguiente (69 Bytes)
Puede condensarse hasta 65 bytes.
Y en cuanto a cómo esto afecta el formato y la sangría, creo que el mejor enfoque para manejar esto es alinear la siguiente declaración con la declaración más externa. P.ej.
fuente
Conoce tus métodos de entrada
QBasic tiene varias maneras de conseguir la entrada de teclado del usuario:
INPUT
,LINE INPUT
,INPUT$
, yINKEY$
.INPUT
es su enunciado de entrada multipropósito estándar. El programa detiene lo que está haciendo, muestra un cursor y permite al usuario escribir alguna entrada, terminada por Enter.INPUT
puede leer números o cadenas, y puede leer múltiples valores separados por comas. Puede especificar una cadena como mensaje, puede ir con el mensaje predeterminado de signo de interrogación, e incluso puede (acabo de enterarme esta noche) suprimir el mensaje por completo. Algunas invocaciones de muestra:INPUT x$,y
Utiliza el
?
indicador predeterminado y lee una cadena y un número, separados por comas.INPUT"Name";n$
Solicita
Name?
y lee una cadena.INPUT"x=",x
Aparece con
x=
(sin signo de interrogación; observe la coma en la sintaxis) y lee un número.INPUT;"",s$
Suprime la solicitud (usando la sintaxis de coma anterior con una cadena de solicitud vacía), lee una cadena y no se mueve a la siguiente línea cuando el usuario presiona enter (eso es lo que hace el punto y coma después
INPUT
). Por ejemplo, siPRINT s$
inmediatamente después de esto, se verá su pantallaUser_inputUser_input
.INPUT
es que no puede leer una cadena con una coma, ya queINPUT
usa la coma como separador de campo. Para leer una sola línea de caracteres arbitrarios (ASCII imprimible), useLINE INPUT
. Tiene las mismas opciones de sintaxis queINPUT
, excepto que toma exactamente una variable que debe ser una variable de cadena. La otra diferencia es queLINE INPUT
no muestra una solicitud por defecto; si quieres uno, tendrás que especificarlo explícitamente.INPUT$(n)
no muestra ningún indicador o cursor, sino que simplemente espera hasta que el usuario ingresen
caracteres y luego devuelve una cadena que contiene esos caracteres. A diferencia deINPUT
oLINE INPUT
, el usuario no necesita presionar Enterdespués, y de hecho Enterpuede ser uno de los caracteres (le dará el carácter ASCII 13, conocido como lenguajes tipo C\r
).Muy a menudo, esto es útil como
INPUT$(1)
, típicamente en un bucle.INPUT$
es bueno en programas interactivos donde las pulsaciones de teclas individuales hacen cosas . Desafortunadamente, solo funciona con teclas que tienen códigos ASCII; esto incluye cosas como Escy Backspace, pero no las teclas de flecha, Inserty Delete, y otros.Que es donde
INKEY$
entra. Es similar aINPUT$(1)
que devuelve los resultados de una sola pulsación de tecla 1 , pero diferente en eso:INKEY$
No tiene argumento.Mientras
INPUT$(n)
detiene la ejecución hasta que el usuario ingresen
caracteres,INKEY$
no detiene la ejecución. Si el usuario está presionando una tecla,INKEY$
devuelve una cadena que representa esa tecla; si no, vuelve""
. Esto significa que si desea utilizarINKEY$
para obtener la siguiente pulsación de tecla, debe envolverlo en un bucle de espera ocupada : 2Ambos caracteres de retorno
INPUT$
yINKEY$
ASCII para claves que corresponden a caracteres ASCII (incluidos los caracteres de control como escape, tabulación y retroceso). Sin embargo,INKEY$
también puede manejar algunas claves que no tienen códigos ASCII. Para estos (dice el archivo de ayuda), "INKEY $ devuelve una cadena de 2 bytes compuesta por el carácter nulo (ASCII 0) y el código de escaneo del teclado".¿Claro como el barro? Aquí hay algunos ejemplos. Si utiliza el
INKEY$
bucle anterior para capturar una pulsación de tecla de la tecla de flecha izquierda,k$
contendrá la cadena"␀K"
(con elK
código de exploración que representa 75). Para la flecha derecha, es"␀M"
(77). Página abajo es"␀Q"
(81). F5 es"␀?"
(63).Todavía claro como el barro? Si. No es la cosa más intuitiva del mundo. El archivo de ayuda tiene una tabla de códigos de escaneo, pero siempre escribo un pequeño programa para imprimir los resultados
INKEY$
y presiono un montón de teclas para averiguar cuáles son los valores correctos. Una vez que sepa qué caracteres corresponden a qué teclas, puede usarRIGHT$(k$,1)
yLEN(k$)
distinguir entre todos los diferentes casos que pueda encontrar.¿Línea de fondo?
INKEY$
es extraño, pero es el único camino a seguir si su programa requiere una entrada sin bloqueo o necesita usar las teclas de flecha .1 No incluye Shift, Ctrl, Alt, PrntScr, Caps Lock, y similar. Esos no cuentan. : ^ P
2 El
WHILE ... WEND
modismo aquí es lo que aprendí en mis libros de QBasic. Para fines de golf, sin embargo, unGOTO
circuito es más corto .fuente
LOCATE puede ser realmente poderoso
La
LOCATE
declaración le permite colocar el cursor en cualquier lugar de la pantalla (dentro de los límites habituales de espacio de 80x40 caracteres) e imprimir algo en esa ubicación. Esta respuesta a un desafío realmente muestra esto (y también se combina con muchos otros consejos de este tema).El desafío nos pide que saquemos todos los caracteres que un usuario ha presionado en una cuadrícula de 16x6. Con
LOCATE
esto es simplemente una cuestión de div y mod sobre el código ASCII (a
en este código):Y luego imprimiendo el personaje:
fuente
En QBasic, es costumbre usar la
DIM
declaración para crear variables, dándoles un nombre y un tipo. Sin embargo, esto no es obligatorio, QBasic también puede derivar un tipo por el sufijo del nombre de la variable. Como no puede declarar e inicializar una variable al mismo tiempo, a menudo es aconsejable omitir elDIM
codegolf. Dos fragmentos que son funcionalmente idénticos *:* Tenga en cuenta que esto crea dos nombres de variables diferentes.
Podemos especificar el tipo de variable agregando
$
al final de un nombre de variable para cadenas,!
para números de precisión individuales y%
para dobles. Se suponen solteros cuando no se especifica ningún tipo.Tenga en cuenta que esto también es válido para las matrices. Por lo general, una matriz se define como:
Pero las matrices tampoco necesitan
DIM
meditar:a$
ahora es una matriz para cadenas con 11 ranuras: desde el índice 0 hasta el índice 10 incluido. Esto se hace porque QBasic tiene una opción que permite la indexación basada en 0 y en 1 para matrices. Un tipo de matriz predeterminado es compatible con ambos de esta manera.¿Recuerdas la matriz de veinte ranuras que
DIM
medimos arriba? Eso en realidad tiene 21 ranuras, porque el mismo principio se aplica tanto a las matrices atenuadas como a las no atenuadas.fuente
IF
Declaraciones de acortamientoIF
las declaraciones son bastante caras, y reducirlas puede ahorrar muchos bytes.Considere lo siguiente (adaptado de una respuesta de Erik the Outgolfer):
Lo primero que podemos hacer es guardar el
ENDIF
mediante el uso de unaIF
declaración de una sola línea :Esto funciona siempre que no intentes ponerlo en la misma línea que cualquier otra cosa. En particular, si tiene
IF
declaraciones anidadas , solo la más interna puede tener una línea.Pero en este caso, podemos eliminar
IF
completamente el uso de las matemáticas. Considere lo que realmente queremos:RND<.5
es verdadero (-1
), queremos:x
disminuir en 1y
permanecer iguala(i)
convertirse en 1RND<.5
es falso (0
), queremos:x
permanecer igualy
disminuir en 1a(i)
convertirse en 0Ahora si salvamos el resultado de la condicional en una variable (
r=RND<.5
), podemos calcular los nuevos valores dex
,y
ya(i)
:r
es-1
,x=x-1
; cuandor
es0
,x=x+0
.r
es-1
,y=y+0
; cuandor
es0
,y=y-1
.r
es-1
,a(i)=1
; cuandor
es0
,a(i)=0
.Entonces nuestro código final se ve así:
ahorrando la friolera de 20 bytes (40%) sobre la versión original.
El enfoque matemático se puede aplicar sorprendentemente a menudo, pero cuando hay una diferencia en la lógica entre los dos casos (por ejemplo, cuando necesita ingresar algo en un caso pero no en el otro), aún tendrá que usarlo
IF
.fuente
A veces, debes evitar las matrices
Las matrices en QBasic, cuando se instancian sin
DIM
tener solo 11 espacios. Si un desafío requiere más de 11 ranuras (o N ranuras, donde N puede ser mayor que 11), debe hacerDIM
la matriz. Además, supongamos que queremos llenar esta matriz con datos:Incluso jugando al golf, esto puede ocupar mucho espacio. En tales ocasiones, puede ser más barato en bytes hacer esto:
Aquí, colocamos todo en 1 cadena concatenada. Más tarde, accedemos así:
Para este enfoque, es importante que todos los valores tengan la misma longitud. Tome el valor más largo y rellene todos los demás:
No necesita rellenar el último valor, ¡e incluso puede omitir las comillas de cierre! Si el desafío especifica que el espacio en blanco no está permitido en la respuesta, utilícelo
RTRIM$()
para solucionarlo.Puedes ver esta técnica en acción aquí .
fuente
PRINT
(?
) tiene algunas peculiaridadesLos números se imprimen con un espacio inicial y final.
La impresión agrega un salto de línea. Este comportamiento puede modificarse agregando una coma al final de la instrucción para insertar una pestaña o un punto y coma para evitar cualquier inserción:
No es necesario usar
&
o;
entre operaciones distintas al imprimir, por ejemplo.?1"x"s$
imprimirá el número1
, con espacios a cada lado, la letrax
y el contenido des$
Salidas
La impresión de un salto de línea se puede hacer con solo
?
fuente
-
se imprime un signo menos allí. También se imprime un espacio después del número. La mejor manera que he descubierto para deshacerme de estos espacios es - noPRINT USING
sé si quieres agregar eso a esta respuesta o si debería ser una respuesta separada.WRITE
puede ser útil en lugar dePRINT
PRINT
generalmente es la forma en que querrá hacer la salida, ya que es bastante flexible y tiene el?
acceso directo. Sin embargo, elWRITE
comando puede guardarle bytes en situaciones específicas:WRITE
envuelve entre comillas dobles ("
). Si necesita resultados con comillas dobles,WRITE s$
es mucho más corto que?CHR$(34);s$;CHR$(34)
. Ver, por ejemplo, la quina QBasic más corta conocida .WRITE
no agrega espacios antes y después, como loPRINT
hace.WRITE n
es mucho más corto que?MID$(STR$(n),2)
. Ver, por ejemplo, FizzBuzz en QB64 .WRITE
sepárelos con comas:WRITE 123,"abc"
salidas123,"abc"
. No puedo pensar en un escenario en el que esto sea útil, pero eso no significa que no haya ninguno.Limitaciones de
WRITE
:PRINT a;b
.LOCATE
, pero eso cuesta muchos bytes).fuente
A veces, QBasic manipula las entradas a las funciones. Abusa de eso!
Hay un par de funciones que funcionan en caracteres en lugar de cadenas, pero no hay un
char
tipo de datos en QBasic, solo existe elstring ($)
tipo. Tomemos por ejemplo laASC()
función, que devuelve el código clave ASCII para un personaje. Si quisiéramos entrarsolo el primero
l
sería considerado por QBasic. De esta manera, no tenemos que molestarnos en cortar una cuerda hasta la longitud 1.Otro ejemplo proviene de esta pregunta donde la
STRING$()
función se usa en una de las respuestas.Tenga en cuenta que QBasic, cuando se le ofrece una cadena de caracteres múltiples y necesita solo un carácter, toma automáticamente el primer carácter e ignora el resto.
fuente