Glenagleys is a text-and-graphics golf simulation game for up to 18 holes, procedurally generating each hole’s fairway, distance (170–489 yards), and par rating. The program defines five User Defined Graphics (UDGs) via POKE USR to represent a golfer, flag, tree, and ball sprites used during course rendering and putting sequences. Players select from eight clubs (1 wood through putter/sand wedge), adjust shot angle and strength within a countdown timer, and contend with a randomly varying wind speed and direction shown as a compass needle. Two distinct gameplay phases handle the fairway shot (using pixel-level PLOT/DRAW ballistics with wind deflection and surface detection via ATTR) and a zoomed-in putting green sequence. Running totals of strokes versus par are tracked across all holes in DIM arrays, with final scoring displaying eagle, birdie, par, or bogey outcomes.
Program Analysis
Program Structure
The program is organized into several functional blocks:
- Lines 100–199: Initialization — UDG setup, array allocation, title screen, and instructions.
- Lines 200–299: Hole generation — random distance, par, fairway drawing using BEEP-animated tree sprites.
- Lines 300–505: Main input loop — club selection, angle/direction setting via compass needle, wind display, countdown timer.
- Lines 506–599: Shot calculation — ballistic trajectory with wind deflection, PLOT-based animation, surface detection.
- Lines 600–740: Landing/surface handlers — sand (INK 1), water (INK 6), rough distance penalty.
- Lines 1000–1080: Putting green phase — zoomed-in view, pixel-ball movement toward hole center.
- Lines 2000–2130: Scoring and scorecard display — eagle/birdie/par/bogey classification, per-hole table, running total vs. par.
- Lines 3000–3015: End-of-round summary with FLASH score banner.
- Lines 9000–9100: UDG loader subroutine using DATA/POKE USR.
Initialization Idioms
The program uses a common memory-efficient idiom to establish constants: LET o=PI-PI (= 0) and LET l=PI/PI (= 1). These single-letter variables replace literal 0 and 1 throughout, saving tokenized bytes. POKE 23609, CODE "#" sets the border color attribute byte directly, here using CODE "#" (= 35) which encodes red border with paper/ink values — a common direct-hardware shortcut.
UDG Definitions
Five UDGs are loaded at line 9000 via a nested loop using POKE USR CHR$ (143+f) for f = 1 to 5, placing data into UDG slots \a through \e (characters 144–148). The five sprites defined are:
| UDG | Label in code | Purpose |
|---|---|---|
| \a [UDG-A] | Golfer | Player character on fairway |
| \b [UDG-B] | Flag/marker | Flag near hole |
| \c [UDG-C] | Tree segment | Used in fairway tree animation |
| \d [UDG-D] | Tree segment | Used in fairway tree animation |
| \e [UDG-E] | Hole cup | Displayed on putting green |
The RESTORE 9000 at the start of the subroutine ensures the DATA pointer is reset correctly before reading, which is necessary since DATA lines are distributed later in the program.
Course Generation
Each hole’s distance is chosen as 170 + INT(RND*319) (170–488 yards), with par derived by thresholds: 3 + (d>229) + (d>429) giving par 3, 4, or 5 without any IF statements — a clean Boolean arithmetic trick. The fairway is drawn using nested loops that PRINT randomized tree UDG substrings at random positions, with BEEP calls providing audio feedback during generation. The green/hole position is set by ho = d/2 (horizontal pixel) and he = 60 + INT(RND*80) (vertical pixel).
Ballistic Physics
Shot trajectory uses trigonometric stepping: each iteration of the flight loop at lines 551–563 adds SIN a to x and COS a to y, giving a straight-line vector flight. Wind deflection is applied probabilistically: IF RND*10 < ws THEN add wind vector components, making stronger wind more frequently deflecting. The shot has two phases: an airborne arc for air = dist * g iterations (where g is a club-specific loft factor, 0 for putter), then a rolling phase for the remainder of dist. Surface type is checked with ATTR(yp,xp) and compared to stored attribute values (at = CODE "£" = 96 for fairway, 33 for sand, 102 for water).
Club Selection and Strength System
Eight clubs are implemented at lines 435–442, each setting a maximum distance multiplier st and loft factor g:
| Key | Club | st (base) | g (loft) |
|---|---|---|---|
| 1 | 1 Wood | ~18–23 | 0.65 |
| 2 | 3 Wood | ~15–20 | 0.70 |
| 3 | 3 Iron | ~13–16 | 0.75 |
| 4 | 5 Iron | ~10–13 | 0.80 |
| 5 | 7 Iron | 9 | 0.85 |
| 6 | 9 Iron | 7 | 0.90 |
| 7 | Putter | 3 | 0 |
| 8 | Sand Wedge | 5 | 0.95 |
Shot distance is computed as dist = (e * st) / 2 where e (1–10) is the strength set during the second input phase. The putter’s g=0 means zero airborne travel — the entire shot rolls.
Putting Green Phase
Upon reaching the green (within 8 pixels of the hole), the game switches to a zoomed-in view at line 1000. The ball’s position is recalculated relative to the hole center (xb = 5*(ho-x)+92, yb = 5*(he-y)+124), giving a 5× zoom. The ball is drawn as a 3×3 pixel block using a loop over xb-l TO xb+l. Putting uses COS a/SIN a for motion components (note the swap from fairway convention — x uses COS, y uses SIN), and a hole-in check at pixel coordinates (92, 124) with tolerance 1.
Scoring and Scorecard
Arrays q(18) and w(18) store strokes and par per hole respectively. After each hole, the scorecard at lines 2085–2094 prints a formatted table. The running score differential t - pt (total strokes minus total par) is displayed. The end-of-round summary at line 3000 uses FLASH 1 for the final score line. The game loops back to line 100 (re-initializing) rather than line 200, so a new round resets all state including hole counter hn.
Notable Bugs and Anomalies
- Line 420 is referenced by
GO TO 420at line 505, but the actual input loop code begins at line 419 — line 420 does not exist as labeled; however the jump lands cleanly at line 435 due to how the interpreter seeks the next line. Actually re-examining:GO TO 420would seek from 419 forward and execute 435 next — the missing line 420 is effectively aGO TO 435-equivalent skip. - Line 506 is referenced by
GO TO 506at lines 490, 491, and 1050, but no line 506 exists in the listing — the interpreter falls through to line 510 (the strength input loop). This is intentional: it skips re-displaying certain prompts. - Line 564 is referenced at line 562 (
IF f>dist THEN GO TO 564) but does not appear — execution would continue at line 570, which appears to be the intended target. - Line 565 is referenced at lines 610 and 715 but is absent; execution falls to 570. This seems intentional, as 570 increments stroke count and checks proximity to the hole.
- At line 2050, the condition
IF p-go > -l(i.e., ≥ 0) will print the birdie/par/eagle message, butb$is only assigned for differences of 2, 1, or 0. A difference of 3 or more (albatross) leavesb$undefined or stale, potentially printing a wrong value. - The
DATAline 9030 contains a literaloinstead of0— this works becauseowas set to 0.0 andREAD areads numeric data. However,ois a variable, not a literal, and DATA statements in Sinclair BASIC store numeric values at parse time. This will cause an error at runtime sinceoin a DATA line is treated as a string token, not the variable. This is likely a typo for the literal digit0. - The
VAL "4"at line 402 (LET x = VAL "4") andCODE "\"(LET y = CODE "\"= 92) andCODE "£"(LET at = CODE "£"= 96) establish pixel starting positions and the fairway attribute value. UsingVALandCODEhere is a byte-saving technique.
Input Loop Architecture
The main game loop uses a polled (non-blocking) INKEY$ approach rather than INPUT or PAUSE/INKEY$. The countdown timer at lines 455/513 decrements ti on each pass through the loop, and if it reaches zero the shot is taken with zero strength (st=0). The loop at lines 419–505 handles both Stage 1 (direction setting) and Stage 2 (strength setting) by testing the stage variable, with GO TO 492 short-circuiting club selection during Stage 2.
Source Code
100 LET o= PI- PI:LET l= PI/ PI:LET hn=l:POKE 23609, CODE "#":GO SUB 9000
105 DIM q(18):DIM w(18)
165 BORDER 2:PAPER 2:INK 7:CLS :PRINT TAB VAL "9";"GLENAGLEYS"''" By Richard Warren"
180 INPUT "Number of holes? (18 max.) "; LINE y$
181 IF VAL y$>18 THEN GO TO 180
183 LET limit= VAL y$
190 PRINT ''" Controls are:"''" Number keys select club"'" A and S to set direction"''" ENTER when happy, then:"''" A and S to set strength"'" And ENTER to shoot."
195 PRINT ''" When on the Green, the club is automatically a putter."''" Press a key to continue."
199 PAUSE o
201 LET go=o
205 PAPER 4:BRIGHT o:INK o:CLS
210 LET d=170+ INT (RND*319)
220 LET p=3+(d>229)+(d>429)
230 LET ho=(d/2):LET t=o:LET he=60+ INT (RND*80):LET a$="[UDG-C][UDG-C][UDG-D][UDG-C][UDG-D][UDG-C][UDG-C]"
250 FOR f=l TO 20:BEEP .1,-30:LET c= RND*8:PRINT AT 5+c,(RND*(ho/8)); INK l;a$( TO (INT (RND*4)+l)); AT 4+c,(RND*(ho/8)); INK l;a$( TO (INT (RND*4)+l))
260 NEXT f
261 LET de=o:BRIGHT l
262 FOR f=o TO (ho/16):BEEP .2,-10:PRINT AT 10+de,f;" "; AT 9+de,f;" "; AT 11+de,f;" ":LET de=de+ INT (RND*3)-l:NEXT f
263 LET hc=10+de:LET hp=(175-he)/8:IF hc>14 THEN LET hc=14
264 FOR f=(ho/16) TO (ho/8)+l:BEEP .2,-10:PRINT AT hc,f;" "; AT hc-l,f;" "; AT hc+l,f;" ":LET hc=hc+(hc<hp)-(hc>hp):NEXT f
265 BRIGHT o
267 FOR f=-l TO l:PRINT AT ((175-he)/8)+f-l,(ho)/8-l; BRIGHT l;" "; AT (175-he)/8-l,(ho/8)+f-l; BRIGHT l;" ":NEXT f
270 CIRCLE BRIGHT l;ho,he,8
280 CIRCLE ho,he,l
290 PRINT AT 10,o; PAPER o; INK 7;"[UDG-A]"
295 FOR f=-l TO l STEP 2:PRINT AT (21-(he/8))+f,(ho/8)-2; INK 6; BRIGHT l;"[UDG-B]":NEXT f
300 PAPER 2:INK 7
301 FOR h=o TO 2:PRINT AT h,o;" ":NEXT h
305 PRINT AT o,o;"DIST. ";d;" yds PAR ";p;" HOLE ";hn
310 FOR f=17 TO 21:PRINT AT f,o;" ":NEXT f
315 PRINT AT 17,13;"ANGLE"; AT 17,l;"WIND SPEED"; AT 17,26;"TIME"; AT 17,20;"STR."
320 PLOT o,o:DRAW 255,o:DRAW o,39:DRAW -255,o:DRAW o,-39
321 PLOT o,o:DRAW 255,o:DRAW o,151:DRAW -255,o:DRAW o,-151
322 PLOT 200,o:DRAW o,39:PLOT 44,o:DRAW o,39
323 PLOT 94,o:DRAW o,39:PLOT 152,o:DRAW o,39
402 LET e=5:LET a$=" ":LET aw= RND*6:LET aw1=o:LET ws= INT (RND*4)+l:LET co=o:LET stage=l:LET i=o:LET x= VAL "4":LET y= CODE "\":LET at= CODE "£"
405 CIRCLE 123,16,12:CIRCLE 20,16,12
416 INK 7:PAPER 2
418 LET ti=100
419 LET a$=" "
435 IF INKEY$="1" THEN LET st=20+ INT (RND*5)-2:LET g=.65:LET a$="1 wood"
436 IF INKEY$="2" THEN LET st=17+ INT (RND*5)-2:LET g=.7:LET a$="3 wood"
437 IF INKEY$="3" THEN LET st=14+ INT (RND*3)-l:LET g=.75:LET a$="3 iron"
438 IF INKEY$="4" THEN LET st=11+ INT (RND*3)-l:LET g=.8:LET a$="5 iron"
439 IF INKEY$="5" THEN LET st=9:LET g=.85:LET a$="7 iron"
440 IF INKEY$="6" THEN LET st=7:LET g=.9:LET a$="9 iron"
441 IF INKEY$="7" THEN LET st=3:LET g=o:LET a$="Putter"
442 IF INKEY$="8" THEN LET st=5:LET g=.95:LET a$="Sand Wedge"
450 IF INKEY$<"9" AND INKEY$>"0" THEN PRINT AT 2,o; PAPER 2;"CLUB SELECTED ";a$;" ":BEEP .5,o
455 LET ti=ti-l:PRINT AT 19,27;ti;" ":IF ti <=o THEN LET st=o:GO TO 535
465 LET a1=a
470 LET a=a+((INKEY$="s")/10)-((INKEY$="a")/10)
475 PLOT 123,16:DRAW INVERSE l;10*(SIN a1),10*(COS a1)
480 PLOT 123,16:DRAW 10*(SIN a),10*(COS a)
490 IF INKEY$= CHR$ 13 AND a$ <>" " AND stage=l THEN BEEP .5,o:GO TO 506
491 IF INKEY$= CHR$ 13 AND stage=2 THEN BEEP .5,o:GO TO 1050
492 IF stage=2 THEN GO TO 465
496 IF INKEY$="x" AND stage=l THEN PLOT o,y:DRAW OVER l; INK l;x,o:PLOT x,40:DRAW OVER l; INK l;o,y-40:
499 PLOT 20,16:DRAW INVERSE l;10*(SIN aw1),10*(COS aw1)
500 PLOT 20,16:DRAW 10*(SIN aw),10*(COS aw)
501 LET aw1=aw
502 LET aw=aw+((INT (RND*3))-l)/10
503 LET ws=ws+(INT (RND*3)-l)/10:IF ws<.5 THEN LET ws=.5
504 PRINT AT 19,6; PAPER 2;ws;" "
505 GO TO 420
510 LET e=e+(INKEY$="s")-(INKEY$="a")
511 IF e<l THEN LET e=l
512 IF e>10 THEN LET e=10
513 IF stage=l THEN LET ti=ti-l:PRINT AT 19,27;ti;" ":IF ti <=o THEN LET st=o:GO TO 535
515 PRINT AT 19,21; PAPER 2; INK 7;e;" "
517 FOR f=l TO 50:NEXT f
520 IF INKEY$= CHR$ 13 THEN BEEP .5,o:GO TO 531
525 GO TO 510
532 IF stage=l THEN PRINT AT 2,o;" "
534 INK o
535 IF stage=2 THEN GO TO 1051
539 LET dist=(e*st)/2
545 LET e=5
548 LET air=dist*g
549 LET x1=x:LET y1=y
551 FOR f=o TO air:LET x1=x:LET y1=y:LET x=x+(SIN a):LET y=y+(COS a)
552 IF RND*10<ws THEN LET x=x+(SIN aw):LET y=y+(COS aw)
554 PLOT INVERSE l;x1,y1:PLOT x,y:NEXT f
555 LET xp= INT (x/8):LET yp=21- INT (y/8)
560 FOR f=air TO dist:LET x1=x:LET y1=y:LET x=x+(SIN a):LET y=y+(COS a):IF POINT (x,y)=l THEN GO TO 600
561 LET xp= INT (x/8):LET yp=21-(INT (y/8)):IF ATTR (yp,xp) <>at THEN GO TO 701
562 PLOT INVERSE l;x1,y1::PLOT INK i;x,y:IF f>dist THEN GO TO 564
563 NEXT f
570 LET go=go+l
580 IF ABS (x-ho) <=8 AND ABS (y-he) <=8 THEN GO TO 1000
599 GO TO 415
610 IF ATTR (yp,xp)=33 OR ATTR (yp,xp)=102 THEN GO TO 565
700 GO TO 563
705 LET xs= ATTR (yp,xp)
710 IF xs=33 THEN LET i=l
715 IF xs=102 THEN LET i=6:GO TO 565
726 IF xs=32 THEN LET i=o:LET dist=dist-l
740 GO TO 562
1000 PAPER 4:INK 7:CLS
1001 LET stage=2
1002 FOR f=18 TO 21:PRINT AT f,0; PAPER 2;" ":NEXT f
1003 FOR f=-20 TO 20 STEP 2:BEEP .1,f:NEXT f
1004 PLOT o,o:DRAW 255,o:DRAW o,32:DRAW -255,o:DRAW o,-32
1005 CIRCLE INK 7;123,16,12
1020 PRINT AT 10,15; INK o;"[UDG-E]"
1029 LET xb=(5*(ho-x))+92:LET yb=(5*(he-y))+124
1031 FOR g=xb-l TO xb+l:PLOT yb-l,g:PLOT yb,g:PLOT yb+l,g:NEXT g
1040 GO TO 459
1050 GO TO 506
1055 :LET s=e*8
1056 INK 7
1058 LET go=go+l
1060 FOR f=o TO s:LET xb1=xb:LET yb1=yb:LET xb=xb+(COS a):LET yb=yb+(SIN a)
1061 FOR g=xb1-l TO xb1+l:PLOT INVERSE l;yb1-l,g:PLOT INVERSE l;yb1,g:PLOT INVERSE l;yb1+l,g:NEXT g
1062 FOR g=xb-l TO xb+l:PLOT yb-l,g:PLOT yb,g:PLOT yb+l,g:NEXT g
1063 IF INT (ABS (yb-124)) <=l AND INT (ABS (xb-92)) <=l THEN GO TO 2000
1064 NEXT f
1074 PRINT AT 10,15; INK o;"[UDG-E]"
1075 FOR g=xb-l TO xb+l:PLOT yb-l,g:PLOT yb,g:PLOT yb+l,g:NEXT g
1080 GO TO 1040
2000 BEEP .2,2:BEEP .4,2:BEEP .6,2:BEEP .2,2:BEEP .4,2:BEEP .6,2:BEEP .2,4:BEEP .4,5:BEEP .6,6
2002 PAPER 2:INK 7:CLS
2010 PRINT " GLENEAGLES"
2020 PRINT :PRINT :PRINT "You completed the hole in ";go
2030 PRINT "strokes":PRINT :PRINT :PRINT "Par for hole ";hn;" is ";p
2040 IF p-go=2 THEN LET b$="eagled"
2041 IF p-go=l THEN LET b$="birdied"
2042 IF p-go=o THEN LET b$="parred"
2050 IF p-go>-l THEN PRINT :PRINT :PRINT "You have ";b$;" the hole"
2055 IF go-p>o THEN PRINT :PRINT :PRINT "You have a bogie "+ STR$ INT (go-p)+" on this hole"
2060 LET q(hn)=go:LET w(hn)=p
2070 PRINT :PRINT :PRINT "Press any key to continue"
2080 PAUSE o
2085 CLS :PRINT AT o,3;"HOLE"; TAB 13;"PAR"; TAB 23;"STROKES"
2090 FOR f=l TO hn:PRINT AT f,5;f; AT f,25;q(f); AT f,15;w(f):NEXT f
2094 LET t=o:LET pt=o:FOR f=l TO hn:LET t=t+q(f):NEXT f:FOR f=l TO hn:LET pt=pt+w(f):NEXT f:PRINT AT hn+2,0;"Score so far = ";t-pt
2095 IF hn >=limit THEN GO TO 3000
2100 LET hn=hn+l
2110 PRINT AT 21,7; INK 7;"Press to continue"
2120 PAUSE o
2130 GO TO 200
3000 LET t=o:LET pt=o:FOR f=l TO hn:LET t=t+q(f):NEXT f:FOR f=l TO hn:LET pt=pt+w(f):NEXT f:PRINT AT 19,10;"par ";pt; AT 19,20;"total ";t; AT 21,o; FLASH l;" "; AT 21,o;" Score ";t-pt;" "
3010 PAUSE o
3015 GO TO 100
9000 RESTORE 9000:FOR f=l TO 5:FOR g=o TO 7:READ a:POKE USR CHR$ (143+f)+g,a:NEXT g:NEXT f
9010 DATA 255,255,195,219,219,195,255,255
9020 DATA 12,60,120,120,120,120,60,12
9030 DATA o,16,56,84,56,84,16,16
9040 DATA o,28,62,28,62,8,8,8
9050 DATA 28,34,65,65,65,34,28,o
9100 RETURN
9998 SAVE "Golf" LINE 1
9999 Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

