Que dicas gerais você tem para jogar golfe no QBasic? Estou procurando idéias que possam ser aplicadas aos problemas de código de golfe em geral que sejam pelo menos um pouco específicos para o QBasic (por exemplo, "remover comentários" não é uma resposta).
Dicas sobre o emulador QB64 também são bem-vindas. Possui alguns recursos extras que não estão no Microsoft QBasic.
Respostas:
Conheça suas construções em loop
QBasic tem várias construções de loop:
FOR ... NEXT
,WHILE ... WEND
, eDO ... LOOP
. Você também pode usarGOTO
ou (em algumas situações)RUN
fazer um loop.FOR ... NEXT
é muito bom no que faz. Ao contrário do Python, é quase sempre menor que o equivalenteWHILE
ouGOTO
loop, mesmo quando fica um pouco mais sofisticado:Observe que você não precisa repetir o nome da variável depois
NEXT
e pode eliminar o espaço entre os números e a maioria das palavras-chave a seguir.WHILE ... WEND
é bom para quando você tem um loop que pode precisar executar 0 vezes. Mas se você souber que o loop será executado pelo menos uma vez,GOTO
pode ser um byte mais curto:DO ... LOOP
para loops infinitos (exceto ondeRUN
pode ser usado). Embora custe o mesmo número de caracteres que um incondicionalGOTO
, é um pouco mais intuitivo de ler. (Note-se que "loop infinito" pode incluir loops que você sair de usar umGOTO
.) ADO WHILE
/DO UNTIL
/LOOP WHILE
/LOOP UNTIL
sintaxe é muito detalhado; é melhor usarWHILE
ouGOTO
conforme apropriado.GOTO
é, como mencionado acima, a maneira geral mais curta de escrever um loop do / while. Use números de linha de um dígito em vez de etiquetas. Observe que quando aGOTO
é a única coisa naTHEN
parte de umaIF
instrução, existem duas sintaxes de atalho igualmente concisas disponíveis:GOTO
também pode ser usado para criar fluxos de controle mais complicados . Os pessimistas se referem a isso como "código de espaguete", mas isso é código de golfe: a ilegibilidade é quase uma virtude!GOTO
orgulho!RUN
é útil quando você precisa pular para um local fixo no programa e não precisa manter nenhum dos valores das variáveis.RUN
por si só irá reiniciar o programa a partir do topo; com um rótulo ou número de linha, ele será reiniciado nessa linha. Eu o usei principalmente para criar loops infinitos sem estado .fonte
Use atalhos para
PRINT
eREM
Você pode usar em
?
vez dePRINT
e'
emREM
(comentário).'
também pode ser útil ao poliglota com idiomas que suportam'
como parte da sintaxe char ou string.fonte
Teste de divisibilidade
Em programas que exigem que você teste se um número inteiro é divisível por outro, a maneira óbvia é usar
MOD
:Mas uma maneira mais curta é usar a divisão inteira:
Ou seja,
x
int-div3
é igual ax
float-div3
.Observe que essas duas abordagens retornarão
0
para falsey e trash-1
, portanto, você pode precisar negar o resultado ou subtraí-lo em vez de adicioná-lo.Se você precisar da condição oposta (ou
x
seja, não é divisível por3
), a abordagem óbvia é usar o operador not-equal:Mas se
x
é garantido que não é negativo, podemos salvar um byte. A divisão inteira trunca o resultado, portanto sempre será menor ou igual à divisão flutuante. Portanto, podemos escrever a condição como:Da mesma forma, se
x
é garantido que é negativo, o truncamento aumenta o resultado e podemos escreverx\3>x/3
. Se você não souber o sinalx
, terá que seguir<>
.fonte
Abuso de scanner
Como em muitos idiomas, é importante saber quais caracteres podem ou não ser removidos.
IF""=a$THEN?0
FOR i=1TO 10STEP 2
. Existem algumas diferenças entre o QBasic 1.1 (disponível em archive.org ) e o QB64 :?123x
torna - sePRINT 123; x
. As exceções ao exposto acima são sequências como1e2
e1d+3
, que são tratadas como notação científica e expandidas para100!
e1000#
(precisão única e dupla, respectivamente).d
,e
ouf
a menos que eles fazem parte de um literal notação científica bem formado. (Por exemplo, você não pode omitir o espaço após o número da linha1 FOR
ou9 END
, como no QBasic propriamente.) Ele deduz o ponto-e-vírgula nas instruções de impressão se uma das expressões for uma string:?123"abc"
funciona, mas não?TAB(5)123
ou?123x
.PRINT
instrução que termina com uma chamada paraTAB
ouSPC
. (QB64 não.)0
pode ser omitido antes ou depois do ponto decimal (.1
ou1.
), mas não ambos (.
).ENDIF
é equivalente aEND IF
.fonte
endif
realmente funciona em Qb64, consulte esta respostaCombinar
Next
declaraçõesPode ser condensado até
onde os iteradores para os
For
loops sãoi
,j
ek
- nessa ordem.Por exemplo, o abaixo (69 bytes)
Pode ser condensado em até 65 bytes
E, na medida em que isso afeta a formatação e o recuo, acho que a melhor abordagem para lidar com isso é alinhar a próxima declaração pela mais externa. Por exemplo.
fonte
Conheça seus métodos de entrada
QBasic tem várias maneiras de obter a entrada do teclado do usuário:
INPUT
,LINE INPUT
,INPUT$
, eINKEY$
.INPUT
é a sua declaração de entrada multiuso padrão. O programa para o que está fazendo, exibe um cursor e permite que o usuário digite alguma entrada, terminada por Enter.INPUT
pode ler números ou seqüências de caracteres e pode ler vários valores separados por vírgula. Você pode especificar uma string como um prompt, pode usar o prompt de ponto de interrogação padrão e pode até (acabei de aprender isso hoje à noite) suprimir completamente o prompt. Algumas invocações de amostra:INPUT x$,y
Usa o
?
prompt padrão e lê uma string e um número, separados por vírgula.INPUT"Name";n$
Solicita
Name?
e lê uma string.INPUT"x=",x
Solicita com
x=
(sem ponto de interrogação! Observe a vírgula na sintaxe) e lê um número.INPUT;"",s$
Suprime o prompt (usando a sintaxe de vírgula acima com uma string de prompt vazia), lê uma string e não passa para a próxima linha quando o usuário pressiona enter (é o que o ponto-e-vírgula depois
INPUT
faz). Por exemplo, se vocêPRINT s$
imediatamente após isso, sua tela será semelhanteUser_inputUser_input
.INPUT
é que você não pode ler uma string com uma vírgula, pois elaINPUT
usa como separador de campos. Para ler uma única linha de caracteres arbitrários (imprimíveis em ASCII), useLINE INPUT
. Ele tem as mesmas opções de sintaxe queINPUT
, exceto que é preciso exatamente uma variável que deve ser uma variável de string. A outra diferença é queLINE INPUT
não exibe um prompt por padrão; se você quiser um, precisará especificá-lo explicitamente.INPUT$(n)
não exibe prompt ou cursor, mas simplesmente espera até que o usuário insiran
caracteres e, em seguida, retorna uma string contendo esses caracteres. Ao contrário deINPUT
ouLINE INPUT
, o usuário não precisa pressionar Enterdepois e, de fato, Enterpode ser um dos caracteres (ele fornecerá o caractere ASCII 13, conhecido como idiomas C\r
).Na maioria das vezes, isso é útil
INPUT$(1)
, geralmente em um loop.INPUT$
é bom em programas interativos em que pressionamentos de tecla únicos fazem coisas . Infelizmente, ele funciona apenas com chaves que possuem códigos ASCII; isso inclui coisas como Esce Backspace, mas não as teclas de seta, Inserte Delete, e outras.É aí que
INKEY$
entra. É semelhante aoINPUT$(1)
fato de retornar os resultados de um único pressionamento de tecla 1 , mas diferente no seguinte:INKEY$
não leva nenhum argumento.Enquanto
INPUT$(n)
interrompe a execução até que o usuário insiran
caracteres,INKEY$
não interrompe a execução. Se o usuário estiver pressionando uma tecla no momento,INKEY$
retornará uma sequência que representa essa tecla; caso contrário, ele retorna""
. Isso significa que, se você deseja usarINKEY$
para obter o próximo pressionamento de tecla, precisará envolvê-lo em um loop de espera ocupada : 2Ambos
INPUT$
eINKEY$
retornam caracteres ASCII para chaves que correspondem a caracteres ASCII (incluindo caracteres de controle como escape, tab e backspace). No entanto,INKEY$
também pode manipular algumas chaves que não possuem códigos ASCII. Para isso (diz o arquivo de ajuda), "INKEY $ retorna uma string de 2 bytes composta pelo caractere nulo (ASCII 0) e pelo código de verificação do teclado".Claro como lama? Aqui estão alguns exemplos. Se você usar o
INKEY$
loop acima para capturar um pressionamento de tecla da tecla de seta esquerda,k$
ele conterá a sequência"␀K"
(com oK
código de varredura representando 75). Para a seta direita, é"␀M"
(77). A página para baixo é"␀Q"
(81). F5 é"␀?"
(63).Ainda limpo como lama? Sim. Não é a coisa mais intuitiva do mundo. O arquivo de ajuda possui uma tabela de códigos de verificação, mas eu sempre escrevo um pequeno programa para imprimir os resultados
INKEY$
e pressiono um monte de teclas para descobrir quais são os valores corretos. Depois de saber quais caracteres correspondem a quais teclas, você pode usarRIGHT$(k$,1)
eLEN(k$)
distinguir entre todos os casos diferentes que pode encontrar.Bottom line?
INKEY$
é estranho, mas é o único caminho a percorrer se o seu programa exigir entrada sem bloqueio ou precisar usar as teclas de seta .1 Não inclui Shift, Ctrl, Alt, PrntScr, Caps Lock, e similar. Aqueles não contam. : ^ P
2 O
WHILE ... WEND
idioma aqui é o que aprendi nos meus livros QBasic. Para fins de golfe, no entanto, umGOTO
loop é mais curto .fonte
LOCATE pode ser realmente poderoso
A
LOCATE
instrução permite que você coloque o cursor em qualquer lugar da tela (dentro dos limites usuais de espaço de 80x40 caracteres) e imprima algo nesse local. Esta resposta a um desafio realmente mostra isso (e também é combinada com muitas outras dicas deste tópico).O desafio pede que produzamos todos os caracteres que um usuário pressionou em uma grade de 16x6. Com
LOCATE
isso, é simplesmente uma questão de div e mod sobre o código ASCII (a
neste código):E depois imprimindo o caractere:
fonte
No QBasic, é habitual usar a
DIM
instrução para criar variáveis, dando-lhes um nome e um tipo. No entanto, isso não é obrigatório, o QBasic também pode derivar um tipo pelo sufixo do nome da variável. Como você não pode declarar e inicializar uma variável ao mesmo tempo, geralmente é aconselhável pular oDIM
no codegolf. Dois trechos que são funcionalmente idênticos *:* Observe que isso cria dois nomes de variáveis diferentes.
Podemos especificar o tipo da variável adicionando
$
ao final de um nome de variável para strings,!
números de precisão únicos e%
duplos. Simples são assumidos quando nenhum tipo é especificado.Observe que isso também vale para matrizes. Geralmente, uma matriz é definida como:
Mas matrizes também não precisam ser
DIM
med:a$
agora é uma matriz para seqüências de caracteres com 11 slots: do índice 0 ao e inclusive o índice 10. Isso é feito porque o QBasic tem uma opção que permite a indexação com base em 0 e em 1 para matrizes. Um tipo padrão de matriz suporta ambos dessa maneira.Lembra da matriz de vinte slots que
DIM
medimos acima? Na verdade, ele possui 21 slots, porque o mesmo princípio se aplica a matrizes esmaecidas e não esmaecidas.fonte
Encurtando
IF
declaraçõesIF
as instruções são bastante caras, e reduzi-las pode economizar muitos bytes.Considere o seguinte (adaptado de uma resposta de Erik, o Outgolfer):
A primeira coisa que podemos fazer é salvar
ENDIF
usando umaIF
instrução de linha única :Isso funciona desde que você não tente colocá-lo na mesma linha que qualquer outra coisa. Em particular, se você tiver
IF
instruções aninhadas , apenas a mais interna poderá ser de uma linha.Mas, neste caso, podemos eliminar
IF
totalmente a matemática. Considere o que realmente queremos:RND<.5
for true (-1
), queremos:x
diminuir em 1y
permanecer o mesmoa(i)
tornar-se 1RND<.5
for false (0
), queremos:x
permanecer o mesmoy
diminuir em 1a(i)
tornar-se 0Agora, se nós salvar o resultado da condicional em uma variável (
r=RND<.5
), podemos calcular os novos valores dex
,y
ea(i)
:r
é-1
,x=x-1
; quandor
é0
,x=x+0
.r
é-1
,y=y+0
; quandor
é0
,y=y-1
.r
é-1
,a(i)=1
; quandor
é0
,a(i)=0
.Portanto, nosso código final se parece com:
economizando 20 bytes (40%) sobre a versão original.
A abordagem matemática pode ser aplicada surpreendentemente com frequência, mas quando houver uma diferença na lógica entre os dois casos (por exemplo, quando você precisar inserir algo em um caso, mas não no outro), você ainda precisará usá-lo
IF
.fonte
Às vezes, você deve evitar matrizes
Matrizes no QBasic, quando instanciadas sem
DIM
ter apenas 11 slots. Se um desafio exigir mais de 11 slots (ou N slots, onde N pode ser maior que 11), você deverá fazerDIM
a matriz. Além disso, vamos supor que queremos preencher essa matriz com dados:Mesmo jogando golfe, isso pode ocupar muito espaço. Nessas ocasiões, pode ser mais barato em bytes fazer o seguinte:
Aqui, colocamos tudo em uma sequência concatenada. Mais tarde, acessamos da seguinte forma:
Para esta abordagem, é importante que todos os valores tenham o mesmo comprimento. Pegue o valor mais longo e elimine todos os outros:
Você não precisa preencher o último valor e pode até pular as aspas finais! Se o desafio especificar que o espaço em branco não é permitido na resposta, use
RTRIM$()
para corrigir isso.Você pode ver esta técnica em ação aqui .
fonte
PRINT
(?
) tem algumas peculiaridadesOs números são impressos com um espaço à esquerda e à direita.
A impressão adiciona uma quebra de linha. Esse comportamento pode ser alterado adicionando uma vírgula no final da instrução para inserir uma guia ou um ponto e vírgula para evitar inserções:
Não é necessário usar
&
ou;
entre operações distintas ao imprimir, por exemplo.?1"x"s$
deve imprimir o número1
, com espaços de cada lado, a letrax
e o conteúdo das$
Saídas
A impressão de uma quebra de linha pode ser feita com apenas
?
fonte
-
é impresso lá. Um espaço também é impresso após o número. A melhor maneira que eu descobri para me livrar desses espaços é - nãoPRINT USING
sei se você deseja adicionar isso a esta resposta ou se deve ser uma resposta separada.WRITE
pode ser útil no lugar dePRINT
PRINT
geralmente é da maneira que você deseja produzir, já que é bastante flexível e possui o?
atalho. No entanto, oWRITE
comando pode salvar bytes em situações específicas:WRITE
coloque-a entre aspas duplas ("
). Se você precisar de saída com aspas duplas,WRITE s$
é muito menor que?CHR$(34);s$;CHR$(34)
. Veja, por exemplo, a solução QBasic mais curta conhecida .WRITE
não adiciona espaços antes e depois como elePRINT
faz.WRITE n
é muito menor que?MID$(STR$(n),2)
. Veja, por exemplo, o FizzBuzz no QB64 .WRITE
separa com vírgulas:WRITE 123,"abc"
saídas123,"abc"
. Não consigo pensar em um cenário em que isso seria útil, mas isso não significa que não exista.Limitações de
WRITE
:PRINT a;b
.LOCATE
, mas isso custa muitos bytes.)fonte
Às vezes, o QBasic gerencia entradas para funções. Abuse isso!
Existem algumas funções que funcionam com caracteres em vez de cadeias, mas não há um
char
tipo de dados no QBasic, existe apenas ostring ($)
tipo. Tomemos, por exemplo, aASC()
função, que retorna o código de chave ASCII para um caractere. Se entrarmossomente o primeiro
l
seria considerado pelo QBasic. Dessa forma, não precisamos nos preocupar em cortar uma corda no comprimento 1.Outro exemplo vem dessa pergunta em que a
STRING$()
função é usada em uma das respostas.Observe que o QBasic, quando oferecido uma sequência de vários caracteres e precisando de apenas um caractere, pega automaticamente o primeiro caractere e ignora o restante.
fonte