Bottom     Previous     Contents

CHAPTER 3
Turtle Graphics III

Whatever colors you have in your mind I'll show them to you and you'll see them shine

Bob Dylan, "Lay, Lady, Lay"

The next obvious extension to TG is to have more than one turtle, with each turtle drawing lines of a specific colour -- a colour which might be different from the other turtles. We need a four colour mode: one colour for the background, and three colours for the turtles.
Mode 1 takes up more memory, though with an increase in resolution, so we will normally use mode 5 to allow more space for complex applications. The question is then which four colours? (and then how to control three different turtles at once). The choice of colours, and the three turtles, incorporates the distinction between 'actual' and 'logical' colours.

Choosing colours

There are four colours available in modes 1 or 5 at any one time. These are the four 'logical' colours labelled 0, 1, 2, and 3: normally these colours are black, red, yellow, and white. Black has an 'actual' colour number 0, red is 1, yellow is 3 and white is 7 (see UG pages 162-168, 222-224, and 262).
It is possible, therefore, to change the logical colour 0 from black (actual colour 0) to flashing cyan-red (actual colour 14), by assigning the logical colour to a new actual colour number. The logical number is like the address of a house, which never varies, but whose occupant (the actual colour) may vary, without the house number altering.
The logical numbers 0 to 3 are 'foreground' colours, they define the colours of the text, or fines in graphics: corresponding to logical foreground colour 0 is background colour 128, and for 3 there is 131. If we printed in logical 2 on a logical 130, we would not be able to see what had been printed (text and background would be the same actual colour).
The normal association of logical and actual colours for mode 5 (as well as mode 1) is shown in Figure 3.1.

Figure 3.1 Colours in Modes 1 and 5

LOGICAL ACTUAL COLOUR
NUMBER NUMBER
0 128 0 Black
1 129 1 Red
2 130 3 Yellow
3 131 7 White

To change the association we use another VDU command (UG pages 224, 382)

VDU 19, logicalnumber, actualnumber, 0, 0, 0

that is:

VDU 19, logicalnumber, actualnumber; 0;

where the second sends two bytes at a time by use of ";". To understand, an example might help -- Mode 5

1000 COLOUR 131 : CLS : REM SET THE BACKGROUND TO

WHITE (LOGICAL 3)

1010 REPEAT COLOUR 0 : PRINT "********************" :

REM PRINT IN FIRST LOGICAL COLOUR

1020 COLOUR I : PRINT "********************'' : REM AND

SECOND

1030 COLOUR 2 :PRINT "********************" : REM AND

THIRD

1040 INPUT LGICAL, ACTUAL : REM COLOUR NUMBERS

1050 VDU 19,LGICAL,ACTUAL;0; : REM WATCH THE

OUTPUT CHANGE COLOUR

1060 UNTIL FALSE : REM AND AGAIN

This little program helps you to investigate the effects of various actual colours, in differing combinations. An important consideration with colour graphics is how well the different colours can be distinguished on a monochrome television. (My colour TV is used for watching TV, most of the time).
By dint of much playing around with the above program, I came to the conclusion that I would use the following logical and actual associations

VDU 19, 0, 7; 0;
VDU 19, 1, 1; 0;
VDU 19, 2, 6; 0;
VDU 19, 3, 3; 0;

that is:

Figure 3.2 New colour associations

LOGICAL
NUMBERS
ACTUAL
NUMBER
COLOUR
0 128 7 White
1 129 1 Red
2 130 6 Cyan
3 131 3 Yellow

I propose to use 131 for the graphics background (ie yellow), and £29 for the text background (ie red); 0, 1, and 3, for graphics foreground (the three turtles), and 0 (ie white) for the text. The means to change these assignments are provided -- for graphics -- within the program.

The three turtles

