Nous allons voir comment optimiser le code des
programmes, autant pour faire baisser la taille que pour en
augmenter la rapidité!
Vous apprendrez comment modifier telle ou telle ligne à bon
écient.
Ce dossier s'étoffera au fur et à mesure que des astuces seront
découvertes : vous
pouvez participer en envoyant vos astuces.
Documentation utilisée
Pour faire ce tutorial, je me suis aidé de 83trix.txt
(que j'ai traduit pour les besoins de ce tutoriel) et de Saving Memory in TI-BASIC Programs.
J'ai également ajouté des trucs que j'ai trouvé par moi même
ou que l'on m'a indiqué.
Je remercie également Raphaël Domenge qui m'a aidé en ce qui
concerne les tests de rapidité (variables et autres).
Vous découvrirez ainsi beaucoups d'optimisations inédites...
Optimisations simples :
En fin de ligne, vous pouvez ne pas fermer les parenthèses ou
les gillemets. C'est la règle la plus importante!
Le caractère "->" ferme
aussi les parenthèses automatiquement, de même que les
guillemets.
Par contre, le caractère ":"
ferme les parenthèses mais pas les guillemets.
If LSN(8)>-1 : dans ce genre d'utilisations,
il ne faut pas hésiter à réinterpréter comme ceci : If -1<LSN(8 pour gagner un octet (la
parenthèse de fermeture)
Calculez le plus posible d'opérations si elles ne demandent pas
de variables. Exemple : 4^3 deviendra
64 : vous gagnez un octet.
Idem pour A^2 et A^3
qui deviennent A2 et A3
(le 3 se trouve
dans [MATHS] [3]).
N'utilisez jamais * si vous faites un
multiplication avec au moins une variable. Exemple : 5*X deviendra 5X.
De même, une multiplication est plus rapide qu'une division.
Exemple : remplacez toujours A/2 par .5A. Et A/10
par .1A.
Les nombres en zéro virugle quelque chose n'ont pas besoin du
zéro initial pour être compris par l'OS. Ainsi 0.214 peut s'écrire .214
pour gagner un octet!
100 s'écrit E2 (2 est le
nombre de 0) : vous gagnez un octet
ceci est plus lent (en effet, la calculatrice calule ce que donne
E2
alors que pour 100, il n'y a pas
besoin).
Réorganisations du code :
Si vous avez l'occasion d'utiliser IS>(
ou DS<(, faites-le : c'est plus
petit et plus rapide.
If K=24:X-1->X peut s'écrire plus
rapidement X-(K=24->X. Vous
gagnerez deux octets et le programme s'exécutera plus
rapidement.
De même pour plusieures conditions : If
K=24:X-1->X:If K=26:X+1->X s'écrit X-(K=24)+(K=26->X.
Préferez DelvarA à 0->A : vous ne gagnez aucun octet (en
effet, il s'agit d'une commande qui prend deux octets) mais cela
s'exécute plus rapidement et vous pouvez omettre les : si vous réinitialisez plusieures
variables à la suite. Exemple : DelvarADelvarBDelvarC.
Ici, vous gagnez deux octets et cela libère l'espace reservé à
la variable (18 octets).
Il n'y a pas que Delvar qui prend
deux octets ; il y a aussi Picx :
lorque vous stoquez ou recalez une image, mettez juste le numéro
de l'image et non sa variable. Exemple : StorePic
Pic1 peut être remplacé par StorePic
1.
À l'instart de Delvar, toujours,
avec les instructions Archive et Unarchive, les :
peuvent être négligés. Attention : ceci n'est valable que pour
la version 1.12 et n'est pas possible avec la 1.14. Il est donc
recommandé de ne pas utiliser cette optimisation si vous mettez
votre programme sur internet.
Les conditions peuvent souvent être amoindries. Exemple : If N<>0 devient If
N et If N=0 devient If not(N.
Pour passer de zéro à un ou de un à deux dans l'exemple d'un
jeu à deux joueurs à tour de rôle, normalisez le joueur un
comme égal à 0 et le joueur deux comme égal à 1 (pour
afficher le joueur en cours, ajoutez 1). Ensuite, pour passer
d'un joueur à un autre, faites simplement not(N->N.
Il existe aussi une autre solution : N xor
1-> N. Vous perdez un octet mais il est bon de savoir
cette astuce...
Pour remettre à zéro une liste, utilisez cette formule : DelvarL1:X->dim(L1.
Cela permet de remettre à zéro la liste (en l'effacant : au cas
où il y aurait déjà des nombres stoqués) puis de la recréer.
C'est la structure la plus petite possible.
Disp"HELLO
Disp"WORLD
et
Disp"HELLO","WORLD
prennent autant d'octets : c'est plutot un choix estétique qui
est à faire.
Par contre, si vous voulez faire une pause après ce Disp, vous pouvez utiliser
Disp"HELLO
Pause "WORLD : vous gagnerez deux octets.
Sachez aussi que vous pouvez mêttre plusieurs textes à écrire
avec l'instrucion Text(. Exemple : Text(25,20,P," points. Ici, la
variable numérique est directement concaténée au texte "
points" pour être affiché sur l'écran grapique.
Si vous avez une incrémentation négative dans une boucle For(, préférez For(L,0,8:Disp
8-L à For(L,8,0,-1:Disp L :
vous gagnerez un octet.
Je vous conseille de ne jamais utiliser Stop
: en effet, il vous viendra peut-être un jour à l'idée (ou
quelqu'un d'autre) d'insérer votre programme dans un shell en
BASIC : préférez Return et, le plus
souvent possible, essayez de finir à la fin du programme : cela
vous épargnera deux octets.
Pour faire des sprites, utilisez l'instruction Text( avec des espaces pour effacer une
partie de l'écran rapidement.
Si vous avez de plus grands espaces à effacer, utilisez Text(-1.
Préférez l'utilisation des boucles (For(,
Repeat(, While()
le plus souvent possible et banissez à tout prix les labels
(sauf pour les menus, bien sûr) car lors d'un appel, le
programme est scanné de haut en bas : ce qui peut être très
long.
Rappelez-vous que votre TI est faite pour faire des calculs :
optimisez le plus possible vos algorithmes en utilisant les
instructions disponibles sur la TI.
Pour calculer 1/X, utilisez X-1 en appuyant sur la touche [x
-1].
L'application Finance contient dbd(
qui permet de calculer le nombre de jours entre deux dates.
De même, servez-vous aussi de vos acquis en mathématiques :
ainsi, A(B+C)-7 deviendra AB+AC-7 après dévelopement. Il s'agit là
d'un exemple simple car tout le monde connaît les dévelopements
mais vous pouvez utiliser d'autres théorèmes (Pythagore (pour
des coordonnées...), identités remarquables...).
En reprennant cet exemple, on remarque qu'on ne peut pas enlever
la parenthèse fermante car ensuite il y a une soustraction (si
c'était un plus, on aurai pu le mettre au début pour enlever la
parenthèse)... Faites attention à ne pas supprimer des
parenthèses qui servent réellement, surtout dans des calculs...
Utilisez des sous-programmes si une suite d'instructions est
répétée.
"->L1 : si la liste n'existe pas, elle
est crée avec une dimention de 0. Si celle-ci existe, la
dimention et les valeurs sont gardées (rien n'est changé).
Enfin, faites attention : l'utilisation de la variable Y n'est
pas une bonne solution car, lors de l'utilisation de ClrDraw, celle-ci est remise à zéro.
Utilisation de la variable Ans :
Cette variable est la plus rapide et elle vous permet parfois de
gagner pas mal d'octets.
Exemple d'utilisation : Repeat
Ans:getKey:End:If Ans=105:Goto M ...
Ce code peut remplacer : Repeat
K:getKey->K:End:If K=105:Goto M ...
De même, voici un programme qui choisit au hazard une chaine et
l'affiche (cas dun chat avec la calculatrice par exemple) : les
variables de type Strx prennent deux
octets : il convient d'utiliser Ans
de cette façon :
:randInt(1,5->N
:If N=1:"SALUT
:If N=2:"CA VA ?
:If N=3:"MON CHIEN EST MORT
:IF N=4:"JE PEUT PARLER ?
:If N=5:"IL FAUT QUE J'Y AILLE!
:Disp Ans
Ici, on voit nettement le gain d'octets. Attention: on ne peut
pas faire :randInt(1,5:If Ans=... car
si Ans serai égal à un chiffre (2
par exemple, si randInt( a renvoyé
ce chiffre) alors la chaine correspondante serai aussi stoquée
dans Ans ; or, à la ligne suivante
le test voudrai dire "Si une chaine (Ans) est égale à un
chiffre (3 dans notre exemple) Alors..." Or cela est
impossible! Faites attetion dans l'utilisation de cette variable.
Pour savoir si une fonction modifie la variable Ans, tapez-la dans l'écran principal : si
un résultat est renvoyé alors celui-ci est placé dans Ans.
Maintenant, c'est à vous de voir ce que vous pouvez faire de
cette variable et de l'utiliser à bon escient. Son utilisation
étant vaste, elle ne sera pas détaillée : à vous de faire des
tests et, après quelque temps, vous la connaitrez bien...
Plus de rapidité :
Bien entendu, vous utiliserez toutes les astuces vues précédemments et, dans
la plupart des cas, un octet en moins permet d'accélérer le programme (même
si ca ne se voit pas forécment).
Mais ce n'est pas toujours le cas : Output( est
deux fois plus rapide que Disp ! Cela s'explique
par le fait qu'il n'y a aucun formatage à faire avant l'affichage, ni
réorganisation de l'écran (passer une ligne...). Très utile
si vous devez effacer l'écran en "gardant" la première
ligne par exemple : l'effacement puis le réafichage se fait quasi-instantanément.
Ensuite, la variable Ans doit être utilisée le plus souvent possible!
L'astuce la plus simple pour accélérer un jeu est en même temps la moins connue
: lorsque vous configurez l'écran graphique, ajoutez cette ligne : 0->Xscl:0->Yscl
. Ainsi, lorsque vous appelerez ClrDraw, cette commande
sera 50% plus rapide! Pratique pour accélérer des pseudo-vidéo faites d'images
séparrées par un effacement de l'écran.
Si vous ne refermez pas vos boucles (Goto menant
hors de la boucle, voir même à l'interieur de celle-ci qui ne se terminera donc
pas normalement), le programme ralentira rapidement. Idem si vous utilisez Goto
dans Then-End.
Certaines variables sont plus rapides d'accès que d'autres. Dans l'ordre, de
la plus rapide à la plus lente : Ans;theta;X;Y;A;B;C;...;W;Z. Mais la différence
est minime! (0.015 secondes pour X et 0.019 secondes
pour Z, la plus lente). Utilisez theta dans les
boucles For(.
De même, L1 est la liste la plus rapide
(après Ans) et la vitesse décroit jusqu'à L6. Ensuite, viennent les listes personnalisées
qui sont bien plus lentes.
Un petit test pour vous éclairer : pour 100 accès et avec la ROM 1.12 :
Matrice A : 1.48 s
Liste perso X : 1.24 s
Liste L1 : 1.10 s
Variable A : 0.85 s
On en conclue qu'il est préférable d'utiliser des variables que des listes et
si ce choix devait être fait, utiliser les liste 1 à 6 avant celles personnalisées,
etc...
Les instructions Pxl-On(), Pxl-Off()
et Pxl-Change() sont un peu plus
rapides que les instructions Pt-On(),
Pt-Off() et Pt-Change()
(gain de sept secondes sur 2000 instructions). Par contre, ces deux groupes
de trois s'exécutent à la même vitesse respectives.
Ajouter un nombre à la suite d'une liste : Vous avez deux solutions :
- augment(L1,{N->L1
- dim(L1)+1->dim(L1:N->L1(Ans
La première ligne ne prend que 9 octets et la seconde 17 octets.
Mais la contrepartie est que la première ligne est deux fois plus lente
que la seconde.
Pour 100 rajouts, la première ligne met 6 secondes contre 3 pour la première.
Le précalculé :
Au lieu de faire :
Getkey-> K
X+(Ans=26)-(Ans=24->X
Y+(K=34)-(K=25->Y
On peut faire :
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,1,0,0,0,0,0,0,0,1->L1
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,1->L2
Getkey->K
X+L1(Ans->X
Y+L2(K->Y
Ceci est une petit aproche du précalculé. En effet, si
le joueur appui sur [Y=], rien ne se passera (0 sera ajouté à X
et à Y). En revanche, s'il appuie sur droite, X sera augmenté
de 1, etc... Pour être plus efficace et ne pas générer de
plantage si le joueur appuie sur [ENTER] par exemple (ENTER=105 :
L1(105 donne ERR:INVALID DIM ), il faudrai faire comme
suit :
105->dim(L1
-1->L1(24
1->L1(26
Idem pour L2...
Vous remarquerez que l'on gagne beaucoup de vitesse avec le
précalculé!
Exemple plus flagrant : si l'on veut faire un jeu avec des
angles, il faudra utiliser sin( et cos( (voir même tan(
). Or, ces fonctions sont très lentes! C'est dans ce genre de
situation que l'on voit l'interêt du précalculé : il faudra
précalculer les angles et y accéder via une liste qui
contiendra d'avance les résultats...
Un autre exemple? Et bien rand et randInt( sont des commandes aussi
coûteuses en rapidité. Si vous désirez avoir un jeu rapide,
faites une étape de chargement qui calculera une liste de
nombres aléatoires avant de jouer. Mais atention : si vous ne
prévoyez pas assez de nombres, le jeu risque de planter. Aussi,
si vous ne savez pas combien de nombres devront être tirés au
hazard, vous pouvez revenir au début de la liste à chaque fois
que celle-ci est terminée. Ou alors, s'il y a une petite pause
entre deux moments, profitez-en pour "piocher" les
nombres suivants.
Cette méthode ouvre la voie à beaucoupe de possibilités. Je
vous y ai juste sensibilisé : ce sera à vous de voir quel
programme demande du précalculé pour fonctionner plus
rapidement. Il serai bon d'appliquer le premier exemple dans tous
les jeux qui demandent de la rapidité...
Compression :
Si vous utilisez des listes pour stoquer de nombreuses données
(coordonnées par exemple), il est bon de la compresser avant de
terminer le programme pour libérer de la place. En effet, chaque
case d'une liste prend invariablement 9 octets quel que soit le
nombre stoqué (entier ou à virgule). Autant mettre 2 voir 4
chiffres dans chaque case : vous diminurez ainsi par quatre la
taille de la liste. Et, si elle est utilisée pour stoquer des
niveaux, vous pourrez ainsi en mettre quatre fois plus sur une
TI! Comment faire?
Tout d'abord, il faut que ces nombre ne puissent pas dépasser un
sertain nombre : exemple si ce sont des coordonnées qui sont
stoquées, elles vont de 0 à 94 (X et Y confondus) si WINDOW est
bien configuré.
Ainsi, par exemple, le code de compression de coordonnées par 2
sera : X+.01Y->L1(A
Et la lecture se fera ainsi pour X : iPart(L1(A->X
; et pour Y : 100fPart(L1(A.
Nous venons de voir une compression par deux. En continuant, on
obtiendrai une compression par quatre :
100A+B+.01C+.0001D->L1(A
Si A=12 ; B=53 ; C=45 ; D=86 :
L1(A=1253.4586. Quatre chiffres seront ainsi stoqués dans
une seule case de neuf octets (contre 36 sans compression).
La décompression se fera de la même façon que pour l'exemple
un :
L1(X->Y:iPart(.01Y->A:iPart(Y-100A->B:iPart(100fPart(Y->C:100fPart(100fPart(Y->D
Ce sera plus long (décompression oblige) mais l'avantage d'avoir
plus de niveaux (ou de place pour le reste des programmes) est un
assez bon argument pour employer cette méthode. Bien sûr, ce ne
devra pas ce faire durant un jeu (chargement seulement) et le
nombre de coordonnées doit être un multiple de quatre si vous
optez pour cette compression.
Tout comme le précalculé, on ne peut donner toutes les
méthodes de compression : ce paragraphe n'a pour but que la
sensibilisation à ce sujet : vous compresserez comme vous le
voudrez, comme le programme le permettra, mais surtout vous savez
maintenant que ce moyen existe...
Le bon et le mauvais programme :
Un bon résumé que j'ai trouvé je ne sais plus où sur internet
: BON PROGRAMME : original ; rapide ; petit en
taille ; pleins de fonctions ; facile à utiliser MAUVAIS PROGRAMME : trop de If ou de labels ;
gros en taille ; lent ; contient des erreurs
Conclusion :
Je suis sûr que vous avez trouvé ça interessant (mais long, le
sujet étant vaste) : si vous voulez faire des jeux rapides, il
va falloir passer par là, de même que si vous faites beaucoups
de petits programmes : vous pouvez gagner beaucoup d'espace! Je
vous conseille de reprendre pas mal de programmes et de les
optimiser à fond : vous découvrirez surement une astuce par
vous même. Plus vous vous entrainerez, moins cette étape sera
indispensable pour vous car vous aurez pris le réflexe
d'optimiser automatiquement.