Blitz:Good Practice

From Amiga Coding
Jump to: navigation, search

Like most BASICs, Blitz is reasonably easy to read and understand, especially with syntax highlighting provided by the editor. However, even moderately complicated programs will quickly grow unwieldy and difficult to follow, especially if you come back to the code after not looking at it for some time. It can be difficult to remember exactly what you were thinking when you wrote it which makes it tricky to fix bugs and modify the code at a later date. With a little effort and by following some simple suggestions it's easy to write code that's better looking, more easily readable and less prone to bugs.

Indent Your Code

Indenting blocks of code helps keep the code very easy to read and helps avoid errors and bugs caused by things like Else statements associated with the wrong If statement for example. Procedures, loops, If/Then/Else and Select blocks, all benefit greatly from being indented, especially when nesting several layers deep. For example:

If AddItem(a())
For i=1 To 10
If i=2
b(i)=a()+i
NPrint "Section 2"
Else
b(i)=i
NPrint "Other Section"
End If
Next i
Else
NPrint "Unable to add item!"
End If

Compared with:

If AddItem(a())
  For i=1 To 10
    If i=2
      b(i)=a()+i
      NPrint "Section 2"
    Else
      b(i)=i
      NPrint "Other Section"
    End If
  Next i
Else
  NPrint "Unable to add item!"
End If

Comment Your Code

Comments are very important for being able to read your code at a later date. When writing your code, they may not seem important and it will probably work fine without them, but when you have a complex program with lots of different sections, it can quickly become confusing, especially with so-called "magic numbers" and arguments whose functions aren't immediately obvious. For example:

x = 27
Dim y(x)
y(2) = 200

Compared with:

x = 27 ; Initial number of enemy ships
Dim y(x) ; Scores for each ship are held here
y(2) = 200 ; Ship 2 is the big one so has 200 points

Keep Lines Simple

It can be tempting to put multiple statements on one line, especially in the case of If statements, and if most of your BASIC programming has previously been on 8-bit machines this might even have been encouraged. But there's no real memory benefit in doing so since your program is compiled rather than interpreted, and the compiler just sees the code as the same. So instead of:

If score >= 200 Then winner = currentplayer:gameover = True:Gosub gamewon:Gosub resetgame

This is far neater and easier to read:

If score >= 200
  winner = currentplayer
  gameover = True
  Gosub gamewon
  Gosub resetgame
End If

On 8-bit machines like the Commodore 64 and Atari 800, this would use an extra few bytes of memory. But Blitz will compile both these examples into identical code so the only difference is in readability.

Use Spaces for Readability

Again, on very old 8-bit versions of BASIC, adding extra spaces cost you a few bytes of memory which might become crucial when the program was running. But Blitz Basic, like other modern languages, compiles the code into an executable and so spaces don't impact the finished result at all. So you can use blank lines and extra spaces to make your code easier to read by separating terms and expressions, by separating sections of code with blank lines, and by aligning columns of similar lines. For example:

#groundcol=3
#skycol=4
#player1col=6
#player2col=7
#enemycol=8
Dim enemies(5)
For i=1 To 5
enemies(i)=0
Next i
Gosub resetgame
Repeat
  Gosub checkplayer
  If score>200 AND player=1
    quit=True
  End If
Until quit
End

Compared with:

#groundcol  = 3
#skycol     = 4
#player1col = 6
#player2col = 7
#enemycol   = 8

Dim enemies(5)

For i = 1 To 5
  enemies(i) = 0
Next i

Gosub resetgame

Repeat
  Gosub checkplayer
  If score > 200 AND player = 1
    quit = True
  End If
Until quit

End

Use Procedures

Procedures help to modularise your code, effectively giving independent functions their own little program. This helps reduce bugs and keeps your code readable since blocks of code can be reduced to a single procedure call. For example, including the following at the top of your program:

Statement resetgame{}
  SHARED board()
  For i = 1 To 10
    For j = 1 To 10
      board(i, j) = 0
    Next j
  Next i
End Statement

Means that instead of including that code in the main part of your program wherever you need it, you can just include the function call:

If gamewon = 2 Then resetgame{}

There is a slight speed penalty for using procedures instead of Gosubs, but this will only have an impact for procedures called hundreds of times a second.

Name Things Well

