Virtual Big Trak Based on the toy of the same name, the Virtual Big Trak application requires the student to program a 3D tank to navigate a simple obstacle course.
Commands are entered into a text file using a standard text editor and loaded into the application. Fn = Forward n units Bn = Back (reverse) n units Ln = Rotate left n degrees Rn = Rotate right n degrees Points are deducted for entering grid squares with a diamond tile and for leaving the 8 x 8 grid. The challenge is to reach the final destination square with the least penalties, having covered the shortest distance. Additional courses/maps can be designed, very simply, using a text editor. Each grid square is 10 units high and 10 units wide. N W
E S
Fig 1
'Course A.txt'
00000003 00000000 00111111 00000000 00000000 11111100 00000000 20000000 Fig 2
1
Figure 1 shows the 'Course A.txt' graphics created by the contents of the text file (figure 2). 0 (zero) = clear tiles 1 = tile to be avoided 2 = starting position of Big Trak 3 = destination tile Each grid square is 10 units in width and height, so the entire grid is 80 units by 80 units. The image below shows a solution for 'Course A'.
Big Trak starts in the centre of the designated square i.e. 5 units up and 5 units to the right of the bottom left corner. If a map doesn't contain '2' or '3', the start and end positions default to the bottom left and top right tiles, respectively. Points are deducted when the Big Trak's centre moves off the grid or into a grid square with a diamond tile. An additional application is included to calculate the approximate bearing and distance required to move Big Trak by x,y units. (Of use if the student wishes to minimise the distance travelled.)
2
BB4W – Virtual Big Trak REM Prevent resize SYS "GetWindowLong", @hwnd%, -16 TO ws% SYS "SetWindowLong", @hwnd%, -16, ws% AND NOT &40000 AND NOT &10000 MODE 9:OFF *ESC OFF *FONT Arial,16 click=FALSE ON MOUSE:click=TRUE:RETURN ON ERROR:PROCerror:END *REFRESH OFF ORIGIN 640,512 VDU 23,23,2;0;0;0;:REM Line width 2 REM File handling DIM Of% 75,filetype% 40,Fn% 255 !Of%=76:Of%!4=@hwnd%:Of%!28=Fn%:Of%!32=256:Of%!52=6:Of%!12=filetype% $filetype%="Text files"+CHR$0+"*.txt"+CHR$0 DIM btn{(9) sts,x,y,name$}:REM Buttons sptr=0:REM Button pointer DIM nd{(54) xt,yt,zt,xr,yr,zr,x2,y2}:REM Nodes DIM link{(90) type,n1,n2,n3}:REM Links (colours & triangles) DIM td{(3) np,lp,nc,lc}:REM Net pointers FOR a=0 TO 54 READ nd{(a)}.xt,nd{(a)}.yt,nd{(a)}.zt NEXT FOR a=0 TO 90 READ link{(a)}.type,link{(a)}.n1,link{(a)}.n2,link{(a)}.n3 NEXT FOR a=0 TO 3 READ td{(a)}.np,td{(a)}.lp,td{(a)}.nc,td{(a)}.lc NEXT REM Create a unit vector pointing towards a light source lightx=100:lighty=100:lightz=10 lm=SQR(lightx^2+lighty^2+lightz^2) lightx/=lm:lighty/=lm:lightz/=lm vX=0:vY=0:vZ=0:REM View point (calculating distances) vXA=0:REM Viewer's x-axis rotation MAXnav=99 DIM nav{(MAXnav) inst$,units}:REM User's program nptr=-1:REM Program line pointer DIM map(7,7):REM 0=good tile, 1=bad tile DIM omega{(1) x,y}:REM Start & End locations mfile$="":REM Map file name havemap=FALSE:REM Course loaded 3
havenav=FALSE:REM Program loaded status=0:REM 0=need map&pgm, 1=can start, 2=roll scene pause=FALSE nfile$="":REM Nav file name cinst=0:REM Current instruction cunit=0:REM Current unit count cx=0:cy=0:cbear=0:REM Current x,y & bearing gx=0:gy=0:REM Current integer grid co-ordinates score=0:REM Penalties distance=0:REM distance travelled complete=FALSE:REM Destination reached? PROCmakebutton(1,-500,460,"Map") PROCmakebutton(1,-500,380,"Nav") PROCmakebutton(1,300,460,"Start") PROCmakebutton(0,300,460,"Pause") PROCmakebutton(1,500,460,"Reset") REPEAT TIME=0:CLS MOUSE mx,my,mb REM Update status CASE status OF WHEN 0 IF havemap AND havenav status=1 IF NOT havemap THEN GCOL 0,7 *FONT Arial,20 PROCcentre(0,100,"Virtual Big Trak") *FONT Arial,16 vX=0:vY=0:vZ=0:vXA=0 PROCdraw(0,0,-10,-60,0,0,cbear):cbear+=3 ENDIF WHEN 2 IF vXA<-15 vXA+=3 ELSE status=3 WHEN 3 : REM Execute instruction IF NOT pause THEN cunit+=1 IF cunit<=nav{(cinst)}.units THEN CASE nav{(cinst)}.inst$ OF WHEN "F" cx+=SIN(RAD(cbear)):cy-=COS(RAD(cbear)) IF NOT complete distance+=1:score+=FNpenalty WHEN "B" cx-=SIN(RAD(cbear)):cy+=COS(RAD(cbear)) IF NOT complete distance+=1:score+=FNpenalty WHEN "L" : cbear-=1:IF cbear<0 cbear+=360 WHEN "R" : cbear+=1:IF cbear>=360 cbear-=360 ENDCASE 4
ENDIF IF cunit>=nav{(cinst)}.units THEN cinst+=1:cunit=0 IF cinst>nptr status=1:REM End of program ENDIF ENDIF IF pause GCOL 0,7:PROCcentre(300,380,"Paused") ENDCASE GCOL 0,7 IF havemap t$=mfile$ ELSE t$="Click to load map" PROCleft(-400,460,t$) IF havenav t$=nfile$ ELSE t$="Click to load navigation program" PROCleft(-400,380,t$) IF havemap PROCquickdraw PROCdrawbuttons REM If board is 'flat' then show score IF vXA=-15 THEN GCOL 0,7 PROCleft(-400,300,"Distance moved: "+STR$(distance)) PROCleft(-400,220,"Bearing: "+FNbearing) PROCleft(-400,140,"Penalties: "+STR$(score)) IF complete PROCleft(-400,60,"Mission Complete!") ENDIF *REFRESH IF havemap WHILE TIME<4:ENDWHILE ELSE WAIT 4-TIME UNTIL 0 END REM Draw the 3D scene: Tiles & Big Trak DEF PROCquickdraw LOCAL x,y,net vX=40:REM Calculate current point of view from 'pitch' of view: vXA vY=SIN(RAD(-vXA))*120+25 vZ=COS(RAD(-vXA))*120+40 REM Draw tiles FOR y=0 TO 7 FOR x=0 TO 7 net=-1:IF x=omega{(1)}.x AND y=omega{(1)}.y net=3 IF net=-1 THEN IF map(x,y)=0 net=1 ELSE net=2 ENDIF PROCdraw(net,x*10+5,0,y*10+5,0,0,0) NEXT NEXT REM Draw Big Trak PROCdraw(0,cx,0,cy,0,0,cbear) 5
ENDPROC DEF PROCerror CLS *REFRESH ON REPORT PRINT''"You've broken me." PRINT '"Type RUN and press ENTER, or close the window to quit." ENDPROC REM Reset run DEF PROCreset cx=omega{(0)}.x*10+5 cy=omega{(0)}.y*10+5 cbear=0:cinst=0:cunit=0 score=0:distance=0 pause=FALSE:IF status>1 status=1:REM Need to click Start complete=FALSE ENDPROC REM Check for off-grid or bad-tile penalties DEF FNpenalty LOCAL v:v=0 gx=INT(cx+.5):gy=80-INT(cy+.5) IF gx<0 OR gx>=80 OR gy<0 OR gy>=80 THEN v=1 ELSE IF map(gx DIV 10,7-(gy DIV 10))=1 v=1 IF (gx DIV 10)=omega{(1)}.x AND 7-(gy DIV 10)=omega{(1)}.y complete=TRUE ENDIF =v DEF FNbearing =STRING$(3-LEN(STR$(cbear)),"0")+STR$(cbear) REM ******************************************************* REM Buttons REM ******************************************************* DEF PROCmakebutton(status,x,y,name$) btn{(sptr)}.sts=status btn{(sptr)}.x=x btn{(sptr)}.y=y btn{(sptr)}.name$=name$ sptr+=1 ENDPROC DEF PROCdrawbuttons LOCAL a,w,h,x,y,hit FOR a=0 TO sptr-1 6
CASE btn{(a)}.name$ OF WHEN "Start" : IF status=1 btn{(a)}.sts=1 ELSE btn{(a)}.sts=0 WHEN "Pause" : IF status>1 btn{(a)}.sts=1 ELSE btn{(a)}.sts=0 WHEN "Reset" : IF status>0 btn{(a)}.sts=1 ELSE btn{(a)}.sts=0 ENDCASE NEXT w=68:h=28 FOR a=0 TO sptr-1 hit=FALSE IF btn{(a)}.sts>0 THEN x=btn{(a)}.x:y=btn{(a)}.y IF mx>x-w AND mx
y-h AND my0 AND click AND hit THEN CASE btn{(a)}.name$ OF WHEN "Map" : PROCloadmap WHEN "Nav" : PROCloadnav WHEN "Start" : PROCreset:status=2 WHEN "Pause" : IF pause pause=FALSE ELSE pause=TRUE WHEN "Reset" : PROCreset ENDCASE click=FALSE ENDIF ENDIF NEXT ENDPROC DEF PROCcentre(x,y,t$) LOCAL w,h:PROCstrpix(t$,w,h):VDU 5:MOVE x-w,y+h:PRINT t$:VDU 4 ENDPROC DEF PROCleft(x,y,t$) LOCAL w,h:PROCstrpix(t$,w,h):VDU 5:MOVE x,y+h:PRINT t$:VDU 4 ENDPROC DEF PROCstrpix(T$,RETURN w,RETURN h) LOCAL size{}:DIM size{w%,h%} SYS "GetTextExtentPoint32",@memhdc%,T$,LEN(T$),size{} w=size.w%:h=size.h% ENDPROC REM ******************************************************* REM File Handling REM *******************************************************
7
DEF PROCloadmap LOCAL R%,T%,X,i,j,b REM Default map map()=0:havemap=FALSE omega{(0)}.x=0:omega{(0)}.y=7 omega{(1)}.x=7:omega{(1)}.y=0 $Fn%=""+CHR$(0) SYS "GetOpenFileName",Of% TO R% T%=Fn%:WHILE ?T%<>0 T%+=1:ENDWHILE:?T%=13 IF R%=0 THEN ENDPROC IF INSTR($Fn%,".")=0 $Fn%+=".txt" mfile$=FNfilename X=OPENIN$Fn%:IF X=0 THEN ENDPROC map()=0:i=0:j=0 REPEAT b=BGET#X IF b>=48 AND b<=51 THEN CASE b OF WHEN 48 : map(i,j)=0 WHEN 49 : map(i,j)=1 WHEN 50 : map(i,j)=0:omega{(0)}.x=i:omega{(0)}.y=j WHEN 51 : map(i,j)=0:omega{(1)}.x=i:omega{(1)}.y=j ENDCASE i+=1:IF i>7 i=0:j+=1 ENDIF UNTIL EOF#X OR j=8 CLOSE#X havemap=TRUE:PROCreset:vXA=-90 ENDPROC DEF PROCloadnav LOCAL R%,T%,X,b$,sts,v$ havenav=FALSE:nptr=-1 $Fn%=""+CHR$(0) SYS "GetOpenFileName",Of% TO R% T%=Fn%:WHILE ?T%<>0 T%+=1:ENDWHILE:?T%=13 IF R%=0 THEN ENDPROC IF INSTR($Fn%,".")=0 $Fn%+=".txt" nfile$=FNfilename X=OPENIN$Fn%:IF X=0 THEN ENDPROC sts=0 REPEAT b$=CHR$(BGET#X) IF b$>="a" AND b$<="z" b$=CHR$(ASC(b$) AND 223) CASE b$ OF WHEN "F","B","L","R" nptr+=1 nav{(nptr)}.inst$=b$ nav{(nptr)}.units=0 sts=1 8
WHEN "." sts=0:REM Inhibit decimal point OTHERWISE IF sts=1 AND b$>="0" AND b$<="9" THEN v$=STR$(nav{(nptr)}.units) IF LEN(v$)<3 v$+=b$:nav{(nptr)}.units=VAL(v$) ELSE sts=0 ENDIF ENDCASE UNTIL EOF#X CLOSE#X IF nptr>-1 havenav=TRUE:PROCreset ENDPROC DEF FNfilename LOCAL i:i=LEN($Fn%) WHILE MID$($Fn%,i,1)<>"\":i-=1:ENDWHILE =MID$($Fn%,i+1,LEN($Fn%)-i-4) REM ******************************************************* REM 3D REM ******************************************************* DEF PROCdraw(net,x,y,z,xa,ya,za) LOCAL f,abort,col,a,n1,n2,n3,v1x,v1y,v1z,v2x,v2y,v2z,nx,ny,nz,nm,v,ca,sa,i,j f=1280:abort=FALSE:col=7:GCOL 0,15 FOR a=td{(net)}.np TO td{(net)}.np+td{(net)}.nc-1 PROCrotzxy(a,x-vX,y-vY,z-vZ,xa,ya,za):REM Rotate node & add offset REM Special rotation about viewer's x-axis! IF vXA<>0 THEN ca=COS(RAD(vXA)):sa=SIN(RAD(vXA)) i=nd{(a)}.yr:j=nd{(a)}.zr:nd{(a)}.yr=i*ca+j*sa:nd{(a)}.zr=j*ca-i*sa ENDIF IF nd{(a)}.zr>-4 THEN abort=TRUE:REM Node behind viewer ELSE nd{(a)}.x2=f*nd{(a)}.xr/-nd{(a)}.zr:REM 3D to 2D nd{(a)}.y2=f*nd{(a)}.yr/-nd{(a)}.zr ENDIF NEXT IF abort ENDPROC FOR a=td{(net)}.lp TO td{(net)}.lp+td{(net)}.lc-1 CASE link{(a)}.type OF WHEN 1 : col=link{(a)}.n1 WHEN 7 n1=link{(a)}.n1+td{(net)}.np n2=link{(a)}.n2+td{(net)}.np n3=link{(a)}.n3+td{(net)}.np REM Calc two vectors from 3 co-ords (triangle): b-a and c-a v1x=nd{(n2)}.xr-nd{(n1)}.xr v1y=nd{(n2)}.yr-nd{(n1)}.yr 9
v1z=nd{(n2)}.zr-nd{(n1)}.zr v2x=nd{(n3)}.xr-nd{(n1)}.xr v2y=nd{(n3)}.yr-nd{(n1)}.yr v2z=nd{(n3)}.zr-nd{(n1)}.zr REM Find the 'normal' of a triangle, (cross product of two vectors) nx=v1y*v2z-v1z*v2y ny=v1z*v2x-v1x*v2z nz=v1x*v2y-v1y*v2x REM Vector towards viewer v1x=0-nd{(n1)}.xr v1y=0-nd{(n1)}.yr v1z=0-nd{(n1)}.zr REM Triangle faces viewer (normal points towards viewer)? IF (nx*v1x+ny*v1y+nz*v1z)>0 THEN nm=SQR(nx^2+ny^2+nz^2) REM Calc triangle shade from: A.B / |A||B| v=(nx*lightx+ny*lighty+nz*lightz)/nm v=255-ABS(ACS(v))/PI*255 REM Tile in shadow of Big Trak? IF net>0 THEN i=SQR((cx-x)^2+(cy-z)^2) IF i<2 i=2:REM Max shade 20% IF i<10 v=v*i/10 ENDIF REM COLOUR 15,v*(col AND 1),v*((col AND 2)/2),v*((col AND 4)/4) MOVE nd{(n1)}.x2,nd{(n1)}.y2 MOVE nd{(n2)}.x2,nd{(n2)}.y2 PLOT 85,nd{(n3)}.x2,nd{(n3)}.y2 ENDIF ENDCASE NEXT ENDPROC REM Rotate a node about 3 axes and add any offset DEF PROCrotzxy(n,x,y,z,za,xa,ya) LOCAL ca,sa,x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4 x1=nd{(n)}.xt:y1=nd{(n)}.yt:z1=nd{(n)}.zt z2=z1:ca=COS(RAD(za)):sa=SIN(RAD(za)):x2=x1*ca+y1*sa:y2=y1*ca-x1*sa x3=x2:ca=COS(RAD(xa)):sa=SIN(RAD(xa)):y3=y2*ca+z2*sa:z3=z2*ca-y2*sa y4=y3:ca=COS(RAD(ya)):sa=SIN(RAD(ya)):z4=z3*ca+x3*sa:x4=x3*ca-z3*sa nd{(n)}.xr=x4+x:nd{(n)}.yr=y4+y:nd{(n)}.zr=z4+z ENDPROC REM 3D objects - originally created in a separate design program REM Nodes (x,y,z) REM Big Trak 42 DATA 4,6,5,4,6,-4,-4,6,-4,-4,6,5,-5,4,-4,-5,4,5,5,4,5,5,4,-4,4,4,-5,-4,4,-5 DATA 5,0,3,3,0,3,3,0,-2,5,0,-2,5,3,-2,3,3,-2,3,3,3,5,3,3,5,2,5,3,2,5,5,1,5,3,1,5 DATA 5,1,-4,3,1,-4,3,2,-4,5,2,-4,-3,0,3,-5,0,3,-5,0,-2,-3,0,-2,-3,3,-2,-5,3,-2 10
DATA -5,3,3,-3,3,3,-3,2,5,-5,2,5,-5,1,5,-3,1,5,-3,1,-4,-5,1,-4,-5,2,-4,-3,2,-4 REM Tile (square) 4 DATA 4,0,-4,4,0,4,-4,0,4,-4,0,-4 REM Tile (diamond) 4 DATA 0,0,-4,0,0,4,4,0,0,-4,0,0 REM Tile (destination) 5 DATA -4,0,-4,-4,0,4,0,0,0,4,0,-4,4,0,4 REM Links (type,n1,n2,n3: type 1=colour, 7=triangle) REM Big Trak 82 DATA 1,0,0,0,7,26,11,12,7,12,29,26 DATA 1,3,0,0,7,6,5,8,7,6,8,7,7,5,4,9,7,5,9,8,1,6,0,0,7,16,15,12,7,12,11,16 DATA 7,11,21,16,7,21,19,16,7,15,24,12,7,24,23,12,7,29,30,33,7,33,26,29 DATA 7,29,38,41,7,41,30,29,7,26,33,34,7,34,37,26,7,16,17,14,7,14,15,16 DATA 7,14,25,24,7,24,15,14,7,16,19,18,7,18,17,16,7,32,33,30,7,30,31,32 DATA 7,32,35,34,7,34,33,32,7,30,41,40,7,40,31,30,1,3,0,0,7,0,3,5,7,5,6,0 DATA 1,6,0,0,7,34,35,36,7,36,37,34,7,18,19,21,7,21,20,18,7,40,41,38,7,38,39,40 DATA 7,24,25,22,7,22,23,24,7,23,22,13,7,13,12,23,7,12,13,10,7,10,11,12 DATA 7,10,20,21,7,10,21,11,7,28,29,26,7,26,27,28,7,39,38,29,7,29,28,39 DATA 7,26,37,36,7,36,27,26,7,14,17,10,7,10,13,14,7,13,22,14,7,22,25,14 DATA 7,17,18,20,7,20,10,17,7,31,28,27,7,27,32,31,7,31,40,39,7,39,28,31 DATA 7,32,27,36,7,36,35,32,1,3,0,0,7,0,6,7,7,7,1,0,7,5,3,2,7,2,4,5,7,1,7,8 DATA 7,2,9,4,1,4,0,0,7,2,1,8,7,8,9,2,1,3,0,0,7,0,1,2,7,2,3,0 REM Tile (square) 3 DATA 1,2,0,0,7,0,3,2,7,2,1,0 REM Tile (diamond) 3 DATA 1,1,0,0,7,2,0,3,7,3,1,2 REM Tile (destination) 3 DATA 1,7,0,0,7,0,1,2,7,2,4,3 REM Pointers DATA 0,3,42,79 DATA 42,82,4,3 DATA 46,85,4,3 DATA 50,88,5,3
11
BB4W – Bearing & Distance REM Prevent resize SYS "GetWindowLong", @hwnd%, -16 TO ws% SYS "SetWindowLong", @hwnd%, -16, ws% AND NOT &40000 AND NOT &10000 VDU 23,22,400;320;16,16,16,0:REM User-defined Mode COLOUR 143:CLS:COLOUR 0 *ESC OFF *FONT Arial,16 PRINT " Calculates bearing & distance" PRINT " given relative co-ordinates of x,y" PRINT " where x and y can be negative" PRINT REPEAT INPUT "Enter co-orinates (x,y) : "x,y PRINT "Bearing = ";FNtrip(INT(FNbearing(x,y)+.5)) PRINT "Range = ";INT(SQR(x^2+y^2)+.5) PRINT UNTIL 0 END DEF FNbearing(x,y) LOCAL a,h h=SQR(x^2+y^2) IF ABS(x)180 a=180-a IF x<0 AND a<>0 a=360-a =a DEF FNtrip(n) =STRING$(3-LEN(STR$(n)),"0")+STR$(n)
12