Within the programs for the first version of turtle graphics, there were four global variables: X, Y, ANGLE and PEN. Though some of the other routines have to be slightly altered to cope with mode 5 rather than mode 4 (eg PROC_CLS) not much else needs great alteration -- as long as it is remembered which turtle is which.
The way I propose to code the routines is to have only one set of routines, without any extra parameters for whichever turtle is to be used. The reason I intend to implement the graphics in this way is universality: all I want to say is PROC_CIRCLE(SIZE) for the routine to work with whichever turtle is being used at that time.
To have routines such as PROC_CIRCLE(SIZE, T_NUMBER) makes the whole programming process far more tedious than needs be. There will be special procedures to define the turtle to be used; and once so defined, the definition will stay until another turtle is defined. We will, however, within the basic TG routines have to keep track of where each turtle is located, and in which direction it faces.
We need at least one extra global variable: TURTLE, which holds the number of the turtle being used at that time (0, I, or 2). Instead of PEN we need three PENs, one for each turtle, just as we need three Xs, three Ys, and three ANGLEs. This all sounds like the place to use arrays, and it is for X, Y , and ANGLE -- but not for PEN. The difference comes from the fact that PEN is a small integer less than 255 (it can fit in a byte), whereas X, Y , and ANGLE can be fractional numbers.
We need to have three examples of each of the above, where PEN is a set of three small integers, and the others are sets of three fractional numbers. We set aside space for these sets of three by a BB statement such as

1000 DIM PEN 2, X(2), Y(2), ANGLE(2)

and we refer to each element in this manner:

Figure 3.3 TURTLE global variables

TURTLE PEN X Y ANGLE
0 PEN?0 X(0) Y(0) ANGLE(0)
1 PEN?1 X(1) Y(1) ANGLE(1)
2 PEN?0 X(2) Y(2) ANGLE(2)

Note that PEN, which was 'dimensioned' differently to the others, and is different to the others in the way in which elements are named. The I'th element of PEN is shown by PEN? I, whereas the I'th element of ANGLE is shown by ANGLE(I). PEN is a 'byte vector' (see UG pages 237, 409-413), whereas ANGLE is an 'array' (see UG pages 120-- 125, 236): PEN is effectively a succession of bytes, and ANGLE is a succession of real numbers.
If we are using a turtle called TURTLE, (where TURTLE is the logical number) then to use the correct actual colour for that wee beastie we say PEN?TURTLE; the turtle is at coordinates X(TURTLE), Y (TURTLE) facing in a direction ANGLECTURTLE). TURTLE always denotes the logical colour number, which is why 3 and 131 refer to the background logical colour (TURTLE lies between 0 and 2).

The new routines

The routines in Version 2.1 are similar but different. For a start, as arrays and vectors have to be dimensioned, we start (using PROC_START) by running the short program part. We could have started Versions 1.1 and 1.2 in this manner, but with arrays and vectors this is the cleanest way. Here goes.

1000REM-------------------------------

1010

1020

1030REM G R A P H I C ART

1040

1050REM (c) Boris Allen, 1983

1060

1070

1080REM-------------------------------

1090

1100REM Turtle Graphics : 2.1

1110

1120REM-------------------------------

1130

1140 REM MAIN PROGRAM

1150 DIM PEN 2, CLR 2, X(2), Y(2), ANGL

E(2)

1160 PEN?0 = 7 : PEN?1 = 1 : PEN?2 = 6

: BACK = 3

1170 FOR I = 0 TO 2

1180 VDU 19, I, PEN?I; 0; : CLR?I = PEN

?I

1190 X(I) = 0 : Y(I) = 0 : ANGLE(I) = 0

1200 NEXT I

1210 VDU 19, 3, BACK; 0;

1220 PROC_TURTLE(0) : PROC_CLRSCR

1230 END : REM OF MAIN PROGRAM

1240

1250 DEF PROC_CLRSCR

1260 PROC_CLS : PROC_CLG

1270 ENDPROC : REM CLRSCR

1280

1290 DEF PROC_CLG

1300 GCOL 0, TURTLE : GCOL 0, 128+BACK

1310 VDU 24, 0; 128; 1279; 1023;

1320 VDU 29, 640; 566; : CLG

1330 MOVE 0,0

1340 ENDPROC : REM PROC_CLG

1350

1360 DEF PROC_CLS

1370 COLOUR 0 : COLOUR 129

