This program is a Dungeons & Dragons character generator that rolls ability scores, calculates hit points, determines starting gold, and optionally assigns magic items. Ability scores are generated using the classic “4d6 drop lowest” method: four dice are rolled (lines 4060–4065), the minimum value is found (lines 4080–4110), and the three highest are summed (line 4120). Hit point calculation accounts for class-specific hit dice (d4 through d10), Constitution bonuses, and special handling for Rangers and Monks. Magic items are drawn from four tiered tables (lines 7000–7375), with item probability scaling with character level via the B() array.
Program Analysis
Program Structure
The program is organized into several functional regions identified by line number ranges:
- Lines 3998–4158: Title screen, dice-rolling loop (6 sets of ability scores), user acceptance prompt.
- Lines 5000–5156: Character detail input (race, class, sex, name, age) and ability score entry subroutine.
- Lines 5162–5567: Hit point calculation by class, Constitution bonuses, and starting gold.
- Lines 5570–5999: Strength combat modifiers, magic item determination using four tiered tables.
- Lines 6990–7375 (via GOSUBs): Four magic item lookup tables, each returning a string in
H$. - Lines 8995–9110: Character sheet display, COPY/rerun options, and SAVE.
Notably, there is a flow anomaly: after the magic item section ends at line 6000 with GOTO 8000, execution jumps to line 8000 which does not exist in the listing. Line 8995 is the next visible line. This is a common ZX81 technique where a non-existent target causes execution to continue at the next available line — here, line 8995.
Ability Score Generation
The program implements the Advanced D&D “4d6, drop lowest” method. Lines 4060–4065 roll four dice using INT(1+6*RND)+1, which produces values 2–8; the IF P(U)>6 THEN LET P(U)=6 clamp corrects this to the intended 1–6 range, indicating the original roll formula should have been INT(6*RND)+1. The minimum of the four rolls is found iteratively (lines 4080–4110), and the sum minus the minimum forms each ability score (line 4120). Six scores are generated and printed for user acceptance.
Hit Point Calculation
Each class uses a different hit die, iterated once per character level (LE). The same off-by-one pattern seen in dice rolling appears here: INT(1+10*RND)+2 yields 3–12 instead of 1–10, clamped back to 10. Constitution bonuses are accumulated in A(9) and added to the total HP in lines 5460–5480. Rangers and Monks receive a double hit die at first level before the loop, and their Constitution bonus is halved afterward (line 5485).
Magic Item Tables
Four subroutines at lines 6990, 7100, 7200, and 7300 provide tiered magic item tables. The probability of receiving an item from each tier is gated by the B() array, which scales with level. The program asks the user whether a given character class can use the rolled item (lines 5898–5904), re-rolling if the answer is “N”. Items are stored in the string array J$(7,30) for later display.
| Table | GOSUB | Items | Examples |
|---|---|---|---|
| 1 (Common) | 6990 | 18 | Potions, +1 weapons, scrolls |
| 2 (Uncommon) | 7100 | 16 | Rings, staves, wands, armor sets |
| 3 (Rare) | 7200 | 16 | Boots, cloaks, rods, +4 weapons |
| 4 (Very Rare) | 7300 | 12 | Regeneration ring, Horn of Valhalla |
Gold Scaling
Starting gold (GO) is calculated by class using multiple inline dice rolls (lines 5505–5525), then multiplied by successive scaling factors for levels above 1 (lines 5530–5560). This multiplicative chaining means a high-level character can accumulate substantial gold, though no upper bound is enforced.
Notable Techniques and Idioms
- The
DIM R(1)at line 4030 and laterDIM R(8)at lines 5164 and 5745 redimension the same array, reusing the variable name for different purposes — a memory-conservation approach. - String variable
I$is used as a copy ofC$(class) to preserve the original whileC$is reused, though in practiceC$is not overwritten. - The strength exceptional percentage
A(7)is only generated for Fighter, Ranger, and Paladin classes with Strength 18 (lines 5200–5225), matching AD&D rules. - The
COPYcommand at line 9076 triggers a printer hard copy of the character sheet, a standard ZX81 peripheral feature. - Lines 9075 and 9110 use
RUNandSAVEfor restart and persistence respectively.
Bugs and Anomalies
- Dice roll formula
INT(1+N*RND)+Kconsistently produces values one higher than intended; the clamp guards (e.g.,IF B>10 THEN LET B=10) partially compensate but also eliminate the top face, skewing distributions toward lower values. - Line 5430 is referenced in the flow (after line 5420) but does not exist; execution falls through to line 5440, which may be intentional.
A(8)andA(9)are never explicitly initialised to zero before the bonus accumulation loops at lines 5400–5420 and 5440–5455; residual values could corrupt results on a second run without a fullRUNrestart. TheDIM A(9)at line 5102 would clear them on first entry but not on repeated GOSUB calls if reached by a different path.- Line 5610 references
IF LE<>2 THEN GOTO 5615but jumps to 5615 regardless (the next line), making the condition a no-op. - The subroutine at line 6990 (Table 1) is entered by
GOSUB 6990but the actual first executable line is 7000; line 6990 does not exist, so execution falls through to 7000 — a valid ZX81 technique but slightly confusing.
Content
Source Code
3998 REM % % % % % % %D%£%D%/%G%E%N% % % % % %
3999 REM BY ? LANE
4000 CLS
4001 PRINT AT 10,1;"D £ D CHARACTER GENERATOR"
4002 PRINT
4003 PRINT
4004 PRINT
4005 PRINT "% % % %P%R%E%S%S% %A%N%Y% %K%E%Y% %T%O% %R%O%L%L% %D%I%C%E% % % "
4006 PAUSE 4E4
4007 SLOW
4008 CLS
4010 DIM P(4)
4020 DIM Q(6)
4030 DIM R(1)
4040 FOR V=1 TO 6
4050 FOR U=1 TO 4
4060 LET P(U)=INT (1+6*RND)+1
4065 IF P(U)>6 THEN LET P(U)=6
4070 NEXT U
4080 LET R(1)=P(1)
4090 FOR S=2 TO 4
4100 IF P(S)<R(1) THEN LET R(1)=P(S)
4110 NEXT S
4120 LET Q(V)=P(1)+P(2)+P(3)+P(4)-R(1)
4130 PRINT Q(V),
4140 NEXT V
4150 PRINT
4151 PRINT
4152 PRINT
4153 PRINT "DO YOU WANT THESE SCORES?"
4154 PRINT "IF SO, PRESS <%E%N%T%E%R>"
4155 PRINT "IF NOT, ENTER AN <X>"
4156 INPUT Q$
4158 IF Q$="X" THEN GOTO 4008
4190 GOSUB 5100
5000 PRINT AT 20,1;"D £ D PROGRAM"
5010 PRINT AT 10,1;"RACE "
5020 INPUT B$
5030 PRINT AT 10,1;"CLASS"
5040 INPUT C$
5045 LET I$=C$
5050 PRINT AT 10,1;"SEX "
5060 INPUT F$
5070 PRINT AT 10,1;"NAME "
5080 INPUT E$
5090 PRINT AT 10,1;"AGE "
5095 INPUT I
5099 GOTO 5162
5100 PRINT AT 10,1;"STRENGTH"
5102 DIM A(9)
5105 INPUT A(1)
5110 PRINT AT 10,1;"INTELLIGENCE"
5115 INPUT A(2)
5120 PRINT AT 10,1;"WISDOM "
5125 INPUT A(3)
5130 PRINT AT 10,1;"DEXTERITY "
5135 INPUT A(4)
5140 PRINT AT 10,1;"CONSTITUTION"
5145 INPUT A(5)
5150 PRINT AT 10,1;"CHARISMA "
5155 INPUT A(6)
5156 RETURN
5162 LET G$=" "
5164 DIM R(8)
5165 LET HP=0
5180 DIM B(6)
5200 IF I$<>"RANGER" AND I$<>"FIGHTER" AND I$<>"PALADIN" THEN GOTO 5250
5210 IF A(1)<>18 THEN GOTO 5250
5220 LET A(7)=INT (1+100*RND)+10
5225 IF A(7)>100 THEN LET A(7)=100
5250 CLS
5260 PRINT "BETWEEN WHAT LEVELS DO YOU WANT? (LOW THEN HIGH)"
5262 INPUT A
5264 INPUT B
5270 LET LE=INT (1+((B-A)+1)*RND)+(A-1)
5280 IF I$<>"FIGHTER" AND I$<>"PALADIN" THEN GOTO 5310
5290 FOR A=1 TO LE
5300 LET B=INT (1+10*RND)+2
5302 IF B>10 THEN LET B=10
5304 LET HP=HP+B
5305 NEXT A
5310 IF I$<>"RANGER" AND I$<>"CLERIC" AND I$<>"DRUID" THEN GOTO 5340
5320 IF I$="RANGER" THEN LET HP=INT (1+8*RND)+2
5325 IF HP>8 THEN LET HP=8
5330 FOR A=1 TO LE
5332 LET B=INT (1+8*RND)+2
5334 IF B>8 THEN LET B=8
5336 LET HP=HP+B
5338 NEXT A
5340 IF I$<>"MAGIC-USER" AND I$<>"ILLUSIONIST" THEN GOTO 5360
5350 FOR A=1 TO LE
5352 LET B=INT (1+4*RND)+1
5354 IF B>4 THEN LET B=4
5356 LET HP=HP+B
5358 NEXT A
5360 IF I$<>"THIEF" AND I$<>"ASSASSIN" AND I$<>"MONK" THEN GOTO 5400
5365 IF I$="MONK" THEN LET HP=INT (1+6*RND)+2
5370 IF HP>6 THEN LET HP=6
5380 FOR A=1 TO LE
5382 LET B=INT (1+6*RND)+2
5384 IF B>6 THEN LET B=6
5386 LET HP=HP+B
5388 NEXT A
5400 IF A(5)>14 THEN LET A(9)=1
5405 IF A(5)>15 THEN LET A(9)=A(9)+1
5410 IF I$<>"FIGHTER" AND I$<>"RANGER" AND I$<>"PALADIN" THEN GOTO 5430
5415 IF A(5)>16 THEN LET A(9)=A(9)+1
5420 IF A(5)>17 THEN LET A(9)=A(9)+1
5440 IF A(4)>14 THEN LET A(8)=A(8)+1
5445 IF A(4)>15 THEN LET A(8)=A(8)+1
5450 IF A(4)>16 THEN LET A(8)=A(8)+1
5455 IF A(4)>17 THEN LET A(8)=A(8)+1
5460 IF A(5)<15 THEN GOTO 5485
5470 LET HP=(LE*A(9))+HP
5471 IF I$<>"RANGER" AND I$<>"MONK" THEN GOTO 5485
5472 IF I$="MONK" AND A(5)=15 THEN LET A(9)=A(9)+1
5473 IF I$="MONK" AND A(5)>=16 THEN LET A(9)=A(9)+2
5476 IF I$="RANGER" AND A(5)=17 THEN LET A(9)=A(9)+3
5477 IF I$="RANGER" AND A(5)=18 THEN LET A(9)=A(9)+4
5478 IF I$="RANGER" AND A(5)=16 THEN LET A(9)=A(9)+2
5479 IF I$="RANGER" AND A(5)=15 THEN LET A(9)=A(9)+1
5480 LET HP=A(9)+HP
5485 IF I$="MONK" OR I$="RANGER" THEN LET A(9)=A(9)/2
5500 REM GOLD
5505 IF I$="CLERIC" OR I$="DRUID" THEN LET GO=(INT (1+6*RND)+INT (1+6*RND)+INT (1+6*RND))*10
5510 IF I$="FIGHTER" OR I$="RANGER" OR I$="PALADIN" THEN LET GO=(INT (1+4*RND)+INT (1+4*RND)+INT (1+4*RND)+INT (1+4*RND)+INT (1+4*RND))*10
5515 IF I$="MAGIC-USER" OR I$="ILLUSIONIST" THEN LET GO=(INT (1+4*RND)+INT (1+4*RND))*10
5520 IF I$="THIEF" OR I$="ASSASSIN" THEN LET GO=(INT (1+6*RND)+INT (1+6*RND))*10
5525 IF I$="MONK" THEN LET GO=(INT (1+4*RND)+INT (1+4*RND)+INT (1+4*RND)+INT (1+4*RND)+INT (1+4*RND))
5530 IF LE>1 THEN LET GO=GO*10
5535 IF LE>2 THEN LET GO=GO*1.5
5540 IF LE>3 THEN LET GO=GO*1.5
5545 IF LE>4 THEN LET GO=GO*1.6
5550 IF LE>7 THEN LET GO=GO*1.4
5555 IF LE>10 THEN LET GO=GO*1.4
5560 IF LE>15 THEN LET GO=GO*1.4
5565 IF LE>20 THEN LET GO=GO*1.4
5567 LET GO=INT GO
5570 IF A(1)<16 THEN GOTO 5600
5575 IF A(1)=16 THEN LET G$=" ,+1"
5576 IF A(1)=17 THEN LET G$="+1,+1"
5577 IF A(1)=18 AND A(7)=0 THEN LET G$="+1,+2"
5578 IF A(1)=18 AND A(7)>0 AND A(7)<51 THEN LET G$="+1,+3"
5579 IF A(1)=18 AND A(7)>50 AND A(7)<76 THEN LET G$="+2,+3"
5580 IF A(1)=18 AND A(7)>75 AND A(7)<91 THEN LET G$="+2,+4"
5581 IF A(1)=18 AND A(7)>90 AND A(7)<100 THEN LET G$="+2,+5"
5582 IF A(1)=18 AND A(7)=100 THEN LET G$="+3,+6"
5590 IF I$<>"MONK" THEN GOTO 5600
5592 LET G$=" "
5600 IF LE<2 THEN GOTO 6000
5601 LET B(1)=0
5602 LET B(2)=0
5603 LET B(3)=0
5604 LET B(4)=0
5605 LET B(5)=1
5606 LET B(6)=1
5607 PRINT "DO YOU WANT MAGIC ITEMS ? (Y/N)"
5608 INPUT A$
5609 IF A$="N" THEN GOTO 8000
5610 IF LE<>2 THEN GOTO 5615
5615 LET B(1)=(LE-1)*10
5620 IF LE<3 THEN LET B(5)=1
5625 IF LE<7 THEN LET B(5)=2
5630 IF LE>=7 THEN LET B(5)=3
5635 LET B(2)=(LE-3)*10
5640 IF LE<7 THEN LET B(6)=1
5645 IF LE>=7 THEN LET B(6)=2
5650 LET B(3)=(LE-7)*10
5655 LET B(4)=(LE-11)*10
5660 IF B(2)<1 THEN LET B(2)=0
5665 IF B(3)<1 THEN LET B(3)=0
5670 IF B(4)<1 THEN LET B(4)=0
5745 DIM R(8)
5800 CLS
5805 DIM J$(7,30)
5880 IF LE<2 THEN GOTO 6000
5881 LET J=4
5882 LET G=0
5883 LET L=1
5884 FOR A=1 TO B(5)
5888 LET C=INT (1+100*RND)
5890 IF C>B(1) THEN GOTO 5916
5892 LET C=INT (1+18*RND)
5898 PRINT AT 2,1;"CAN A ";LE;" ";C$;" USE THIS ?"
5899 LET H=C
5900 PRINT AT 1,1;"Y/N"
5902 GOSUB 6990
5903 INPUT A$
5904 IF A$="N" THEN GOTO 5892
5908 LET J$(L)=H$
5910 PRINT AT J,1;J$(L)
5911 LET J=J+1
5912 LET L=L+1
5916 LET G=G+1
5917 IF C>B(1) THEN LET C=0
5918 LET R(G)=C
5920 NEXT A
5922 LET J=7
5924 LET L=4
5926 LET G=3
5928 FOR A=1 TO B(6)
5932 LET C=INT (1+100*RND)
5934 IF C>B(2) THEN GOTO 5948
5935 LET D=INT (1+16*RND)
5937 LET H=D
5939 PRINT AT 1,1;"Y/N"
5943 GOSUB 7100
5944 INPUT A$
5945 IF A$="N" THEN GOTO 5935
5946 LET J$(L)=H$
5947 PRINT AT J,1;J$(L)
5948 LET J=J+1
5949 LET L=L+1
5950 LET G=G+1
5951 IF C>B(2) THEN LET D=0
5952 LET R(G)=D
5953 NEXT A
5954 LET L=6
5955 LET G=5
5956 FOR A=1 TO 1
5957 LET J=9
5958 LET C=INT (1+100*RND)
5959 IF C>B(3) THEN GOTO 5973
5960 LET E=INT (1+16*RND)
5962 LET H=E
5964 PRINT AT 1,1;"Y/N"
5966 GOSUB 7200
5967 INPUT A$
5968 IF A$="N" THEN GOTO 5960
5969 LET J$(L)=H$
5970 PRINT AT J,1;J$(L)
5971 LET J=J+1
5972 LET L=L+1
5973 LET G=G+1
5974 IF C>B(3) THEN LET E=0
5976 LET R(G)=E
5978 NEXT A
5979 LET J=10
5980 LET L=7
5981 LET G=6
5982 FOR A=1 TO 1
5983 LET C=INT (1+100*RND)
5984 IF C>B(4) THEN GOTO 5994
5985 LET F=INT (1+12*RND)
5987 LET H=F
5989 GOSUB 7300
5990 INPUT A$
5991 IF A$="N" THEN GOTO 5985
5992 LET J$(L)=H$
5993 PRINT AT J,1;J$(L)
5994 LET J=J+1
5995 LET L=L+1
5996 LET G=G+1
5997 IF C>B(4) THEN LET F=0
5998 LET R(G)=F
5999 NEXT A
6000 GOTO 8000
7000 IF H=1 THEN LET H$="POTION, CLIMBING OR FLYING"
7005 IF H=2 THEN LET H$="POTION, EXTRA-HEAL OR POLYMORPH"
7010 IF H=3 THEN LET H$="POTION, RESIST-FIRE OR SPEED"
7015 IF H=4 THEN LET H$="POTION, HEAL OR GIANT STRENGTH"
7020 IF H=5 THEN LET H$="POTION, HEROISM OR INVULNRBLITY"
7025 IF H=6 THEN LET H$="POTION; HUMAN CONTROL/LEVITATE"
7030 IF H=7 THEN LET H$="POT.;S-HEROISM/ANIMAL CONTROL"
7035 IF H=8 THEN LET H$="SCROLL; 1 SPELL, LEVEL 1-6"
7040 IF H=9 THEN LET H$="SCROLL; 1 PROT. FROM MAGIC"
7050 IF H=10 THEN LET H$="1 RING; MAMMAL CONTROL"
7055 IF H=11 THEN LET H$="1 RING; +1 PROTECTION"
7060 IF H=12 THEN LET H$="ARMOR; +1 LEATHER"
7065 IF H=13 THEN LET H$="SHIELD; +1 SHIELD"
7070 IF H=14 THEN LET H$="SWORD; +1 SWORD"
7075 IF H=15 THEN LET H$="10 ARROWS; +1"
7080 IF H=16 THEN LET H$="DAGGER; +1"
7090 IF H=17 THEN LET H$="JAVELIN; +2"
7095 IF H=18 THEN LET H$="MACE; +1"
7096 PRINT AT 12,1;" "
7097 PRINT AT 12,1;H$
7099 RETURN
7100 REM TABLE 2
7105 IF H=1 THEN LET H$="1 SCROLL; 3 SPELLS, 2-9 LEVEL"
7110 IF H=2 THEN LET H$="1 RING; INVISIBILITY"
7115 IF H=3 THEN LET H$="STAFF; STRIKING"
7120 IF H=4 THEN LET H$="RING; +3 PROTECTION"
7125 IF H=5 THEN LET H$="WAND; ILLUSION"
7130 IF H=6 THEN LET H$="WAND; NEGATION"
7135 IF H=7 THEN LET H$="BRACERS; AC 4"
7140 IF H=8 THEN LET H$="BROOCH OF SHIELDING"
7145 IF H=9 THEN LET H$="CLOAK OF ELVENKIND"
7150 IF H=10 THEN LET H$="DUST OF APPEARNCE"
7155 IF H=11 THEN LET H$="FIGURINE-; ONYX DOG"
7160 IF H=12 THEN LET H$="3 JAVELINS OF LIGHTNING"
7165 IF H=13 THEN LET H$="ARMOR SET; +1 CHAIN +2 SHIELD"
7170 IF H=14 THEN LET H$="ARMOR; +4 SPLINT MAIL"
7175 IF H=15 THEN LET H$="SWORD; +3"
7180 IF H=16 THEN LET H$="CROSSBOW/SPEED OR +2 HAMMER"
7190 PRINT AT 12,1;" "
7192 PRINT AT 12,1;H$
7194 RETURN
7200 REM TABLE 3
7205 IF H=1 THEN LET H$="RING OF SPELL STORING"
7210 IF H=2 THEN LET H$="ROD OF CANCELLATION"
7215 IF H=3 THEN LET H$="STAFF; SERPENT"
7220 IF H=4 THEN LET H$="FIGURINE-; SERPENTINE OWL"
7225 IF H=5 THEN LET H$="BOOTS OF SPEED"
7230 IF H=6 THEN LET H$="BOOTS OF STRIDING/LEAPING"
7235 IF H=7 THEN LET H$="CLOAK OF DISPLACEMENT"
7240 IF H=8 THEN LET H$="GAUNTLETS OF OGRE POWER"
7245 IF H=9 THEN LET H$="PIPES OF THE SEWERS"
7250 IF H=10 THEN LET H$="ROBE OF BLENDING"
7255 IF H=11 THEN LET H$="ROPE: CLIMBING/ENTANGLEMENT"
7260 IF H=12 THEN LET H$="ARMOR SET; +2 PLATE +3 SHIELD"
7265 IF H=13 THEN LET H$="+5 SHIELD"
7270 IF H=14 THEN LET H$="SWORD; +4 DEFENDER"
7275 IF H=15 THEN LET H$="MACE +3"
7280 IF H=16 THEN LET H$="SPEAR +3"
7285 PRINT AT 12,1;" "
7290 PRINT AT 12,1;H$
7295 RETURN
7300 REM TABLE 4
7305 IF H=1 THEN LET H$="RING; REGENERATION"
7310 IF H=2 THEN LET H$="RING; SPELL TURNING"
7315 IF H=3 THEN LET H$="WAND; OF FIRE"
7320 IF H=4 THEN LET H$="WAND; OF FROST"
7325 IF H=5 THEN LET H$="EYES OF CHARMING"
7330 IF H=6 THEN LET H$="HORN OF VALHALLA"
7335 IF H=7 THEN LET H$="BEAKER OF PLENTIFUL POTIONS"
7340 IF H=8 THEN LET H$="DUST OF SNEEZING AND CHOKING"
7345 IF H=9 THEN LET H$="DUST OF DISAPEARANCE"
7350 IF H=10 THEN LET H$="ARMOR SET; +2 FIELD/+4 SHIELD"
7355 IF H=11 THEN LET H$="SWORD; +2 GIANT SLAYER"
7360 IF H=12 THEN LET H$="ARROW OF SLAYING"
7365 PRINT AT 12,1;" "
7370 PRINT AT 12,1;H$
7375 RETURN
8995 SLOW
8999 CLS
9000 PRINT AT 1,1;E$;" ";B$;" AGE ";I;" LEVEL ";LE
9005 PRINT " ";I$
9010 PRINT "STRENGTH ";A(1);"/";A(7);" ";G$
9015 PRINT "INTELLIGENCE ";A(2)
9020 PRINT "WISDOM ";A(3)
9025 PRINT "DEXTERITY ";A(4);" -";A(8);" AC"
9030 PRINT "CONSTITUTION ";A(5);" +";A(9);" HP"
9035 PRINT "CHARISMA ";A(6)
9045 PRINT " HIT POINTS ";HP;" GOLD"
9050 PRINT " ";GO
9060 FOR L=1 TO 7
9062 PRINT J$(L)
9064 NEXT L
9068 PRINT "%A%N%O%T%H%E%R% %C%H%A%R%A%C%T%E%R%?%? (Y/N) OR A HARDCOPY OF ";E$
9069 INPUT Q$
9070 IF Q$="N" THEN GOTO 9080
9072 IF Q$="Y" THEN CLS
9075 IF Q$="Y" THEN RUN
9076 IF Q$="COPY" THEN COPY
9077 GOTO 9069
9080 STOP
9090 CLEAR
9100 SAVE "1020%4"
9110 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
