A simple game that reminds you of the classic Atari arcade game Tempest, but fits in 10 lines of BASIC!
tenpest is an offensive-only game, which ends after a time limit runs out. Your goal is to get as high a score as you can, by blasting enemies when they're as far away as possible.
Atari XL or XE computer (or 800 with Incognito board, I guess?), capable of running TurboBASIC XL — or an emulator — and a joystick.
tenpest.atr
- ATR disk image
(for emulators, or real Ataris with SIO2PC, SIO2SD, etc.)
tenpest.txt
- ASCII text file
of the source (as seen on this web page)
Use a joystick in port 1. Press left/right to move your blaster around the edge of the surface. Any enemies within the same segment as your blaster will be destroyed automatically.
The further down the tube an enemy is when your blaster shoots it, the more points you receive. If the enemy is at the very edge, no points are awarded.
As you receive points, they're displayed in the text window at the bottom of the screen.
Once time runs out, the game ends. Your final score will have already been
visible on the screen. Type "RUN
" and press [Return] to start
a new game.
For each of the 10 lines of source code, the abbreviated version (which fits within 120 characters) is shown, immediately followed by an expanded version that's slightly more readable. Then, each individual command is shown, and its purpose is explained, using nested bullet lists (to show the code's structure within IF/ENDIF, FOR/NEXT, etc. blocks).
Set-up
10 POKE 559,0:DEG:DIM CX(12),CY(12),R(12),BA(5),BR(5),S$(3840):F.A=0TO11:CX(A)=COS(A*30):CY(A)=-SIN(A*30)
10 POKE 559,0:DEG:DIM CX(12),CY(12),R(12),BA(5),BR(5),S$(3840):FOR A=0 TO 11:CX(A)=COS(A*30):CY(A)=-SIN(A*30)
POKE 559,0
— Disable ANTIC (graphics chip)
DMA (direct memory access), which gives the CPU some extra time to do
work. (Result: screen is blanked, and BASIC runs a little faster.)
DEG
— Switch trigonometric functions to use degrees (vs. radians).
DIM CX(12),CY(12),R(12),BA(5),BR(5),S$(3840)
—
create storage for the trig. fast look-up table (CX, CY), and the radii
of the web (it gets a fancy shape, vs. being a circle-like polygon)
(R), as well as the enemy's location around the web (BA), and distance
up the web (BR); finally also some storage for a copy of screen data (S$)
FOR A=0 TO 11
— loop 12 times (number of segments of the web)
CX(A)=COS(A*30)
— fill in cosine look-up table
CY(A)=-SIN(A*30)
— fill in sine look-up table
20 R(A)=20+RAND(20):N.A:GR.7:COL.1:CX=79:CY=40:Z=ADR(S$):DPOKE708,36032:PL.CX(0)*R(0)+CX,CY(0)*R(0)+CY:SC=DPEEK(88)
20 R(A)=20+RAND(20):NEXT A:GRAPHCIS 7:COLOR 1:CX=79:CY=40:Z=ADR(S$):DPOKE 708,36032:PLOT CX(0)*R(0)+CX,CY(0)*R(0)+CY:SC=DPEEK(88)
R(A)=20+RAND(20)
— pick a random radius for an
endpoint of each segment (to make it non-circular)
NEXT A
GRAPHCIS 7
— switch to 160x96 4-color bitmap mode (ANTIC DMA gets reenabled, BTW)
COLOR 1
— set drawing color to 1
CX=79:CY=40
— quick look-up for the center of the web (79,40)
Z=ADR(S$)
— quick look-up for the address of the storage at S$
DPOKE 708,36032
— set what color colors 1 and 2 draw
PLOT CX(0)*R(0)+CX,CY(0)*R(0)+CY
— plot a point at the first segment's endpoint, up close
SC=DPEEK(88)
— quick look-up for the address of the start of screen memory
30 F.A=1TO11:DR.CX(A)*R(A)+CX,CY(A)*R(A)+CY:N.A:DR.CX(0)*R(0)+CX,CY(0)*R(0)+CY:PL.CX(0)*R(0)/8+CX,CY(0)*R(0)/8+CY
30 FOR A=1 TO 11:DRAWTO CX(A)*R(A)+CX,CY(A)*R(A)+CY:NEXT A:DRAWTO CX(0)*R(0)+CX,CY(0)*R(0)+CY:PLOT CX(0)*R(0)/8+CX,CY(0)*R(0)/8+CY
FOR A=1 TO 11
— loop around the rest of the web (1 thru 11)
DRAWTO CX(A)*R(A)+CX,CY(A)*R(A)+CY
— draw a line to the next segment
NEXT A
DRAWTO CX(0)*R(0)+CX,CY(0)*R(0)+CY
— draw a line back to the first segment
PLOT CX(0)*R(0)/8+CX,CY(0)*R(0)/8+CY
— plot a point at the first segment's endpoint, far away
40 F.A=1TO11:DR.CX(A)*R(A)/8+CX,CY(A)*R(A)/8+CY:N.A:DR.CX(0)*R(0)/8+CX,CY(0)*R(0)/8+CY:POKE710,66:F.A=0TO11
40 FOR A=1 TO 11:DRAWTO CX(A)*R(A)/8+CX,CY(A)*R(A)/8+CY:NEXT A:DRAWTO CX(0)*R(0)/8+CX,CY(0)*R(0)/8+CY:POKE 710,66:FOR A=0 TO 11
FOR A=1 TO 11
— loop around the rest of the web
DRAWTO CX(A)*R(A)/8+CX,CY(A)*R(A)/8+CY
— same as before
NEXT A
DRAWTO CX(0)*R(0)/8+CX,CY(0)*R(0)/8+CY
— same as before
POKE 710,66
— set what color color 3 draws
FOR A=0 TO 11
— loop around the entire web
50 PL.CX(A)*R(A)/8+CX,CY(A)*R(A)/8+CY:DR.CX(A)*R(A)+CX,CY(A)*R(A)+CY:N.A:A=0:F.I=0TO4:BA(I)=-1:N.I:MOVE SC,Z,3840
50 PLOT CX(A)*R(A)/8+CX,CY(A)*R(A)/8+CY:DRAWTO CX(A)*R(A)+CX,CY(A)*R(A)+CY:NEXT A:A=0:FOR I=0 TO 4:BA(I)=-1:NEXT I:MOVE SC,Z,3840
PLOT CX(A)*R(A)/8+CX,CY(A)*R(A)/8+CY
— plot a point at this segment's endpoint, far away
DRAWTO CX(A)*R(A)+CX,CY(A)*R(A)+CY
— drawto its endpoint, up close
NEXT A
A=0
— set the blaster's position around the web
FOR I=0 TO 4
— loop through all enemies
BA(I)=-1
— disable them (set their segment to -1, which is invalid)
NEXT I
MOVE SC,Z,3840
— now that the empty web has been drawn, copy a chunk of screen memory into S$ to take a snapshot of it
Main loop
60 MOVE Z,SC,3840:S=STICK(0):IF S=11:A=(A+1)MOD12:ELSE:IF S=7:A=A-1:IF A<0:A=11:END.:END.:END.:COL.2:GOS.1000
60 MOVE Z,SC,3840:S=STICK(0):IF S=11:A=(A+1) MOD 12:ELSE:IF S=7:A=A-1:IF A<0:A=11:ENDIF:ENDIF:ENDIF:COLOR 2:GOSUB 1000
MOVE Z,SC,3840
— copy the 'empty web' snapshot
S=STICK(0)
— grab the state of the joystick
IF S=11
— if pushing left
A=(A+1) MOD 12
— move around the web one direction (0-11)
ELSE
IF S=7
— if pushing right
A=A-1
— move around the web in the other direction (11-0)
IF A<0
— if we went beyond 0...
A=11
— ...wrap around
ENDIF
ENDIF
ENDIF
COLOR 2
— set drawing color to 2
GOSUB 1000
— jump to blaster-drawing routine
70 F.I=0TO4:IF BA(I)=A:BA(I)=-1:Q=Q+(1-BR(I))*100:?Q:END.:IF BA(I)=-1:IF RAND(10)=0:BA(I)=RAND(12):BR(I)=0.03:END.:ELSE
70 FOR I=0 TO 4:IF BA(I)=A:BA(I)=-1:Q=Q+(1-BR(I))*100:? Q:ENDIF:IF BA(I)=-1:IF RAND(10)=0:BA(I)=RAND(12):BR(I)=0.03:ENDIF:ELSE
FOR I=0 TO 4
— for each enemy (0-4)
IF BA(I)=A
— if they're on the same segment of the web as the blaster...
BA(I)=-1
— disable the enemy
Q=Q+(1-BR(I))*100
— accumulate points based on how far up the web it has reached
? Q
— show your new score
ENDIF
IF BA(I)=-1
— if the enemy is not active...
IF RAND(10)=0
— there's a 1-in-10 chance that we'll...
BA(I)=RAND(12)
— enable the enemy (on a random segment, 0-11)
BR(I)=0.03
— at the farthest distance
ENDIF
ELSE
— (enemy was already active...)
80 BR(I)=BR(I)+0.1:IF BR(I)>1:BA(I)=-1:ELSE:COL.3:GOS.2000:END.:END.:N.I:L=L+1:IF L=200:END:END.:GOTO 60
80 BR(I)=BR(I)+0.1:IF BR(I)>1:BA(I)=-1:ELSE:COLOR 3:GOSUB 2000:ENDIF:ENDIF:NEXT I:L=L+1:IF L=200:END:ENDIF:GOTO 60
BR(I)=BR(I)+0.1
— make the enemy move up the web
IF BR(I)>1
— if they reach the top...
BA(I)=-1
— disable the enemy
ELSE
COLOR 3
— set drawing color to 3
GOSUB 2000
— jump to enemy-drawing routine
ENDIF
ENDIF
NEXT I
L=L+1
— increase time counter
IF L=200
— if we've reached the time limit...
END
— end the program
ENDIF
GOTO 60
— otherwise, go back to the beginning of the main loop
Blaster-drawing routine
1000 A2=(A+1)MOD12:R1=R(A):R2=R(A2):PL.CX(A)*R1+CX,CY(A)*R1+CY:DR.CX,CY:DR.CX(A2)*R2+CX,CY(A2)*R2+CY:RET.
1000 A2=(A+1) MOD 12:R1=R(A):R2=R(A2):PLOT CX(A)*R1+CX,CY(A)*R1+CY:DRAWTO CX,CY:DRAWTO CX(A2)*R2+CX,CY(A2)*R2+CY:RETURN
A2=(A+1) MOD 12
— get the next segment, relative to blaster (wrapping around 11-0)
R1=R(A)
— get radius at current segment
R2=R(A2)
— and radius at next segment
PLOT CX(A)*R1+CX,CY(A)*R1+CY
— plot a point at the first segment (near)
DRAWTO CX,CY
— draw a line to the center of the screen (far)
DRAWTO CX(A2)*R2+CX,CY(A2)*R2+CY
— draw a line back up to the second segment (near)
RETURN
Enemy-drawing routine
2000 BR=BR(I):M=BA(I):R=R(M)*BR:A2=(M+1)MOD12:R2=R(A2)*BR:PL.CX(M)*R+CX,CY(M)*R+CY:DR.CX(A2)*R2+CX,CY(A2)*R2+CY:RET.
2000 BR=BR(I):M=BA(I):R=R(M)*BR:A2=(M+1) MOD 12:R2=R(A2)*BR:PLOT CX(M)*R+CX,CY(M)*R+CY:DRAWTO CX(A2)*R2+CX,CY(A2)*R2+CY:RETURN
BR=BR(I)
— get this enemy's distance down the web
M=BA(I)
— get this enemy's position (segment) around the web
R=R(M)*BR
— scale web segment's radius by the enemy's distance (0.03-1.00)
A2=(M+1) MOD 12
— get the next segment, relative to enemy (wrapping 11-0)
R2=R(A2)*BR
— scale that web segment's radius
PLOT CX(M)*R+CX,CY(M)*R+CY
— plot a point at along segment 1
DRAWTO CX(A2)*R2+CX,CY(A2)*R2+CY
— draw a line across to segment 2
RETURN