1380 VDU 28, 0, 31, 19, 28 : CLS

1390 REM It is VDU 28, 0, 31, 39, 28 :

CLS for mode 1

1400 ENDPROC : REM PROC_CLS

1410

1420 DEF PROC_TURTLE(LGCL)

1430 TURTLE = (LGCL MOD 3 + 3) MOD 3 :

GCOL 0,TURTLE

1440 MOVE X(TURTLE),Y(TURTLE)

1450 ENDPROC : REM TURTLE

1460

1470 DEF PROC_START

1480 RUN

1490 ENDPROC : REM START

1500

1510 DEF PROC_COL(ACT)

1520 VDU 19, TURTLE, ACT; 0;

1530 ENDPROC : REM COL

1540

1550 DEF PROC_CENTRE

1560 MOVE 0,0 : ANGLE(TURTLE) = 0

1570 X(TURTLE) = 0 : Y(TURTLE) = 0

1580 ENDPROC : REM CENTRE

1590

1600 DEF PROC_RESTART

1610 LOCAL I

1620 FOR I=2 TO 0 STEP -1

1630 PROC_TURTLE(I) : PROC_CENTRE

1640 NEXT I : PROC_CLG

1650 ENDPROC : REM RESTART

1660

1670 DEF PROC_INVERT

1680 IF PEN?TURTLE = CLR?TURTLE THEN PE

N?TURTLE=BACK ELSE PEN?TURTLE=CLR?TURTLE

1690 PROC_COL(PEN?TURTLE)

1700 ENDPROC : REM INVERT

1710

1720 DEF PROC_TURNTO(A)

1730 ANGLE(TURTLE) = FN_ANGLE(A)

1740 ENDPROC : REM TURNTO

1750

1760 DEF PROC_LOC

1770 PRINT "TURTLE "TURTLE

1780 PRINT "COORDS ";INT(X(TURTLE)+.5);

" ";INT(Y(TURTLE)+.5)

1790 PRINT "ANGLE "INT(ANGLE(TURTLE)+.5

)

1800 ENDPROC : REM LOC

1810

1820 DEF PROC_TURN(A)

1830 ANGLE(TURTLE) = FN_ANGLE(ANGLE(TUR

TLE)+A)

1840 ENDPROC : REM TURN

1850

1860 DEF PROC_MOVE(DISTANCE,STYLE)

1870 X(TURTLE) = X(TURTLE) - DISTANCE*S

IN(RAD(ANGLE(TURTLE)))

1880 Y(TURTLE) = Y(TURTLE) - DISTANCE*C

OS(RAD(ANGLE(TURTLE)))