Variables, labels, procedures and constants should all be named well. Again, if your only experience of BASIC is from 8-bit systems, you could be tempted to call your first variable a, your second b and so on. With effectively no affect on memory required by your program, it makes much more sense to give your variables short descriptive names instead. Capitals and underscores can be included to help readability. For example, MyScore, MyLives and CurrentEnemy will all make your program much easier to read than a, b and c. Likewise, function names like ResetTable{} and LoadPrefs{} make much more sense than F1{} and F2{}. Giving things names that are too long won't help matters either so it's best to find something maybe between 1 and 3 words in length.

Use Includes

Where possible, use the AmiBlitz include files instead of the built-in Blitz functions for operating system interaction. The include files are regularly updated and fixed, and use the official system-friendly methods for their workings. Of course, if you're programming a game and banging the hardware you won't be worried about the OS...

Avoid "Magic Numbers"

"Magic numbers" are literal numbers used in your code, which is fine when you're writing it, but may appear to be random to anyone else or even to yourself at a later date. A good alternative is to substitute a descriptive constant - the compiler will substitute in the actual value during compilation. This will make your code easy to read, and also make it much easier to modify values that might be used more than once in your program. For example:

Dim entries(100)

<Some other code here>

For i = 1 To 100
  result = calculate{entries(i)}
  NPrint "Result ", i, " of 100: ", result
Next i

To increase the maximum number of entries your program can use, you would need to search through your code and replace all the 100 values with, say, 200. Instead of this, using a constant means the value only has to be changed once which avoids the risk of forgetting to update the value somewhere:

#maxentries = 100

Dim entries(#maxentries)

<Some other code here>

For i = 1 To #maxentries
  result = calculate{entries(i)}
  NPrint "Result ", i, " of ", #maxentries, ": ", result
Next i

In particular, building and using a GUI for your program involves a lot of constants and repeated use of constants. While the old GUI examples that are supplied with Blitz Basic and AmiBlitz use plain numbers, it can quickly become tricky and slow to work with just the numbers as your interface and program grow in complexity. Remembering that the Cancel button is gadget ID number 27 isn't as easy as remembering it's #cancel...

Type Your Variables Appropriately

By default, Blitz automatically assigns the quick type to variables used without any type being specified by your code. Most of the time, however, there are more suitable variable types that can be used instead - if you don't need the fractional part of the number for example, the word type hold the same integer value range as a quick, is faster, and only takes up 2 bytes instead of 4. It is good practice to explicitly declare the desired variable type. This can either be done at before first use using the DEFTYPE statement, or by specifying the correct type extension when using the variable for the first time. For example:

DEFTYPE .l MyAddress

Or

 MyAddress.l = 0

Once declared, the variable no longer requires the type extension. In AmiBlitz, it is recommended that you use the Syntax statement to enforce stricter typing rules. Syntax 0 is the default Blitz behavior where no declarations are required, Syntax 1 requires all variables to be declared by DEFTYPE or Dim, and Syntax 2 requires types to be specified either using DEFTYPE or Dim, or on their first usage. Both stricter modes will also help to avoid bugs arising from mistyping variable names - an incorrectly named variable will quietly default to 0 in the default mode, which can be a tricky bug indeed to find! For example:

fileopen = OpenFile(0, "HighScore.txt")

if flieopen
  Fileoutput 0
  NPrint topscore
  CloseFile 0
  DefaultOutput
Else
  NPrint "Error opening file!
End If

With the default loose typing rules, this code will run, but will never save the top score to the file and will always give an error, since flieopen defaults to 0, even if fileopen is true from successfully opening the file. Moreover, the file will be held open since the CloseFile command won't be run, which prevents other programs from using the file. Using Syntax 1 or Syntax 2 at the start of your code however will cause that example to generate an error during compilation, allowing you to fix the mistake before it causes problems.

Check for Success

Many statements in Blitz return a value that can tell you whether a command was successful or not. Where possible it's a good idea to check this value so that your program can handle unexpected situations, since blindly assuming it was successful can result in crashes and other nasty things. For example, opening a window might fail due to lack of RAM, lack of graphics RAM, no public screen available etc. If that happens, it would be much better for your program to quit (preferably with a decent error message), than to carry on assuming it opened. Trying to draw graphics to a window that doesn't exist is pretty much guaranteed to crash your machine.