How was randomness achieved in older video games?

I’m not entirely sure why the 0 would be exclusive, but everything I’ve been able to find elsewhere on the RND function is that “1” cannot be returned (when the RND function returns a floating point number between 0 and 1, as opposed to RND functions that return integers in other forms of BASIC). I assume that’s because the highest floating point value represented by all binary 1s (or however the maximum value is represented) would just be under one.

And on some systems it would generate a number between 1 and A+1.

Which is why, before I wrote a BASIC program that relies on random numbers, the first thing I do is run a program like this one

10 x=RND(0)
20 IF x = 0 THEN GOTO 50
30 IF x=1 THEN GOTO 50
40 GOTO 10
50 PRINT x

This checks to see if, when it generates a random number, it is generating a number between 0 and … almost one, or between almost zero and one. Because some systems do one, others do the other.

I found out I’d have to do this when a program I wrote that would generate the results of 3d6 (that is, the sum of three random integers from 1 to 6) returned a result of 2.

Interesting. I wonder what systems had it implemented that way. How long did you generally run the program before you were reasonably sure it never generated 1?

(ETA: For example, the documentation I have for the C64 says the number generator generates 0, inclusive, to 1, exclusive, but I’m not sure I believe it actually generates a zero value. I’ve had your basic program running for several minutes, and it’s still just looping.)

Pretty sure this is how I did it in DOS back in 1988 or 89.

Random Numbers

The Windows CMD shell contains a dynamic variable called %RANDOM% that can be used to generate random numbers.

%RANDOM% generates a random integer from 0 to 32,767 (inclusive)

0 ≤ %RANDOM% ≤ 32767

The range of numbers can be made smaller than 32767 with a little arithmetic,
for example to generate a range between 1 and 500:

@ECHO OFF
SET /a _rand=(%RANDOM%*500/32768)+1
ECHO Random number %_rand%

And, out of curiosity, how do you account for the one in your code, then? That’s a pain in the ass, because “1.0000” exactly will only show up very, very rarely, almost never, but if you want to generate an integer between 1 and 10, at least using the INT(RND(0)*10)+1 format you need a conditional to account for this extreme side case. It doesn’t really make sense to me to implement RND that way. Or is there another way to write the statement to get numbers from 1-10 in all cases?

Actually, if you have an RND function that returns 0 exclusive to 1 inclusive, I guess you could rewrite the INT function by using (1-RND (0)) and that should work just as well.

The way I did it starting back in the Apple II/C64 days, since I programmed for many different machines over the years, was as follows:

Get random number
multiply by (range needed + 2)
subtract 1
throw away any result above or below the range needed and redo

That way, you never have to worry about the specific machine having exclusive limits at either end. Both ends you actually use will be inclusive.

That seems to be unnecessarily complex and slow if you need to generate a large selection of random numbers by adding a conditional in there. Why not read the documentation for each machine? I ask specifically, because not only do you need to know what number range the RND function generates, but also because the number in between the paranetheses differs from machine to machine in behavior. For example, on a C64, RND(0) is terrible for ranges of numbers higher than 256, so when asking it for a range from 0-1000, say, a good 50%+ of numbers will never show up, while when using RND(1), you will generate all possible values. Applebasic’s RND is similar, where using RND(0) will not give you the results you want. It returns the last generated random number, so if you use a loop with RND(0), you will get the same value over and over.

So, since you have to read the documentation anyway to figure out the correct format, why not account for the values that system’s machine can generate?

This, among other things put forth in this thread, is why amateurs should never be put in charge of random number generation. What is the distribution for your code if you want the range from 1 to 32767?*

There have been many examples lately of slight flaws in RNGs used in encryption resulting in significant security holes.

Hint: you can’t map 2^15 values into a smaller non-power of two number of values and have an even distribution.

This is true if you’re doing security or real-money gambling, but most of the time programmers need random numbers it doesn’t matter if the distribution is very slightly wonky. The problem is when you use the same techniques for things that do.
(You almost certainly should be using a secure RNG library instead of rolling your own anyway. Especially for security because even if you get the distribution right you might screw up and invite timing attacks)

I should also mention that generating uniformly distributed floating points of the range MIN_FLOAT to MAX_FLOAT is non-trivial, because of the loss of precision at higher exponents. The incremental step in floating point isn’t even (that is, you’ll get different step sizes even by constantly doing float+= machine_epsilon). You will end up being biased towards lower-magnitude numbers. There have been papers published on good floating point RNG. Use a library with built-in distributions.