Tennis simulates a two-player tennis rally viewed in perspective, drawing a court and animating a ball traveling in a sine-wave arc between two players. The court geometry is rendered using PLOT and DRAW commands to create a three-dimensional perspective effect, including net posts and baseline markers. Eight user-defined graphics (UDGs A–H) are loaded via a DATA/POKE loop at initialization, defining the player figures in two animation frames each. Ball trajectory height and speed are randomized after each exchange using RND, and each player character shifts left or right depending on the ball’s arc height, providing a rudimentary AI movement system controlled by threshold comparisons on the sine-wave amplitude variables.
Program Structure
The program divides into four logical sections. Lines 10–15 initialize all variables. Lines 20–43 draw the tennis court using PLOT and DRAW. The main game loop runs from line 44 onward, with line 90 sending execution back to line 50. The subroutine at line 9000 loads eight UDG characters from DATA statements at lines 9010–9040.
Court Rendering
Lines 20–42 construct a perspective court entirely with PLOT and DRAW. Line 20 draws a series of short vertical segments stepping across the screen to suggest a back fence or crowd. Line 30 draws diagonal lines for the net posts. Lines 40–42 trace the court outline and service boxes using connected DRAW commands, giving a convincing three-quarter perspective view without any fill routines.
UDG Initialization
The subroutine at line 9000 uses a nested FOR loop to POKE 8 bytes into the UDG area for each of 8 characters (UDGs A–H, at USR CHR$ (143+f) for f 1 to 8). The 64 bytes are spread across DATA lines 9010–9040, each line holding 16 values. UDGs A/B and E/F represent the left player in two animation frames; UDGs C/D and G/H represent the right player similarly.
Ball Animation via Sine Arcs
Ball travel from left to right is handled in lines 50–60. The ball is plotted at coordinates (n, 50 + hl * SIN(n/235 * PI)) where hl controls arc height and sl controls step size (speed). The previous position is erased with PLOT OVER 1 before the next point is drawn, giving smooth pixel-by-pixel motion. The return journey (lines 70–80) uses hr and sr with a slightly different arc formula (n/255 * PI) so the two directions look distinct. After each rally, hl, hr, sl, and sr are re-randomized at lines 65 and 85.
Player Animation and Movement
Each player is drawn as a two-row UDG pair (e.g. AT a,b and AT a+1,b). When the ball is near the receiving end, the program switches to an alternate UDG pair (swing frame) for a brief PAUSE, then restores the standing frame, simulating a return stroke. Lines 57 and 59 move the right player one column left or right depending on whether hl (arc height) is below or above threshold values (65 and 72 respectively), controlled by flag variable t. Lines 77 and 79 do the same for the left player using hr, thresholds 73 and 85, and flag v. This gives a simple AI repositioning based on the incoming ball’s trajectory.
Key Variables
| Variable | Role |
|---|---|
hl | Arc height for left-to-right ball travel |
hr | Arc height for right-to-left ball travel |
sl | Step size (speed) for left-to-right travel |
sr | Step size (speed) for right-to-left travel |
a, b | Row and column of left player |
c, d | Row and column of right player |
t | Flag preventing repeated column shifts for right player |
v | Flag preventing repeated column shifts for left player |
Notable Techniques
- Using
PLOT OVER 1to erase the previous ball pixel rather than redrawing the whole screen, keeping animation smooth within BASIC. - Two different divisors (235 vs. 255) in the sine argument alter arc shape for each direction of travel.
- Redrawing partial court lines at lines 83 and 87 restores segments that the ball pixel may have corrupted during transit.
- Short
BEEP .01,0calls provide a click sound at each stroke without interrupting the visual flow. PAUSE 5after each stroke gives a brief freeze that makes the hit visually readable.
Anomalies and Observations
The variable names a, b, c, d, f, and g are reused in the UDG setup subroutine at line 9000, which overwrites the player-position variables initialized in line 15. The subroutine is called only once at startup (line 10) before the player positions are set in line 15, so this does not cause a bug in practice, but it is a tight ordering dependency. Lines 83 and 87 repair specific court segments that are likely to be hit by the ball but do not repair all potentially affected pixels, meaning prolonged play could degrade other court lines.
Source Code
10 GO SUB 9000:LET hl=57:LET hr=70:LET sl=12:LET sr=8
15 LET v=0:LET t=0:LET a=13:LET b=4:LET c=11:LET d=24
20 LET g=110:FOR f=103 TO 53 STEP -5:PLOT g,f:DRAW 0,-15:LET g=g+2:NEXT f
30 FOR f=0 TO 15 STEP 5:PLOT 110,103-f:DRAW 20,-50:NEXT f
40 PLOT 12,88:DRAW 190,0:DRAW 24,-50:DRAW -190,0:DRAW -24,50
41 PLOT 22,67:DRAW 70,0:DRAW -10,20:DRAW 24,-49
42 PLOT 212,67:DRAW -70,0:DRAW -10,20:DRAW 24,-49
44 PRINT AT a,b;"[UDG-A]"; AT a+1,b;"[UDG-B]"
45 PRINT AT c,d;"[UDG-C]"; AT c+1,d;"[UDG-D]":PAUSE 5
47 PRINT AT a,b;"[UDG-E]"; AT a+1,b;"[UDG-F]"
48 PAUSE 5:BEEP .01,0:PRINT AT a,b;"[UDG-A]"; AT a+1,b;"[UDG-B]"
50 FOR n=36 TO 190 STEP sl:PLOT n,50+hl* SIN (n/235* PI )
52 PLOT OVER 1;n,50+hl* SIN (n/235* PI )
55 IF n>170 THEN PRINT AT c,d;"[UDG-G]"; AT c+1,d;"[UDG-H]"
56 IF t=1 THEN GO TO 58
57 IF hl<65 AND n>160 AND t=0 THEN LET d=d-1:PRINT AT c,d;"[UDG-C] "; AT c+1,d;"[UDG-D] ":LET t=1
58 IF t=0 THEN GO TO 60
59 IF hl>72 AND n>160 AND t=1 THEN LET d=d+1:PRINT AT c,d-1;" [UDG-C]"; AT c+1,d-1;" [UDG-D]":LET t=0
60 NEXT n
62 PAUSE 5:PRINT AT c,d;"[UDG-C]"; AT c+1,d;"[UDG-D]":BEEP .01,0
65 LET hl= RND *26+57:LET sl= RND *16+4
70 FOR n=190 TO 44 STEP -sr:PLOT n,40+hr* SIN (n/255* PI )
72 PLOT OVER 1;n,40+hr* SIN (n/255* PI )
74 IF n<64 THEN PRINT AT a,b;"[UDG-E]"; AT a+1,b;"[UDG-F]"
76 IF v=1 THEN GO TO 78
77 IF hr<73 AND n<60 AND v=0 THEN LET b=b+1:PRINT AT a,b-1;" [UDG-A]"; AT a+1,b-1;" [UDG-B]":LET v=1
78 IF v=0 THEN GO TO 80
79 IF hr>85 AND n<60 AND v=1 THEN LET b=b-1:PRINT AT a,b;"[UDG-A] "; AT a+1,b;"[UDG-B] ":LET v=0
80 NEXT n
82 PAUSE 5:PRINT AT a,b;"[UDG-A]"; AT a+1,b;"[UDG-B]":BEEP .01,0
83 PLOT 22,67:DRAW 70,0
85 LET hr= RND *30+65:LET sr= RND *16+4
87 PLOT 12,88:DRAW 190,0
90 GO TO 50
9000 FOR f=1 TO 8:FOR g=0 TO 7:READ a:POKE USR CHR$ (143+f)+g,a:NEXT g:NEXT f:RETURN
9010 DATA 0,0,24,24,255,56,92,92,92,154,153,148,212,210,18,27
9020 DATA 0,0,24,24,8,28,58,58,58,89,153,41,43,75,72,216
9030 DATA 3,3,25,25,255,62,92,92,92,152,152,148,20,18,18,27
9040 DATA 192,192,152,152,72,60,58,58,58,25,25,40,40,72,72,216Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
