Your Spectrum
Issue 5, July 1984 - Random Number Routine
Home Contents KwikPik
A   C H A N C E
 
E N C O U N T E R

As luck would have it, Toni Baker has come up with an interesting way to access true random numbers - with a little help from machine code, of course.
As many of you will know, random numbers on the Spectrum are not truly random - they may appear to be random, but they are in fact totally pre- determined. For instance, RANDOMIZE 42 followed by PRINT RND,RND will always give the same numbers: 0.049194336 and 0.69065857; I haven't cheated by using the RANDOMIZE statement, I've just used it to make the point easier to demonstrate - RND does not rely on chance.

RANDOM THOUGHTS

Now when you play any sort of game which involves the throwing of dice or the tossing of coins, then one would usually agree that the outcome is truly random. (At this point, the philosophers amongst us may like to have a quick debate on the various merits of free will versus determinism.) Isaac Newton would tell us that if we knew all the variables involved (velocity of dice, density of air, etc) then we could work out in advance which number would get thrown; on the other hand, Schrodinger's Cat tells us differently. Maybe the world is flat after all.
So now you should be able to see the subtle difference between the formula INT (6*RND)+1 and throwing a dice. However many suitable tests for randomness you may care to think up, one overriding fact remains - that, whereas dice-rolling involves the mysterious hand of fate, the RND formula is completely predetermined. The outcome of a dice game is fixed before you even press RUN.
You can add a little bit of chance into the game by putting a RANDOMIZE statement into your program somewhere (without a number after RANDOMIZE). This will confuse the issue a bit, but it still won't stop the remainder of the game being just as predetermined as ever. What you could do instead is to precede every single use of RND by either an INPUT statement or a PAUSE 0 statement, followed by RANDOMIZE. For instance:

PAUSE 0: RANDOMIZE: LET d=INT (6*RND)+1

This will prevent the game from being predetermined by relying on the fact that a human player will not necessarily
wait the same number of TV frames before pressing a key; it'll load the random number seed with a different value each time and hence give you a different random number each time.

A TRUE DEFINITION

But I'd like introduce you to another approach. I'm going to define a user- defined function called FN R(X) (the number inside the bracket is important). Here's what happens when it's reached in the middle of a Basic statement. Firstly, control will PAUSE until a key is pressed, and then the computer will
select a random integer between one and the number inside the brackets. This new random number function is not predetermined, and as in the above example, its differing possible outcomes are strictly the result of human intervention.
Unlike the previous example, however, the determining factor is not how long you take to press a key - it's which key you press! In other words - suppose you used FN R(6) to represent throwing a dice. At this point the action would PAUSE. The human player would be aware that the outcome of this dice throw is determined only by which of the keys they decide to press, but does not know, and can never know,
CODEASSEMBLERCOMMENTS
CD8E02
1C
20FA
CHANCE    CALL KEY_SCAN
          INC E
          JR NZ,CHANCE
DE: = immediate keyboard scan.
 
Waits until your finger is released
from the last key pressed.
CD8E02
1C
28FA
43
 
C5
ED4B765C
CD2B2D
 
C1
EF
A1
0F
NEW_KEY   CALL KEY_SCAN
          INC E
          JR Z,NEW_KEY
          LD B,E
 
          PUSH BC
          LD BC,(SEED)
          CALL STACK_BC
 
          POP BC
          RST 28
          STACK 1
          ADD
DE: = immediate keyboard scan.
 
Waits until a new key is pressed.
B: = code corresponding to the key
pressed.
 
BC: = random number seed.
Pushes this seed onto the
calculator stack.
B: = key code.
Uses the floating point calculator.
S,1
S+1
343716
04
348041
000080
32
 
02
35F3
A1
03
38
CH_LOOP   STK DATA 75
          MULT
 
          STK DATA 65537
          MOD
 
          DELETE
          DJNZ CH_LOOP
          STACK 1
          SUBTRACT
          END CALC
S+1,75
75*(S+1)
 
75*(S+1),65537
75*(S+1) MOD 65537,75*(S+
1) DIV 65537
75*(S+1) MOD 65537
SEED+1
SEED+1,1
SEED
And back to machine code ...
CDA22D
          CALL FP_TO_BC
BC: = new random seed number.
ED43765C
          LD (SEED),BC
Stores this new seed.
C9
          RET
Returns to Basic. Note that this
final value will be divided by
65536 to give a final random
number.

which key gives which result. As in real life, the human being determines their own fate, and synchronicity alone choose the winners and losers.
The algorithm I've used to define FN R is an almost direct rip-off of the RND feature in the ROM, except that I've added a bit to allow for human intervention. It's mostly a machine code routine, but the machine code only works out a random number between zero and 65535, and so a bit of multiplying, dividing and INTing is required to get it into range. See if you can write a new Basic DEF FN statement to define, say, FN S() as a random decimal between zero and one - you can use exactly the same USR call (that is, my machine code) as I've used for FN R(X) but you'll need to change the rest of the statement.

CHINESE CHANCE

For those of you with an inquiring mind (all of you, I hope) look carefully at how I've used the floating point calculator by bringing in the RST 28 instruction. You don't need to understand all the the calculator instruction codes (I'll admit that 348041000080 is a bit confusing) but if you can get the drift of how in principle the calculator instructions manipulate the calculator stack, then you're well on the way to the light at the end of the tunnel.
I've used a bit of computer jargon here, which I'd better explain. We all know that 15 divided by seven is two remainder one. In fractions, it's 21/7, and in decimals it's 2.142857 ... but let's just stick to integers - it's easier. In general, A divided by B is Q remainder R. In computerese we can write 'A DIV B' to mean Q, and 'A MOD B' to mean R. Thus, in the above example 15 DIV 7 equals two, and 15 MOD 7 equals one. Mathematically we can say that A DIV B equals INT (A/B) - see if you can work out what the mathematical formula for A MOD B is. Anyway, that explains all the unknowns - let's move on now to a bit of program.
This is a Basic program for throwing
Chinese coins. The question marks in the last line refer to the address of the machine code routine labelled CHANCE, which is listed separately. See you up there.

10 FOR i=1 TO 6
20 LET c=FN r(2)+FN r(2)+FN r(2)
30 PRINT AT 6-i,0; "---";
40 IF c>4 THEN PRINT " ";
50 It c<5 THEN PRINT "-";
60 PRINT "---"
70 NEXT i
80 DEF FN r(x)=INT (x*USR ?????/65536)+1
cartoon
Home Contents KwikPik