Conseils pour jouer au golf dans QBasic


13

Quels conseils généraux avez-vous pour jouer au golf dans QBasic? Je cherche des idées qui peuvent être appliquées aux problèmes de golf de code en général qui sont au moins quelque peu spécifiques à QBasic (par exemple, "supprimer les commentaires" n'est pas une réponse).

Les conseils relatifs à l' émulateur QB64 sont également les bienvenus. Il a des fonctionnalités supplémentaires qui ne sont pas dans Microsoft QBasic.


Je suis curieux de connaître votre motivation. Je n'ai pas utilisé QBASIC depuis ma classe de programmation de 10e année. Incroyable comment j'ai enregistré directement sur les disquettes 1,44 sans aucune forme de contrôle de version et (généralement) évité les échecs catastrophiques.
Andrew Brēza

5
@ AndrewBrēza Motivation? La même chose que ma motivation pour le golf dans n'importe quelle langue: pour le plaisir! J'aime écrire de petits programmes en QBasic (même si je ne voudrais pas l'utiliser pour quelque chose de sérieux). Il y a aussi le bonus supplémentaire que le son et les graphiques (texte et pixel) sont intégrés, contrairement à mon "vrai" langage préféré, Python.
DLosc

Il est beaucoup plus facile d'écrire des jeux graphiques en QBasic qu'en python.
Anush

Si quelqu'un veut essayer des applications graphiques QBasic directement dans le navigateur, il peut utiliser ceci: github.com/nfriend/origins-host
mbomb007

Réponses:


10

Connaissez vos constructions en boucle

QBasic a plusieurs constructions de boucle: FOR ... NEXT, WHILE ... WEND, et DO ... LOOP. Vous pouvez également utiliser GOTOou (dans certaines situations) RUNpour boucler.

  • FOR ... NEXTest assez bon dans ce qu'il fait. Contrairement à Python, il est presque toujours plus court que l'équivalent WHILEou la GOTOboucle, même quand il devient un peu plus sophistiqué:

    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
    

    Notez que vous n'avez pas besoin de répéter le nom de la variable après NEXT, et vous pouvez éliminer l'espace entre les nombres et la plupart des mots-clés suivants.

  • WHILE ... WENDest bon lorsque vous avez une boucle qui peut avoir besoin d'être exécutée 0 fois. Mais si vous savez que la boucle s'exécutera au moins une fois, elle GOTOpourrait être plus courte d'un octet:

    WHILE n>1:n=n\2:WEND
    1n=n\2:IF n>1GOTO 1
    
  • Je n'utilise que DO ... LOOPpour des boucles infinies (sauf où RUNpeut être utilisé à la place). Bien qu'il coûte le même nombre de caractères qu'un inconditionnel GOTO, il est un peu plus intuitif à lire. (Notez que "boucle infinie" peut inclure des boucles que vous ne pouvez pas utiliser avec a GOTO.) La syntaxe DO WHILE/ DO UNTIL/ LOOP WHILE/ LOOP UNTILest trop verbeuse; il vaut mieux utiliser WHILEou GOTOselon le cas.
  • GOTOest, comme mentionné ci-dessus, la manière générale la plus courte d'écrire une boucle do / while. Utilisez des numéros de ligne à un chiffre au lieu d'étiquettes. Notez que lorsque a GOTOest la seule chose dans la THENpartie d'une IFinstruction, deux syntaxes de raccourcis également concises sont disponibles:

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

    GOTOpeut également être utilisé pour créer des flux de contrôle plus compliqués . Les opposants appellent cela du "code spaghetti", mais c'est du code golf: l'illisibilité est presque une vertu! GOTOfierté!

  • RUNest utile lorsque vous devez sauter à un endroit fixe du programme et que vous n'avez pas besoin de conserver les valeurs des variables. RUNpar lui-même redémarrera le programme par le haut; avec une étiquette ou un numéro de ligne, il redémarrera à cette ligne. Je l'ai principalement utilisé pour créer des boucles infinies sans état .

5

Utilisez des raccourcis pour PRINTetREM

Vous pouvez utiliser à la ?place de PRINTet 'au lieu de REM(commentaire).

'peut également s'avérer utile lors du polyglotage avec des langages qui prennent 'en charge la syntaxe char ou string.


5

Test de divisibilité

Dans les programmes qui vous demandent de tester si un entier est divisible par un autre, la manière la plus évidente est d'utiliser MOD:

x MOD 3=0

Mais un moyen plus court consiste à utiliser la division entière:

x\3=x/3

Autrement dit, xint-div 3est égal à xfloat-div 3.

Notez que ces deux approches reviendront 0pour falsey et -1pour true, vous devrez donc peut-être annuler le résultat ou le soustraire au lieu de l'ajouter.


Si vous avez besoin de la condition opposée (c'est-à x- dire qu'elle n'est pas divisible par 3), l'approche évidente consiste à utiliser l'opérateur non égal:

x\3<>x/3

Mais s'il xest garanti d'être non négatif, nous pouvons enregistrer un octet. La division entière tronque le résultat, il sera donc toujours inférieur ou égal à la division flottante. Par conséquent, nous pouvons écrire la condition comme suit:

x\3<x/3

De même, s'il xest garanti négatif, la troncature augmente le résultat et nous pouvons écrire x\3>x/3. Si vous ne connaissez pas le signe de x, vous devrez vous y tenir <>.


5

Abus de scanner

Comme dans de nombreuses langues, il est important de savoir quels caractères peuvent et ne peuvent pas être supprimés.

  • Tout espace à côté d'un symbole peut être supprimé: IF""=a$THEN?0
  • L' espace peut habituellement être enlevé entre un chiffre et une lettre apparaissant dans cet ordre : FOR i=1TO 10STEP 2. Il existe quelques différences entre QBasic 1.1 (disponible sur archive.org ) et QB64 :
    • QBasic 1.1 permet de supprimer l'espace entre n'importe quel chiffre et une lettre suivante. De plus, dans les instructions print, il déduira un point-virgule entre des valeurs consécutives: ?123xdevient PRINT 123; x. Les exceptions à ce qui précède sont des séquences comme 1e2et 1d+3, qui sont traitées comme une notation scientifique et étendues à 100!et 1000#(simple et double précision, respectivement).
    • QB64 est généralement le même, mais les chiffres ne peuvent pas être suivis de d, eou pas fdu tout, sauf s'ils font partie d'un littéral de notation scientifique bien formé. (Par exemple, vous ne pouvez pas omettre l'espace après le numéro de ligne dans 1 FORou 9 END, comme vous le pouvez dans QBasic proprement dit.) Il n'infère les points-virgules dans les instructions d'impression que si l'une des expressions est une chaîne: ?123"abc"fonctionne, mais pas ?TAB(5)123ou ?123x.
  • En parlant de points-virgules, QBasic 1.1 ajoute un point-virgule à la fin d'une PRINTinstruction qui se termine par un appel à TABou SPC. (QB64 ne fonctionne pas.)
  • 0peut être omis avant ou après le point décimal ( .1ou 1.), mais pas les deux ( .).
  • ENDIFest équivalent à END IF.
  • Le guillemet double de fermeture d'une chaîne peut être omis à la fin d'une ligne.

endiffonctionne réellement dans QB64, voir cette réponse
wastl

@wastl Il en est ainsi. Lorsque je l'ai testé pour la première fois en QB64, j'utilisais une ancienne version dans laquelle il s'agissait d'une erreur de syntaxe. Merci d'avoir mentionné!
DLosc

4

Combiner des Nextdéclarations

Next:Next:Next

Peut être condensé jusqu'à

Next k,j,i

où les itérateurs pour les Forboucles sont i, jet k- dans cet ordre.

Par exemple ci-dessous (69 octets)

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

Peut être condensé jusqu'à 65 octets

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

Et en ce qui concerne la façon dont cela affecte la mise en forme et l'indentation, je pense que la meilleure approche pour gérer cela est d'aligner la prochaine déclaration avec la plus externe pour la déclaration. Par exemple.

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

4

Connaissez vos méthodes de saisie

QBasic a plusieurs façons d'obtenir l' entrée du clavier de l' utilisateur: INPUT, LINE INPUT, INPUT$et INKEY$.

  • INPUTest votre instruction d'entrée polyvalente standard. Le programme arrête ce qu'il fait, affiche un curseur et permet à l'utilisateur de taper une entrée, terminée par Enter. INPUTpeut lire des nombres ou des chaînes, et il peut lire plusieurs valeurs séparées par des virgules. Vous pouvez spécifier une chaîne comme invite, vous pouvez utiliser l'invite de point d'interrogation par défaut et vous pouvez même (je viens de l'apprendre ce soir) supprimer complètement l'invite. Quelques exemples d'appels:
    • INPUT x$,y
      Utilise l' ? invite par défaut et lit une chaîne et un nombre séparés par des virgules.
    • INPUT"Name";n$
      Demande Name? et lit une chaîne.
    • INPUT"x=",x
      Invite avec x=(pas de point d'interrogation! Notez la virgule dans la syntaxe) et lit un nombre.
    • INPUT;"",s$
      Supprime l'invite (en utilisant la syntaxe de virgule ci-dessus avec une chaîne d'invite vide), lit une chaîne et ne passe pas à la ligne suivante lorsque l'utilisateur frappe Entrée (c'est ce que fait le point-virgule après INPUT). Par exemple, si vous PRINT s$immédiatement après cela, votre écran ressemblera User_inputUser_input.
  • Un inconvénient INPUTest que vous ne pouvez pas lire une chaîne avec une virgule, car INPUTutilise la virgule comme séparateur de champ. Pour lire une seule ligne de caractères arbitraires (ASCII imprimables), utilisez LINE INPUT. Il a les mêmes options de syntaxe que INPUT, sauf qu'il prend exactement une variable qui doit être une variable de chaîne. L'autre différence est qu'il LINE INPUTn'affiche pas d'invite par défaut; si vous en voulez un, vous devrez le spécifier explicitement.
  • INPUT$(n)n'affiche ni invite ni curseur, mais attend simplement que l'utilisateur entre des ncaractères, puis renvoie une chaîne contenant ces caractères. Contrairement à INPUTou LINE INPUT, l'utilisateur n'a pas besoin d'appuyer Enterpar la suite, et Enterpeut en fait être l'un des caractères (il donnera le caractère ASCII 13, connu sous le nom de langages de type C \r).

    Le plus souvent, cela est utile car INPUT$(1), généralement en boucle. INPUT$est bon dans les programmes interactifs où les touches uniques font des choses . Malheureusement, cela ne fonctionne qu'avec des clés qui ont des codes ASCII; cela inclut des choses comme Escet Backspace, mais pas les touches fléchées, Insertet Delete, et d'autres.

  • C'est là INKEY$qu'intervient. Il est similaire au INPUT$(1)fait qu'il renvoie les résultats d'une seule touche 1 , mais différent en ce que:

    • INKEY$ ne prend aucun argument.
    • Bien qu'il INPUT$(n)arrête l'exécution jusqu'à ce que l'utilisateur entre des ncaractères, il INKEY$n'arrête pas l'exécution. Si l'utilisateur appuie actuellement sur une touche, INKEY$renvoie une chaîne représentant cette touche; sinon, il revient "". Cela signifie que si vous souhaitez utiliser INKEY$pour obtenir la touche suivante, vous devez l'encapsuler dans une boucle d' attente occupée : 2

      k$=""
      WHILE""=k$
      k$=INKEY$
      WEND
      
    • Les deux INPUT$et INKEY$renvoient des caractères ASCII pour les clés qui correspondent aux caractères ASCII (y compris les caractères de contrôle comme d'échappement, tabulation et retour arrière). Cependant, INKEY$peut également gérer certaines clés qui n'ont pas de codes ASCII. Pour ceux-ci (selon le fichier d'aide), "INKEY $ renvoie une chaîne de 2 octets composée du caractère nul (ASCII 0) et du code de numérisation du clavier."

      Clair comme de la boue? Voici quelques exemples. Si vous utilisez la INKEY$boucle ci-dessus pour capturer une pression sur la touche fléchée gauche, k$contiendra la chaîne "␀K"(avec le Kcode de balayage représentatif 75). Pour la flèche droite, c'est "␀M"(77). La page suivante est "␀Q"(81). F5 est "␀?"(63).

      Toujours aussi limpide que de la boue? Ouais. Ce n'est pas la chose la plus intuitive au monde. Le fichier d'aide a un tableau de codes de numérisation, mais j'écris toujours un petit programme pour imprimer les résultats INKEY$et j'appuie sur un tas de touches pour trouver les bonnes valeurs. Une fois que vous savez quels caractères correspondent à quelles touches, vous pouvez utiliser RIGHT$(k$,1)et LEN(k$)faire la distinction entre tous les différents cas que vous pouvez rencontrer.

    En bout de ligne? INKEY$est bizarre, mais c'est la seule façon de procéder si votre programme nécessite une entrée non bloquante ou doit utiliser les touches fléchées .


1 Non compris Shift, Ctrl, Alt, PrntScr, Caps Locket similaire. Ça ne compte pas. : ^ P

2 L' WHILE ... WENDidiome ici est ce que j'ai appris dans mes livres QBasic. Pour le golf, cependant, une GOTOboucle est plus courte .


3

LOCATE peut être vraiment puissant

L' LOCATEinstruction vous permet de placer le curseur n'importe où sur l'écran (dans les limites habituelles de l'espace de 80 x 40 caractères) et d'imprimer quelque chose à cet endroit. Cette réponse à un défi le montre vraiment (et est également combinée avec de nombreux autres conseils de ce sujet).

Le défi nous demande de sortir chaque caractère sur lequel un utilisateur a appuyé dans une grille 16x6. Avec LOCATEc'est simplement une question de div et de mod sur le code ASCII ( adans ce code):

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

Et puis imprimer le caractère:

?CHR$(a)

3

Dans QBasic, il est habituel d'utiliser l' DIMinstruction pour créer des variables, en leur donnant un nom et un type. Cependant, ce n'est pas obligatoire, QBasic peut également dériver un type par le suffixe du nom de la variable. Comme vous ne pouvez pas déclarer et initialiser une variable en même temps, il est souvent sage d'ignorer le DIMcodegolf. Deux extraits fonctionnellement identiques *:

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

* Notez que cela crée deux noms de variables différents.

Nous pouvons spécifier le type de la variable en ajoutant $à la fin d'un nom de variable pour les chaînes, !pour les nombres en simple précision et %pour les doubles. Les simples sont supposés quand aucun type n'est spécifié.

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

Notez que cela vaut également pour les tableaux. Habituellement, un tableau est défini comme:

DIM a(20) AS STRING

Mais les tableaux n'ont pas non plus besoin d'être DIMtraités:

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

a$est maintenant un tableau pour les chaînes avec 11 emplacements: de l'index 0 à l'index 10. Ceci est fait parce que QBasic a une option qui permet l'indexation basée sur 0 et basée sur 1 pour les tableaux. Un type de tableau par défaut prend en charge les deux de cette façon.

Rappelez-vous le tableau à vingt emplacements que nous avons DIMcité ci-dessus? Cela a en fait 21 emplacements, car le même principe s'applique aux tableaux atténués et non atténués.


Je n'ai jamais réalisé que cela s'appliquait également aux tableaux. Intéressant.
trichoplax

3

raccourcissant IFdéclarations

IF les instructions sont assez chères, et les jouer au golf peut économiser beaucoup d'octets.

Considérez ce qui suit (adapté d'une réponse d'Erik l'Outgolfer):

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

La première chose que nous pouvons faire est d'enregistrer le ENDIFen utilisant une IFinstruction sur une seule ligne :

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

Cela fonctionne tant que vous n'essayez pas de le mettre sur la même ligne que n'importe quoi d'autre. En particulier, si vous avez des IFinstructions imbriquées , seule la plus interne peut être unilatérale.

Mais dans ce cas, nous pouvons éliminer IFentièrement les mathématiques. Considérez ce que nous voulons réellement:

  • Si RND<.5est vrai ( -1), nous voulons:
    • x diminuer de 1
    • y rester le même
    • a(i) devenir 1
  • Sinon, si RND<.5est false ( 0), nous voulons:
    • x rester le même
    • y diminuer de 1
    • a(i) devenir 0

Maintenant , si nous gagnons le résultat du conditionnel dans une variable ( r=RND<.5), nous pouvons calculer les nouvelles valeurs x, yet a(i):

  • Quand rest -1, x=x-1; quand rest 0, x=x+0.
  • Quand rest -1, y=y+0; quand rest 0, y=y-1.
  • Quand rest -1, a(i)=1; quand rest 0, a(i)=0.

Notre code final ressemble donc à:

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

économiser un énorme 20 octets (40%) sur la version originale.


L'approche mathématique peut être appliquée de manière surprenante souvent, mais lorsqu'il y a une différence de logique entre les deux cas (par exemple lorsque vous devez saisir quelque chose dans un cas mais pas dans l'autre), vous devrez toujours utiliser IF.


3

Parfois, vous devez éviter les tableaux

Les tableaux dans QBasic, lorsqu'ils sont instanciés sans, DIMn'ont que 11 emplacements. Si un défi nécessite plus de 11 emplacements (ou N emplacements, où N peut être supérieur à 11), vous devez utiliser DIMle tableau. Supposons également que nous voulons remplir ce tableau avec des données:

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

Même au golf, cela peut prendre beaucoup de place. Dans de telles occasions, cela peut être moins cher en octets:

a$ = "value 1value 2"

Ici, nous plaçons tout dans une chaîne concaténée. Plus tard, nous y accédons comme suit:

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

Pour cette approche, il est important que toutes les valeurs soient de longueur égale. Prenez la valeur la plus longue et supprimez toutes les autres:

a$="one  two  threefour "

Vous n'avez pas besoin de remplir la dernière valeur et vous pouvez même ignorer les guillemets de clôture! Si le défi spécifie que l'espace blanc n'est pas autorisé dans la réponse, utilisez RTRIM$()pour corriger cela.

Vous pouvez voir cette technique en action ici .


3

PRINT( ?) a quelques bizarreries

Les nombres sont imprimés avec un espace de début et de fin.

L'impression ajoute un saut de ligne. Ce comportement peut être modifié en ajoutant une virgule à la fin de l'instruction pour insérer un onglet à la place, ou un point-virgule pour éviter toute insertion:

Il n'est pas nécessaire d'utiliser &ou ;entre des opérations distinctes lors de l'impression, par exemple. ?1"x"s$imprime le numéro 1, avec des espaces de chaque côté, la lettre xet le contenu dus$

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

Les sorties

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

L'impression d'un saut de ligne peut être effectuée avec juste ?


Plus précisément sur l'impression des nombres: un espace est imprimé avant le nombre s'il n'est pas négatif; sinon, un signe moins y -est imprimé. Un espace est également imprimé après le numéro. La meilleure façon que j'ai découverte pour se débarrasser de ces espaces est PRINT USING--dunno si vous voulez ajouter cela à cette réponse ou s'il doit s'agir d'une réponse distincte.
DLosc

2

WRITE peut être utile à la place de PRINT

PRINTest généralement la façon dont vous voudrez faire la sortie, car il est assez flexible et a le ?raccourci. Cependant, la WRITEcommande peut vous faire économiser des octets dans des situations spécifiques:

  • Lors de la sortie d'une chaîne, la WRITEplace entre guillemets doubles ( "). Si vous avez besoin d'une sortie avec des guillemets doubles, elle WRITE s$est beaucoup plus courte que ?CHR$(34);s$;CHR$(34). Voir, par exemple, le quine QBasic connu le plus court .
  • Lors de la sortie d'un nombre, WRITEn'ajoute pas d'espaces avant et après comme il le PRINTfait. WRITE nest beaucoup plus court que ?MID$(STR$(n),2). Voir, par exemple, FizzBuzz dans QB64 .
  • Lors de la sortie de plusieurs valeurs, WRITEséparez-les par des virgules: WRITE 123,"abc"sorties 123,"abc". Je ne peux pas penser à un scénario où cela serait utile, mais cela ne signifie pas qu'il n'y en a pas.

Limitations de WRITE:

  • Il n'y a aucun moyen de sortir plusieurs valeurs sans séparateur comme avec PRINT a;b.
  • Il n'y a aucun moyen de supprimer la nouvelle ligne à la fin de la sortie. (Vous pouvez peut-être contourner ce problème avec LOCATE, mais cela coûte beaucoup d'octets.)

1

Parfois, QBasic modifie les entrées des fonctions. Abusez ça!

Il y a quelques fonctions qui fonctionnent sur les caractères au lieu des chaînes, mais il n'y a pas de chartype de données dans QBasic, il n'y a que le string ($)type. Prenons par exemple la ASC()fonction, qui renvoie le code-clé ASCII d'un caractère. Si nous voulions entrer

PRINT ASC("lala")

seul le premier lserait pris en compte par QBasic. De cette façon, nous n'avons pas à nous soucier de couper une chaîne jusqu'à la longueur 1.

Un autre exemple vient de cette question où la STRING$()fonction est utilisée dans l'une des réponses.

La fonction STRING $ prend deux arguments, un nombre n et une chaîne s $, et construit une chaîne composée de n copies du premier caractère de s $

@DLosc, ici

Notez que QBasic, lorsqu'il propose une chaîne multi-caractères et ne nécessitant qu'un seul caractère, prend automatiquement le premier caractère et ignore le reste.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.