Atari Action - Chapter 6
Procedures
by Sheila (Spencer) Robbins
This is the only chapter that was written of an unfinished, unpublished book about the Action!
programming language.
Procedures
and Functions, collectively referred to as "routines", are the heart of the Action!
Language. (We will
cover functions in Chapter 7.) A procedure is a group of statements,
expressions, and/or commands that accomplish a certain task. See if you can
tell what the following simple procedure is intended to do:
PROC Count_ten()
BYTE counter
FOR counter=1 TO 10
DO
PrintB(counter)
Print("
")
OD
RETURN
If you were
to type in the procedure, compile it and run it, this is what you would see on
your screen:
1 2 3 4 5 6 7 8 9 10
Obviously,
Procedure "Count_ten" is designed to print the BYTE numbers 1 through 10 (with
a space between each number). That's all it does. Of course, most procedures
will be more useful and probably longer, but the preceding example gives you an
idea of how simple the procedure structure really is. The only requirements are
a name for the procedure and a RETURN at the end.
NOTE: Don't leave out the RETURN! Terrible things can
happen if you do, even if the program compiles without error. You will more
than likely have to turn your computer off and back on again to regain control,
thereby losing anything you may have had in the Editor. This by itself is
reason enough to save your program to disk or cassette before each compile or
run attempt.
The
procedure name can contain any alpha-numeric characters you wish, including the
underline, as long as it begins with a letter and isn't longer than 255
characters. The parentheses are also required, and later we will discuss
passing parameters within those parentheses.
You could
even have a procedure to do nothing at all if you wished, and in fact this is
quite useful when you are writing a program that will contain a number of
procedures. For example, if you were writing a program with several procedures,
but you weren't ready to flesh them all out, you could still call them from
your program without getting a compiler error. Here's an example of a
"do-nothing" procedure:
PROC Do_Nothing()
RETURN
Calling a
procedure is simplicity itself. Just type in the procedure's name at the point
you wish it to be executed. This is almost exactly like calling a subroutine in
BASIC. To call our previous two examples, we would enter the following:
PROC Main()
Count_ten()
Do_nothing()
RETURN
That's all
there is to it!
Now that
you know how to declare and call a procedure, you're ready to write your first
real Action! Program. You will find suggestions for changes after we finish, so
please type it in and save it to disk or cassette.
An
excellent way to learn any new language is to analyze a simple BASIC program
and then rewrite it in the new language. In order to make the translation, we
first need to study the program so that its structure will become more obvious.
This is a simple program, and even in BASIC the flow is easy to see. That will
make it much easier for us to make our translation.
Here, in
Atari BASIC, is the program that we will be taking apart and putting back
together in Action!. It's called "The Great Butterfly Chase".
10 REM INITIALIZE
20 DIM
MOVE$(1)
30 HCOL=1:HROW=1:BCOL=3:BROW=3:
REM
INITIAL POSITIONS OF HUMAN & BUTTERFLY
40 REM 10 TURNS EACH GAME
50 FOR
PLAYS=1 TO 10
60 REM DRAW THE "MAP"
70 FOR
ROW=1 TO 5
80 FOR
COL=1 TO 5
90 PRINT
" ";
100 IF
COL=BCOL AND ROW=BROW THEN PRINT "B";:GOTO 150: REM
THE BUTTERFLY
110 IF
COL=HCOL AND ROW=HROW THEN PRINT "H";:GOTO 150: REM
THE HUMAN
140 PRINT".":
REM NOBODY THERE
150 NEXT
COL
160 PRINT
170 NEXT
ROW
180 REM HUMAN MOVEMENT LOOP
190 PRINT:PRINT:PRINT
"Move number ";PLAYS
200 PRINT
"Direction (NSEW)";:INPUT MOVE$
210 IF
MOVE$="N" THEN HROW=HROW-1: REM MOVED NORTH (UP)
220 IF
MOVE$="S" THEN HROW=HROW+1 : REM MOVED SOUTH (DOWN)
230 IF
MOVE$="E" THEN HCOL=HCOL+1: REM MOVED EAST (RIGHT)
240 IF
MOVE$="W" THEN HCOL=HCOL-1: REM MOVED WEST (LEFT)
245 REM CHECK FOR "OUT OF BOUNDS"
250 IF
HCOL>5 THEN HCOL=5:GOTO 300
260 IF
HCOL<1 THEN HCOL=1:GOTO 300
270 IF
HROW>5 THEN HROW=5:GOTO 300
280 IF
HROW<1 THEN HROW=1:GOTO 300
290 GOTO
320
300 PRINT
"You hit the wall!"
310 REM CHECK TO SEE IF HUMAN AND BUTTERFLY OCCUPY THE SAME
SPACE
320 IF
HCOL=BCOL AND HROW=BROW THEN GOTO 700
330 REM BUTTERFLY'S MOVE
340 REM THE BUTTERFLY IS "SMART"
350 REM AND WILL MOVE AWAY FROM YOU
360 REM BUT HE DOESN'T ALWAYS MAKE THE
370 REM PERFECT MOVE!
380 IF
BCOL>HCOL AND BROW=HROW THEN BDIR=1
390 IF
BCOL>HCOL AND BROW>HROW THEN BDIR=2
400 IF
BCOL=HCOL AND BROW>HROW THEN BDIR=3
410 IF
BCOL<HCOL AND BROW>HROW THEN BDIR=4
420 IF
BCOL<HCOL AND BROW=HROW THEN BDIR=5
430 IF
BCOL<HCOL AND BROW<HROW THEN BDIR=6
440 IF
BCOL=HCOL AND BROW<HROW THEN BDIR=7
450 IF
BCOL>HCOL AND BROW<HROW THEN BDIR=8
460 REM THROW IN SOME RANDOMNESS
470 CHANGE=INT(RND(0)*9)
480 IF
CHANGE=0 THEN BDIR=BDIR+1
490 IF
CHANGE=1 THEN BDIR=BDIR-1
500 IF
BDIR=0 THEN BDIR=8
510 IF
BDIR=9 THEN BDIR=1
520 GOTO
BDIR*10+520
530 BCOL=BCOL+1:GOTO
610
540 BCOL=BCOL+1:BROW=BROW+1:GOTO
630
550 BROW=BROW+1:GOTO
610
560 BCOL=BCOL-1:BROW=BROW+1:GOTO 630
570 BCOL=BCOL-1:GOTO
610
580 BCOL=BCOL-1:BROW=BROW-1:GOTO
630
590 BROW=BROW-1:GOTO
610
600 BCOL=BCOL+1:BROW=BROW-1:GOTO
630
610 REM MAKE SURE THE BUTTERFLY
620 REM IS NOT OUT OF BOUNDS
630 IF
BCOL>5 THEN BCOL=5
640 IF
BCOL<1 THEN BCOL=1
650 IF
BROW>5 THEN BROW=5
660 IF
BROW<1 THEN BROW=1
670 REM SEE IF THE BUTTERFLY GOT HIMSELF CAUGHT
680 IF
BCOL=HCOL AND BROW=HROW THEN GOTO 700: REM OUT OF THE
LOOP
690 GOTO
710
700 PRINT
"You got him!":GOTO 740: REM OUT OF THE LOOP
710 NEXT
PLAYS
720 PRINT
"He escaped!"
730 REM OPTION TO PLAY AGAIN
740 PRINT:PRINT
"Want another game (Y/N)?";:OPEN #1,4,0,"K:":GET #1,KEY
750 PRINT
CHR$(KEY):IF CHR$(KEY)="Y" THEN CLOSE #1:RUN
760 END
Let's take
the BASIC program section by section:
Lines 20-30
set up the initial conditions of the game, setting the human's column and row
to "1" and the butterfly's column and row to "3". "MOVE$" is also dimensioned
here.
Line 50
starts the 10-play loop.
Lines 7-170
draw the map, printing an "H" for the human, a "B" for the butterfly, and a "."
For an empty cell.
Line 190
prints the play number
Line 200
asks for player input.
Lines
210-240 calculate the human's new position, based on his input.
Lines
250-280 check to see if the human tried to pass through a wall and "bounces"
him back to his previous position if he did.
Line 320
determines whether or not the human and butterfly are in the same place and
exits from the loop if they are.
Lines
380-450 determine the best possible move for the butterfly.
Lines
470-490 add the random factor for the butterfly's move.
Lines
500-510 correct the butterfly's move – there is no direction 0 or
direction 9.
Line 520
directs the program to the proper line according to the direction in which the
butterfly will be moving.
Lines
530-600 calculate the butterfly's new position.
Lines
610-660 keep the butterfly from going out of bounds.
Line 680 is
a repeat of line 320.
Line 710
moves us to the next play.
Line 720
declares the butterfly safe.
Lines
730-750 offer another game.
You might want
to run this program in BASIC and then in Action! to see the improvement in speed of
execution.
Here are
the names of the procedures we will be using in our Action! translation:
PROC Draw_Map()
PROC Move_Human()
PROC Move_Fly()
PROC Check_Stat()
PROC More_Play()
Our first Action! program may look a little
cumbersome compared with the BASIC program. But you can learn a great deal by
modifying it later, after you learn some new tricks to make it more compact.
Right now we're going to do an almost direct translation, though. We'll start
with the procedure to draw the map:
PROC
Draw_Map()
;Procedure to draw a five by five grid in Graphics 0
;(lines 60-170 in the BASIC program)
BYTE
row, col ;variables LOCAL to this procedure
FOR
row=1 TO 5 ;line 70
DO
FOR
col=1 TO 5 ;line 80
Print("
") ;line 90
IF
col=b_col AND row=b_row THEN ;line 100
Print("B")
ELSEIF
col=h_col AND row=h_row THEN ;line 110
Print
"H")
ELSE
Print(".") ;line 140
FI
OD ;line 150
PutE() ;line 160
RETURN
Now the
section to allow the human's input and movement:
PROC Move_Human()
;Human movement section
;(lines 180-300 in the BASIC program)
BYTE move ;"move"
is LOCAL to this procedure
PutE()
PutE()
Print("Move
number ") ;line 190
PrintBE(plays)
Print("Direction
(NSEW)a")
Move=GetD(7) ;line 200
PrintF("%C",move) ;we want it printed as a Character
old_col=h_col ;save these variables in case
old_row=h_row ;the human goes out of bounds
IF
move='N OR move='n THEN
h_row==-1 ;move him up (line 210)
ELSEIF
move='S or move='s THEN
h_row==+1 ;move down (line 220)
ELSEIF
move='E or move='e THEN
h_col==_1 ;move right (line 230)
ELSE
h_col==-1 ;move left (line 240)
FI
IF
h_col<1 OR
h_row<1 OR
h_col>5 OR
h_row>5 THEN
h_col=old_col ;out of bounds
h_row=old_row ;correction
(lines 250-300)
FI
RETURN
The procedure to move the butterfly:
PROC
Move_Fly()
;Procedure to move butterfly
;(Lines 330-600)
BYTE
b_dir ;LOCAL to this procedure
IF
b_col>h_col AND b_row=h_row THEN
b_dir=1 ;line 380
ELSEIF
b_col>h_col AND b_row>h_row THEN
b_dir=2 ;line 390
ELSEIF
b_col<h_col AND b_row>h_row THEN
b_dir=3 ;line 400
ELSEIF
b_col<h_col AND b_row>h_row THEN
b_dir=4 ;line 410
ELSEIF
b_col<h_col and b_row=h_row THEN
b_dir=5 ;line 420
ELSEIF
b_col<h_col AND b_row<h_row THEN
b_dir=6 ;line 430
ELSEIF
b_col=h_col AND b_row<h_row THEN
b_dir=7 ;line 440
ELSE
b_dir=8 ;line 450
FI
Change=Rand(10) ;line 470 – "Rand" is a library function
;to be discussed in a later chapter
IF
change=1 THEN
b_dir==+1 ;line 480
ELSEIF
change=0 THEN
b_dir==-1 ;line 490
FI
IF
b_dir=0 THEN
b_dir=8 ;line 500
ELSEIF
b_dir=9 THEN
b_dir=1 ;line 510
FI
old_col=b_col
old_row=b_row
IF
b_dir=1 THEN
b_col==+1 ;move it to the right (line 530)
ELSEIF
b_dir=2 THEN
b_col==+1 ;move it to the right
b_row==+1 ;and down (line 540)
ELSEIF
b_dir=3 THEN
b_row==+1 ;move it down (line 550)
ELSEIF
b_dir=4 THEN
b_col==-1 ;move it to the left
b_row==+1 ;and down (line 560)
ELSEIF
b_dir=5 THEN
b_col==-1 ;move it to the left (line 570)
ELSEIF
b_dir=6 THEN
b_col==-1 ;move it to the left
b_row==-1 ;and up (line 580)
ELSEIF
b_dir=7 THEN
b_row==-1 ;move up (line 590)
ELSE
b_col==+1 ;move right
b_row==-1 ;and
up (line 600)
FI
IF
b_col<1 OR
b_row<1 OR
b_col>5 OR
b_row>5 THEN
b_col=old_col ;correction for out of bounds
b_row=old_row ;(lines 620-660)
FI
RETURN
We need a procedure to check the status of the human and
the butterfly to see if they are trying to occupy the same space at the same
time:
PROC
Check_Stat()
;Procedure to see if the butterfly has been caught –
;serves the same function as lines 320 and 680 in BASIC
;No local variables
IF
(h_row=b_row) AND (h_col=b_col) THEN ;lines 320 and 680
PutE()
PrintE("You got
him!") ;line 700
Caught=1 ;the "flag" to indicate a catch
FI
RETURN
Now the procedure to offer another
game:
PROC
More_Play()
;This procedure determines if the player wants to quit or play
again
;See lines 730-750 in BASIC
;No local variables
PutE()
PrintE("Do
you want to play")
Print("another
game (Y/N)?") ;line 740
Decide=GetD(7) ;line 740
PrintF("%C",decide) ;line 750
PutE()
If
decide='Y or decide='y THEN
Decide=1 ;program will run again after the RETURN
ELSE
decide=0 ;program will end after the RETURN
FI
RETURN
And last,
Procedure "Main" – the procedure that controls the whole program:
PROC
(Main()
DO
B_row=3 ;initial conditions (line 30)
B_col=3
H_row=1
H_col=1
Caught=0
Decide=0
FOR plays=1 to 10 ;(line 50)
DO
Draw_Map() ;call Procedure "Draw_Map" (lines 60-170)
Move_Human() ;call Move_Human (lines 180=300)
Check_Stat() ;call Check_Stat (line 320)
IF
caught=1 THEN ;"caught" is set by Procedure Check_Stat
EXIT ;get out of the loop
FI
Move_Fly() ;call Move_Fly (lines 330-600)
Check_Stat() ;call Check_Stat (line 680)
IF
caught=1 THEN
EXIT
FI
PutE() ;print a line with carriage return
OD
IF
caught=0 THEN
PutE()
PrintE("He
escaped!") ;line 720
FI
More_Play() ;call More_Play (lines 730-750)
UNTIL decide=0 ;no more games
OD
RETURN
Let's take Procedure "Main" step by
step.
There are two "DO" loops in this
procedure, one nested inside the other. We'll look at the nested loop first and
then we can work back to the original one.
Right after "FOR plays=1 TO 10" is
the nested loop. It tells the program to do the following:
1. Draw the map
2. Let the human make a move
3. Check to see if the human caught
the butterfly and EXIT if he did
4. Print a blank line
This loop will execute 10 times (10
plays) or until the butterfly is caught. The outer loop controls the inner loop
as well as a few other things:
1.
It
sets up the initial conditions – positions of human and butterfly –
and initializes the variables "caught" and "decide" to 0.
2.
It
tells you you lost if all 10 plays have passed without a "catch".
3.
It
asks if you want another game.
4.
It
does all this UNTIL you say "No" to another game.
We need to add one more section of
code and our program will be ready to compile and run. This section is called
simply "MODULE" and all it does is tell the compiler that we want to declare
some global variables.
MODULE
;declaring global variables
BYTE
h_col, h_row, b_col, b_row, old_col, old_row, plays, caught, decide
Go ahead and type this program in if
you haven't already done so. Make sure that MODULE is the first piece of code,
and Main the last. Otherwise the procedures can go in any order you like.*
After you have typed in the program in the Action! Editor and saved it <CTRL-SHIFT-W>, use <CTRL-SHIFT-M> to go to the Monitor, Compile the program, and Run it. If you have typed it in exactly as you see it here, it will compile and run without errors. If you do get an error, proofread your source carefully and make any necessary corrections, save the revised program, and try it again.
There's your first Action! program. To test what you've
learned, try making the following changes:
1.
Enlarge
the grid.
2.
"Teach"
the butterfly not to let himself get cornered.
3.
Allow
the human to make moves diagonally
4.
Make
the starting positions random – include code to keep the human and
butterfly from starting out in the same place or too close (within 1 square).
*NOTE:
This is not always the case. In more complex programs, where routines call
other routines, you will have to ensure that a procedure to be called precedes the calling procedure (or function).
We will cover this in detail in a later chapter.