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.
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.
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 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 |
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.
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.