Seeding the RNG

I've looked through the code of a lot of old BASIC programs, and they all seem to have one omission: the lack of an RNG seeding routine. When you first turn on an 8-bit computer, the RNG is always initialized to the same seed every time. If you don't reseed it, the random numbers will be the same every time you start a program after booting. I'm going to show you some common methods to seed the RNG.

The first, and easiest, method is to ask the user to input a number. This technique should work on any version of BASIC you can think of. Here's the randomization routine from the Altair 8800 version of the Battle Test:

1 REM RANDOMIZATION ROUTINE
2 PRINT "TYPE A RANDOM NUMBER:"
3 INPUT Z
4 Y=RND(-Z)

As you can see, it asks the user to type a random number, it inputs that number to a variable, and it uses that variable to seed the RNG by passing the negative of the number to the RND function. The way RND usually works is that RND(1) generates a new random number, RND(0) returns the last random number generated, and RND(-<number>) seeds the RNG with the number provided.

TRS-80 Level 1 BASIC uses a variant on this technique. Instead of seeding the RNG with the user provided number, it calls the RND function in a loop to move the number down the list of generated numbers.

1 REM RANDOMIZATION ROUTINE
2 INPUT "ENTER A NUMBER BETWEEN 1 AND 100:";Z
3 FOR Y=1 TO Z
4 A=RND(32767)
5 NEXT Y

The best way by far to seed the RNG is by using timing. In its simplest form, it uses a loop to increment the number that will be passed to the RND function. Here's an example from the CP/M BASIC-80 v5 version:

1 REM RANDOMIZATION ROUTINE
2 PRINT "PRESS A KEY"
3 Z=0
4 Z=Z+1
5 A$=INKEY$
6 IF A$="" THEN 4
7 Y=RND(-Z)

It sets a variable to zero, and then it enters a loop that increments the number by one each time, until the user presses a key. Some versions of BASIC, such as CBM BASIC, have a dedicated timer variable that can be referenced, obviating the need to increment:

1 REM RANDOMIZATION ROUTINE
2 PRINT "PRESS A KEY:"
3 GET A$
4 IF A$="" THEN 3
5 Z=RND(-TI)

With CBM BASIC, the timer variable is called TI. It results in the code being a little bit shorter. There are other versions of BASIC that have a system timer that isn't referenced by a variable. In those cases, you need to PEEK a pair of memory locations, and then turn those into a combined number like so:

1 REM RANDOMIZATION ROUTINE
2 PRINT "80-COLUMNS?"
3 GET A$
4 Z=RND(-1*(PEEK(78)+256*PEEK(79)))

The formula shown on line 4 can be used to turn any two 8-bit numbers into one 16-bit number.

Finally, there are some versions of BASIC that have a built-in command for randomization. For instance, TRS-80 Level 2 BASIC uses RANDOM to seed the RNG from the system timer. GW-BASIC uses RANDOMIZE TIMER to do the same thing. It's still good practice to wait on user input before seeding the RNG, as many emulators have an auto-start feature that might result in a program being loaded in such a way as to have the system timer be set identically every time you run the program.

There is one major exception to seeding the RNG, and that's with the Atari 8-bit computers. The RNG is automatically seeded on those from noise generated by the POKEY chip, and thus there is no need to seed the RNG on them, ever.

Comments