TOPIC 1: Layout
Groups
Before we can write structured code, we obviously need a structure.
We can create an organised structure by grouping statements that perform similar tasks.
Grouping subroutines and functions below the main loop keeps them tidy and out of the way.
Always place subroutines before functions; if you declare an array in a subroutine it must come before the function that uses it or the program will crash.
Grouping on a smaller scale further improves the readability of your code.
By grouping similar commands it becomes easier to follow the flow of your code.
e.g.
`Control
if upkey()=1 then inc x,1
if downkey()=1 then dec x,1
`Display
Dot x,100
Dot x,200
Dot 320,x
The Main Loop
The main loop is the hub of a program; it points to all the subroutines and functions that are required to keep the program running.
Try to write it as a conditional loop - when the conditions are met to leave the main loop the program should also end - this enables you to end the program in a much classier way than a do-loop, which forces the user to press F12 before they are allowed to leave.
By definition the main loop should be relatively short consisting mainly of calls rather than actual commands.
Indentation
When a command consumes more than one line, indent the code within the opening and closing statements so that you can clearly see where each command begins and ends. This is of great importance if you ever want help from other coders in the forums :-p.
Here's an example without indentation:
DO
For x = 1 to 20
if object angle y(x) <90
print "acute"
else
if object angle y(x) <180
print "obtuse"
else
print "reflex"
endif
endif
Next x
LOOP
and the same code indented:
DO
For x = 1 to 20
if object angle y(x) <90
print "acute"
else
if object angle y(x) <180
print "obtuse"
else
print "reflex"
endif
endif
Next x
LOOP
There is a vast difference between the two; the latter is much easier to follow.
TOPIC 2: Documentation
Even if your program has a good layout it can still be difficult to see what it does straight away.
Remarks are brief notes you can add to your code to help make it easier to understand, this not only helps others but can also refresh your own memory when you come back to an old program or work in progress.
Here is an example without documentation:
DIM win(1,4)
win(1,0) = 1
win(1,1) = 50
win(1,2) = 100
win(1,3) = 200
win(1,4) = 200
All we can determine from this is that the two-dimensional array "win" has been created, and five values assigned.
and the documented version of the same code:
`Create window array
DIM win(1,4)
win(1,0) = 1 : `0 = closed >1 = order (1 at front)
win(1,1) = 50 : `window x origin
win(1,2) = 100 : `window y origin
win(1,3) = 200 : `window width
win(1,4) = 200 : `window height
Now we can clearly see what the array is for, and what each field does.
Headings and Auxiliary Remarks
In the above code, I used two types of remark:
A header remark
and several auxiliary remarks
code here : `auxiliary remark
Auxiliary remarks are very useful as they allow us to document our code and still save lines. However, they can only document a single line of code, so to explain an entire block of code we use a header remark.
Boundaries
You can use symbols in remarks to give your program distinct boundaries. Coming up with your own system of boundaries can help you quickly navigate your code.
Here's an example of the type of boundaries I use.
`%%%%%%%%%%%%%%%%
` Program Title
` By OBese87
`%%%%%%%%%%%%%%%%
`--------
` Setup
`--------
set display mode 1024,768,32
sync on
sync rate 60
`------------
` Main Loop
`------------
For n= 1 to 20
pprint(20,20)
Next n
`--------------
` Subroutines
`--------------
label:
`blah
return
`---
label:
`blah
return
`------------
` Functions
`------------
`---Tier 3---
Function pprint(x,y)
print b(10,20,5)
Endfunction
`---Tier 2---
Function b(num1,num2,num3)
n= a(num1,num2) + a(num2,num3)
Endfunction
`----Tier 1---
Function a(num1,num2)
Endfunction num1*num2
TOPIC 3: Planning
No-one can write a perfect program in one fell swoop, we'd need some sort of amazing self-programming machine to do that, and the mad programmer who takes on the task will have to plan his code very carefully indeed!
I think of planning in the same way as sketching a portrait; if you start off by drawing the basic shapes, filling in the details later, you'll come away with something quite life-like. If you dive straight in and draw the entire thing without lifting your pencil you're just going to offend people.
Gosub Skeletons
Revisiting our sketch metaphor for a moment: the gosub skeleton is the basic shapes of our portrait; all we are doing is listing the routines that our program will require, we'll fill in the actual code later.
`------------------------
` Hello World mk.2
`------------------------
gosub format_font
gosub clear_screen
gosub print_message
End
`------------------------
` Gosubs
`------------------------
format_font:
return
`-----
clear_screen:
return
`-----
print_message:
return
Every gosub should
always return to the main loop.
The great benefit of using gosubs in this way is that it makes our program very easy to follow: we can just read the processes our program is going through without having to look through every single line of code and work it out. Another benefit is that while we are in the development phase, we can switch our gosubs on/off (by remming them out) when we want; this is extremely helpful for isolating bugs.
Testing & De-Bugging
Now and then you'll make mistakes, and that's when DB pounces on you and rubs it in your face in the form of a big error message. DB's error messages are famed for not being very informative so you'll have to learn how to fend for yourself in the bug-infested jungles of your code. Learning to root out bugs will save you from hours of tearing your hair out only to find that you've been trying to make DB draw a "corcle".
The most basic method of de-bugging is via process of elimination, as Sherlock Holmes said:
"When you eliminate the impossible, then whatever is left, no matter how improbable, must be the truth."
If we find everything that works, whatever is left must be what doesn't work.
Let's get to it! Since we are good programmers and we have planned out our program into subroutines, this should be easy.
`------------------------
` Hello World mk.2
`------------------------
rem gosub format_font : `remmed out for bug catching
gosub clear_screen
gosub print_message
End
`------------------------
` Gosubs
`------------------------
format_font:
set text funt "tahoma",1
set text size 16
ink rgb(255,0,128),0
return
`-----
clear_screen:
cls rgb(0,0,128)
return
`-----
print_message:
print "Hello World!"
return
When we rem out the "format_font" gosub our program works fine, it's only when we include it that DB mocks us with its error messages. So that's where our problem is!
Now we could go through the process of remming out each line and narrowing down the problem until we find the exact line that is causing trouble, but since this is a simple syntax error it is quite easy to spot: we have been trying to set the text "funt", I'm not sure what setting the funt does but it's not what we wanted to do. We change "funt" to "font", strip all the rems out of our code and hey-presto, it works! (You cannot set text funt)
All that remming out lines may be quick and painless for smaller programs but with larger ones it is worth writing a small "bug trap" to make DB show you what is going wrong. Most of the time bugs slip past you because the outcome of the offending procedure is not displayed on screen, so... we display it on screen!
This example is based on a DB Challenge entry that I wrote; it is designed to sort the animal names into alphabetical order:
sync on : sync rate 0
gosub MakeArrays
SortNames()
PrintNames()
sync
End
`------------------
` Gosubs
`------------------
MakeArrays:
Dim word$(6)
word$(0) = "~"
word$(1) = "Tiger"
word$(2) = "Gorilla"
word$(3) = "Zebra"
word$(4) = "Lion"
word$(5) = "Rhinocerus"
word$(6) = "Hippopotamus"
Dim sort(6) :`stores the alpha order
return
`------------------
` Functions
`------------------
Function SortNames()
DataMax=6
`Wipe Previous Sort Data
For clr = 1 to DataMax
Sort(clr)=0
Next clr
`Sort Data
For entity = 1 to DataMax
For pos = 1 to DataMax-1
`Is current position empty?
If Sort(pos)=0 then Sort(pos)=entity : exit
`Is selected field less than sorted field?
If ASC(word$(entity)) < ASC(word$(Sort(pos)))
`shift data to make space for current entity
For shift = pos to DataMax-1
Sort(shift+1)=Sort(shift)
Next shift
`place current entity in Sort array
Sort(pos)=entity
exit
Endif
if pos=DataMax-1 then Sort(DataMax)=entity :`If we can't find a place for it then it belongs at the end
Next pos
Next ent
EndFunction
`-----
Function PrintNames()
For animal = 1 to 6
Print animal ; ". " ; word$(sort(animal))
Next animal
EndFunction
Copy this program into your editor and run it. You'll see that something has gone wrong. The lion seems to have bred another 3 lions and they've eaten the zebra, the tiger and the rhino! To find out what is happening, I've written a "bug trap" that will print the position of each animal in the sort array as the animals are sorted.
`+++++++BUG TRAP+++++++
cls 128
for a = 1 to DataMax
Print a ; ". " ; Sort(a) ; ": " ; word$(Sort(a))
next a
sync
wait key
`-------BUG TRAP-------
Here is the program again with the new bug trap slotted in. Please copy this into your editor and run it.
sync on : sync rate 0
gosub MakeArrays
SortNames()
`PrintNames()
sync
End
`------------------
` Gosubs
`------------------
MakeArrays:
Dim word$(6)
word$(0) = "~"
word$(1) = "Tiger"
word$(2) = "Gorilla"
word$(3) = "Zebra"
word$(4) = "Lion"
word$(5) = "Rhinocerus"
word$(6) = "Hippopotamus"
Dim sort(6) :`stores the alpha order
return
`------------------
` Functions
`------------------
Function SortNames()
DataMax=6
`Wipe Previous Sort Data
For clr = 1 to DataMax
Sort(clr)=0
Next clr
`Sort Data
For entity = 1 to DataMax
For pos = 1 to DataMax-1
`Is current position empty?
If Sort(pos)=0 then Sort(pos)=entity : exit
`Is selected field less than sorted field?
If ASC(word$(entity)) < ASC(word$(Sort(pos)))
`shift data to make space for current entity
For shift = pos to DataMax-1
Sort(shift+1)=Sort(shift)
Next shift
`place current entity in Sort array
Sort(pos)=entity
exit
Endif
if pos=DataMax-1 then Sort(DataMax)=entity :`If we can't find a place for it then it belongs at the end
Next pos
`+++++++BUG TRAP+++++++
cls 128
for a = 1 to DataMax
Print a ; ". " ; Sort(a) ; ": " ; word$(Sort(a))
next a
sync
wait key
`-------BUG TRAP-------
Next ent
EndFunction
`-----
Function PrintNames()
For animal = 1 to 6
Print animal ; ". " ; word$(sort(animal))
Next animal
EndFunction
We can now see that the animal names are not being shifted properly; the higher entries are overwriting the lower ones!
Here's the code with the bug fixed:
sync on : sync rate 0
gosub MakeArrays
SortNames()
`PrintNames()
sync
End
`------------------
` Gosubs
`------------------
MakeArrays:
Dim word$(6)
word$(0) = "~"
word$(1) = "Tiger"
word$(2) = "Gorilla"
word$(3) = "Zebra"
word$(4) = "Lion"
word$(5) = "Rhinocerus"
word$(6) = "Hippopotamus"
Dim sort(6) :`stores the alpha order
return
`------------------
` Functions
`------------------
Function SortNames()
DataMax=6
`Wipe Previous Sort Data
For clr = 1 to DataMax
Sort(clr)=0
Next clr
`Sort Data
For entity = 1 to DataMax
For pos = 1 to DataMax-1
`Is current position empty?
If Sort(pos)=0 then Sort(pos)=entity : exit
`Is selected field less than sorted field?
If ASC(word$(entity)) < ASC(word$(Sort(pos)))
`shift data to make space for current entity
For shift = DataMax-1 to pos step -1
Sort(shift+1)=Sort(shift)
Next shift
`place current entity in Sort array
Sort(pos)=entity
exit
Endif
if pos=DataMax-1 then Sort(DataMax)=entity :`If we can't find a place for it then it belongs at the end
Next pos
`+++++++BUG TRAP+++++++
cls 128
for a = 1 to DataMax
Print a ; ". " ; Sort(a) ; ": " ; word$(Sort(a))
next a
sync
wait key
`-------BUG TRAP-------
Next ent
EndFunction
`-----
Function PrintNames()
For animal = 1 to 6
Print animal ; ". " ; word$(sort(animal))
Next animal
EndFunction
Now all the animals are being shifted around properly and are displaying in alphabetical order.
Compatibility
Sooner or later you are going to write a program that you are so proud of you want to share it with the DB community.
How horrible would it be if your moment of shining glory was tarnished by people saying your code didn't work?
The problem is that what works fine on your computer wont necessarily work on other peoples'. Luckily DB lets you standardise the way your programs run. [unfinished]
TOPIC 4: Economy
"It's the economy stupid!" - Ah if only this was a tutorial where each topic had a not-so-witty quote that was very loosely tied to it's subject matter.
Economy serves two masters - the computer and the author - but these masters have a conflict of interests.
When economy reduces the workload of the program by executing every command only when it is absolutely neccessary it serves the computer; when it reduces or modulates the code into independent procedures that are easier to manage and utilise it serves the author.
Economy tends to tip towards the computer when writing very demanding programs but on the whole it is much more beneficial to economise for the author. [unfinished]
Using The Right Commands
Program Speed
A small program that works is better than a large one that doesn't.
DBC Challenge Rank:
Rookie