1890 IF STYLE=0 THEN MOVE (X(TURTLE),Y(

TURTLE) ELSE PLOT (STYLE-1)+5, X(TURTLE)

,Y(TURTLE)

1900 ENDPROC : REM MOVE

1910

1920 DEF PROC_MOVETO(XN,YN,STYLE)

1930 LOCAL XDIF,YDIF : XDIF = XN-X(TURT

LE) : YDIF = Y(TURTLE)-YN

1940 IF YDIF<0 THEN PROC_TURNTO(DEG(ATN

(XDIF/YDIF))+180*(YN<Y(TURTLE))) ELSE PR

OC_TURNTO(SGN(-XDIF)*90)

1950 X(TURTLE) = XN : Y(TURTLE) = YN

1960 IF STYLE=0 THEN MOVE X(TURTLE),Y(T

URTLE) ELSE PLOT (STYLE-1)+5, X(TURTLE),

Y(TURTLE)

1970 ENDPROC : REM MOVETO

1980

1990 DEF FN_ANGLE(A)

2000 IF A MOD 360 <0 THEN =A MOD 360 +

360 ELSE = A MOD 360 : REM ANGLE

PROC_START (ie main program) dimensions two vectors (ie PEN and CLR), and three arrays (ie X, Y, and ANGLE): all have three elements (ie 0, 1, and 2). PEN?() (corresponds to first turtle) is set to actual colour number 7 (white); the second turtle is set to colour I (red), and the third turtle is set to colour 6 (cyan); and the BACKground colour is set to 3 (yellow).
The actual colours are assigned to the logical numbers by use of the VDU 19 command; the original CoLouR is remembered (used in PROC_INVERT); and the coordinates and angle are initialised to zero for all turtles. The background colour is set by the VDU 19, 3, BACK; 0; command; the initial turtle is set (by PROC_TURTLE) to be turtle 0, and the screen is set up by PROC_CLRSCR.
PROC_CLRSCR merely calls the two routines PROC_CLS and PROC_CLG.
PROC_CLG first sets the current logical colour to the operative turtle number, and sets the background to the operative background number (altering the value of BACK can produce some interesting effects). The rest of the routine matches that of the monochrome version.
PROC_CLS sets colours for text and background (white on red) and sets up a text window for mode 5 : if the VDU 28 command is kept the same as that in the first version, it is possible to use mode 1.
Next in the (boring?) list is PROC--TURTLE, which takes as parameter the new LoGiCaL number for the graphics, and makes that the new TURTLE number. The equation with MOD is to account for negative values -- FN_ANGLE uses a different method -- in case by chance out of bounds numbers are given. This routine is the one used to change from one turtle to another, and so the graphics cursor is moved to the last coordinates for that turtle (ie X(TURTLE), Y(TURTLE).

3000REM-------------------------------

3010

3020

3030REM G R A P H I C ART

3040

3050REM (c) Boris Allen, 1983

3060

3070

3080REM-------------------------------

3090

3100REM Turtle Routines : 2.1

3110

3120REM-------------------------------

3130

3140 DEF PROC_SQTURN(F)

3150 LOCAL I,A$

3160 FOR I=0TO2 : PROC_TURTLE(I): PROC_

TURNTO(120*I):NEXT I

3170 REPEAT : PROC_TURTLE(I MOD 3)

3180 PROC_TURN(90) : PROC_MOVE(F*I,0)

3190 PROC_SQUARE(F*I) : I = I+1 : UNTIL

INKEY(-68)

3200 *FX15,0

3210 ENDPROC : REM SQTURN

3220

3230 DEF PROC_SQUARE(SIDE)

3240 LOCAL I : FOR I = 1 TO 4

3250 PROC_MOVE(SIDE,1) : PROC_TURN(90)

3260 NEXT I

3270 ENDPROC : REM SQUARE

3280

3290 DEF PROC_MOIRE

3300 LOCAL I

3310 FOR I=0 TO 1079 : PROC_TURTLE(RND(

3)-1) : PROC_CENTRE : PROC_TURNTO(I/3)

3320 PROC_MOVE(400,1) : NEXT I

3330 ENDPROC : REM MOIRE

3340

3350 DEF PROC_INITIALIZE

3360 REMDIM D(2)

3370 PROC_TURTLE(0): PROC_MOVETO(-32,-3

68,0) : PROC_TURNTO(0)

3380 PROC_TURTLE(1): PROC_MOVETO(32,-36

8,0) : PROC_TURNTO(0)

3390 D(0) = 10 : D(1) = 10 : VDU 7

3400 ENDPROC : REM INITIALIZE

3410

3420 DEF PROC_DEVIATION

3430 PROC_TURTLE(1): PROC_TURN(90*INKEY

(-89)-(INKEY(-88)))

3440 PROC_TURTLE(0) : PROC_TURN(90*INKE

Y(-51)-(INKEY(-66)))

3450 ENDPROC : REM DEVIATION

3460

3470 DEF PROC_ACCELN

3480 D(0) = D(0) - 18*INKEY(-82) : D(1)

= D(1) - 18*INKEY(-73)

3490 ENDPROC : REM ACCELN

3500

3510 DEF PROC_TRAVEL

3520 LOCAL I%

3530 PROC_DEVIATION : PROC_ACCELN

3540 I% = RND(1) +.5

3550 PROC_GO(I%) : PROC_GO(1-I%)

3560 ENDPROC : REM TRAVEL

3570

3580 DEF PROC_GO(I)

3590 LOCAL J : FOR J = 1 TO 8

3600 PROC_DEVIATION

3610 PROC_TURTLE(I)

3620 PROC_MOVE(D(I),1)

3630 PROC_MOVE(4,0):IF POINT(X(TURTLE),

Y(TURTLE))<>3 THEN PROC_END

3640 PROC_MOVE(-4,0) : REM Note that it

is -4 for mode 1, -8 for mode 5

3650 PROC_TURTLE(1-I)

3660 PROC_MOVE(D(1-I),1)

3670 PROC_MOVE(4,0):IF POINT(X(TURTLE),

Y(TURTLE))<>3 THEN PROC_END

3680 PROC_MOVE(-4,0) : NEXT J : REM See

remark above

3690 ENDPROC : REM GO

3700

3710 DEF PROC_END

3720 VDU 7 : VDU 7

3730 PRINT "TURTLE ";TURTLE;" LOSES ":

VDU 7

3740 FOR I = 1 TO 500 : NEXT I : *FX15,

0

3750 END

3760

3770 DEF PROC_NORT

3780 LOCAL A$ : A$ = GET$

3790 PROC_INITIALIZE

3800 REPEAT PROC_TRAVEL

3810 UNTIL FALSE

3820 ENDPROC : REM NORT

Some highly interesting results can be achieved by changing the actual colours of the turtles, and PROC_COL is the means by which the change is made -- see Figure 3.4 -- on this, more later.
PROC_CENTRE centres the current turtle (it does not affect the other two turtles).
PROC_RESTART centres all turtles and clears graphics by PROC_CLG: a routine without PROC_CLG would centre all turtles without affecting the display.
The operation of PROC_INVERT is rather different from the routine of the same name in the monochrome version. Whereas the monochrome version changes the plotting mode from foreground to background colour (or vice versa), this routine either hides all the plotting of the turtle -- thus far -- or makes it all reappear again.
If the present actual colour of the PEN is the same as the original colour (CLR?TURTLE) then the new actual colour becomes that of the background, or else the colour of the PEN becomes the same as the original colour. This is a rather useful way of hiding a piece of plotting, for the plotting to suddenly appear, as if by magic.
PROC_TURNTO is the first of the TG routines proper (the rest being housekeeping, more or less). All it does is make the present angle for the present turtle equal to the parameter of the procedure (within 0 to 359). PROC_TURN is as simple in operation.
PROC_LOC has had to be modified to fit on the lesser space of mode 5: the use of INT is also to reduce space. Note the addition of the information which tells which is the operative turtle.
The main difference in PROC_MOVE (apart from distinguishing between turtles), is to do with the STYLE of plotting. If the style is 0 then a move does not plot; if 1 then plot in the normal turtle colour; and if the style is 3 then plot in the background colour (ie erase, or the old version of PROC_INVERT). The different use of STYLE is again the only real change to PROC_MOVETO. FN_ANGLE is unchanged.
Finally here is the list of actual colour numbers, and the colours to go with the number.

Figure 3.4 Actual Colours and Numbers

ACTUAL NUMBER COLOUR
0 Black
1 Red
2 Green
3 Yellow
4 Blue
5 Magenta
6 Cyan
7 White
8 Flashing BIack/White
9 Flashing Red/Cyan
10 Flashing Green/Magenta
11 Flashing Yellow/B1ue
12 Flashing BIue/Yellow
13 Flashing Magenta/Green
14 Flashing Cyan/Red
15 Flashing White/Black

Example pictures

Before the game is designed, here are two different examples of coloured TG.
Though the name is the same as a Version I routine, PROC_SQTURN is different. For a start PROC_SQTURN in the coloured version has a parameter (F), but first examine the routine. Each turtle is turned to a different angle, turtle0 to 0 degrees, 1 to 120 degrees, and 2 to 240 degrees (the first loop).
For a large number of times (1800) a turtle is chosen (the loop index MOD 3), this turtle is turned through 90 degrees from where it was previously, it is moved through a distance F*I (without plotting), and then a square of side F*I is drawn -- by use of PROC_SQUARE. PROC_SQUARE is a simple routine to draw a square. An example is shown in Icon 3.1, but it does not do justice to the multi-coloured effects.

This routine produced multi-coloured effects, of a varying nature, depending upon the value of F . Further effects can be investigated by using the combination

PROC_TURTLE(A_VALUE) :
PROC_COL(ANOTHER_VALUE)

The various values of ANOTHER_VALUE can be chosen with the assistance of Figure 3.4.
The other example is a circular Moire demonstration: PROC_MOIRE. In the loop (which is activated 3*360 times), a TURTLE is chosen at random; the turtle is centred; it turns to 1/3 degrees (I is the loop counter); and the turtle moves 400 units forward. A multi-coloured circle is drawn. Modifying the PROC-JFURTLE parameter, eg

PROC_TURTLE((I DIV 3) MOD 3)

(though the MOD 3 is not necessary), produces various intriguing effects. As with the PROC_SQTURN, using PROC_COL can produce startling effects. The effects, as shown in Icon 3.2, are far better in colour than in monochrome -- my screen dump routine only works in black and white.

Both routines show how easy it is to program with three coloured turtles.

The game

One of the less important reasons for producing multi-coloured graphics is the design of video games -- the most important reason is the pure delight of experimenting with artistic effects. So here is a game.
The game is called NORT, and involves only two of the three turtles. It is a turtle race, but remember these turtles are cybernetic beasties and so can move pretty speedily. The trail that each turtle leaves is noxious, and so to travel over your own or your opponent's trail is calamitous. You lose.
The two turtles start out side by side, at the same speed: the left turtle is controlled by the 'A' , 'S' , 'D' ,keys; and the right turtle is controlled by the ';' , ':' , ']' , keys. In each case the left key turns the turtle through 90 degrees to the left, the fight key turns the turtle through 90 degrees to the fight, and the„ middle key speeds up the turtle. Once a turtle has speeded up then it stays at that speed (it does not slow down), though it is always possible to increase the speed further.
The faster the turtle goes, the easier it is to cut across the other turtle, but the easier it is to get out of control. As you will realise, this game is extremely simple to implement using coloured TG.
I do not claim that this is a definitive game, and it can stand much improvement, but it indicates what can be done. An improved method of reading the keyboard, and controlling the turtle racers would greatly help. To the routines.
The first routine, in order, is PROC_INITIALIZE: the distances the turtles have to move are stored in the array D which has two elements, D(0) and D(1). The first turtle (ie 0) is moved to its starting point at -2, -368, and turned to face upwards. The second turtle (ie 1) is moved to 32, -368, facing upwards, and the initial speeds are set at 12 (ie D(0) and D(1)). There is a starting beep (VDU 7).
PROC_DEVIATION senses the keyboard for both turtles, and turns accordingly (uses PROC_TURN and INKEY()).
Another short routine is PROC_ACCELN, which modifies the two speeds (only upwards) by sensing the keyboard via INKEY().
PROC_TRAVEL has a local variable I%, and establishes the deviation and speed before setting I% randomly to 0 or 1. The routine PROC_GO is called with I% as parameter, and this establishes the order in which the two turtles are moved.
The turtles move in eight segments -- so sometimes it is possible to 'jump' over a trail. These eightjumps are produced by the loop J = 1 TO 8, and at each jump the keyboard is sensed, and then the turtles jumped in order -- given by the parameter to the routine. After each jump, there is a move forward of four units (without plotting), and that location is checked to see if the colour there is not logical 3 (the background). If that square is the wrong colour, then the game ends. If the colour is that of the background, then there is a move (back) of -4 units.
PROC_END double beeps (VDU 7), tells you who has lost, waits and then flushes the buffers (*FX15,0).
To start the game you have to enter RUN and then PROC_NORT: the game commences with the press of a key (GET$), when we PROC_INITIALIZE and then REPEAT PROC_TRAVEL forever -- or at least until we hit PROC_END.
The two trails shown in Icon 3.3 give some idea of the courses of a typical game. The game is more effective in colour than in black and white, though quite possible to follow because of the colours chosen.


Next     Top