Random Numbers

[Table of Contents]
RANDOM Disk Image

I am not an expert on random number generation algorithms, nor do I want to be. I use terms pseudo-random and random interchangeably, when I know that they are not. I’m simply exploring the functionality available in Kyan Pascal 2.x. That being said …. 

Whether a program is simulating a card game, rolling of dice, or determining the outcome of melee combat deep in the dungeon of Deceit, many game programs (and other types of programs as well) require the use of random number generation.

Kyan Pascal includes a few facilities for pseudo-random number generation. The Kyan Pascal User’s Manual has two short paragraphs on random numbers, I managed to fill almost 18 pages with my random musings.

Let’s take a look.

What’s on the disk?


RANDOM.I - Include file from Kyan Pascal 2.x Disk #2
RANDOMS.I - Include file from Kyan Pascal Utility Disk #1
RAND1.P
RAND2.P
RAND3.P
RAND4.P
RAND5.P
RAND6.P
RAND7.P
RAND1
RAND2
RAND3
RAND4
RAND5
RAND6
RAND7

Kyan Pascal 2.x Disk #2


On Kyan Pascal Disk #2, there is an include file called RANDOM.I. This include file has one function:

FUNCTION Random: Real;  

The Kyan Pascal User's Manual states that: 

... calling the function Random in the body of the program will return a random number between 0 and 1.

RAND1


My testing seems to show that it actually generates a random Real Number between -32,768.0 and +32,767.0. It also generates the same series of random numbers over and over. If you reboot and rerun the program, you will get the exact same series of random numbers. As there is only one function in the include file, there isn't any way to “seed” the random number generator to generate different results. 

Source Code


(* RAND1.P *)
(* Kyan Pascal 2.x / Atari 8-bit *)

Program RAND1(Input,Output);

var
  x : integer;
  i : integer;
  r : real; 
  av : real;

#i random.i

(* Main *)
begin
   write(chr(125)); (* Clear screen *)

   for x := 0 to 10 do
   begin
      r := random;
      writeln('Real: ',r:1:4);

      i := round(r);
      writeln('Integer: ',i);
   end;
end.

Sample Run



RAND2


If you need to generate a random integer number in a particular range, between 1000 and 2000, for example, you have to do a little bit of algebraic gymnastics.


Source Code


(* RAND2.P *)
(* Kyan Pascal 2.x / Atari 8-bit *)

Program RAND2(Input,Output);

var
  x : integer;
  i : integer;
  r : real; 

#i random.i

(* Main *)
begin

   write(chr(125)); (* Clear screen *)

   for x := 1 to 10 do
   begin   
      r := random; (* generate a 'random' number *)
      i := round(r); (* convert it to an integer *)
      i := abs(i); (* make sure it is a positive integer *)
      i := (i mod (2000-1000)) + 1000; (* desired range *)
      writeln('Integer: ',i);
   end;

end.

Sample Run




Kyan Pascal Utility Disk #1


The Kyan Pascal 2.x Utilities Disk #1 has an additional include file, RANDOMS.I, with more random number generation functionality.

RANDOMS.I


PROCEDURE SEED(NUM1, NUM2, NUM3, NUM4:INTEGER)
FUNCTION RND:REAL
FUNCTION RANDOM(MIN,MAX:INTEGER):INTEGER
FUNCTION RANDOM_BYTE:INTEGER

RAND3


Here is a sample program testing the random number generators from RANDOMS.I, without using the seed procedure.

Source Code


(* RAND3.P *)
(* Kyan Pascal / Atari 8-bit *)

Program RAND3(Input,Output);
var
   r : real;
   i : integer; 
   b : integer; 
   x : integer;

#i randoms.i

begin
   write(chr(125)); (* clear screen *)

   for x := 1 to 10 do
   begin
      r := rnd;
      i := random(1,10000);
      b := random_byte;
      writeln('Real: ',r:2:2,' Int: ',i:5,' Byte: ',b:3); 
   end;
end.

Sample Run #1




Note that I originally had 

i := random(0,32767);

but I apparently stepped on some boundary and got weird screen results and run-time errors. Try it.  

Sample Run #2


After rebooting and rerunning the program, I got the same sequence of Real numbers and Integer numbers, but the generated Byte numbers are different as shown below.



RAND4


In RAND4, the seed procedure is used to seed the random number generator. Sample Run #1 shows that a different sequence of numbers was indeed generated when compared to RAND3. Sample Run #2 shows that same sequence of Real numbers, and Integer numbers, as is expected because you would need to change the seed to generate a different sequence of numbers. The random Byte generator doesn’t seem to pay any attention to the seed value at all. The explanation for why will be addressed later. 

Source Code


(* RAND4.P *)
(* Kyan Pascal / Atari 8-bit *)

Program RAND4(Input,Output);
var
   r : real;
   i : integer; 
   b : integer; 
   x : integer;

#i randoms.i

begin
   write(chr(125)); (* clear screen *)

   seed(16,32,64,128);

   for x := 1 to 10 do
   begin
      r := rnd;
      i := random(1,10000);
      b := random_byte;
      writeln('Real: ',r:2:2,' Int: ',i:5,' Byte: ',b:3); 
   end;
end.

Sample Run #1




Sample Run #2




RAND5


In sample program rand5.p, I changed the seed values to

seed(162,163,232,100);

And indeed, changing the seed values changed the results. 

Sample Run 




Note: I used a Random Number Generator site to generate four random numbers between 0 and 255 that I used for the seed procedure.

So, there is a bit of a catch-22. We need to generate four random bytes between 0 and 255 to seed the random number generator to generate a sequence random of numbers. I guess the user of a program could be prompted to enter four random numbers to seed the random number generator, but that is not very user-friendly. 

Maybe there is another way. Maybe we can have the Atari generate four random numbers to seed the random number generator.

RAND6


There are some memory locations on the Atari that can possibly give us some random, or at least some constantly changing numbers. A few quickly come to mind. Address 18, 19, and 20 are the Atari’s Real Time Clock memory locations. Address 20 changes with each vertical blank interrupt. When 20 gets to 255, memory location 19 gets incremented by 1 and 20 resets to 0. When memory location 19 gets to 255, 18 gets incremented by 1, and 19 and 20 get reset to 0. You could plug these numbers into the seed procedure. 

You can read more about these memory locations on page 7 of Mapping The Atari Revised Edition by Ian Chadwick.

 A PEEK function is implemented in the Kyan Pascal User’s Manual. It is include in RAND6.P.  This function can be used to watch the changing values in the Real Time Clock memory locations.

Source Code


(* RAND6.P *)
(* Kyan Pascal 2.x / Atari 8-bit *)

Program RAND6(Input,Output);
var
   x : integer;

Function Peek(Loc: Integer) : Integer;
Begin
   Peek := 0;

#a
   LDY #7
   LDA (_SP),Y
   STA _T
   INY
   LDA (_SP),Y
   STA _T+1;
   LDY #0;
   LDA (_T),Y

   LDY #5
   STA (_SP),Y

   INY
   LDA #0

   STA (_SP),Y
#
End;

(* Main *)
Begin

   write(chr(125));

   for x := 1 to 10 do
   begin

      writeln(peek(20):4,peek(19):4,peek(18):4);
   
   end;
End.


Sample Run




But there is an easier way. The Pokey chip can supply a random number between 0 and 255 by reading (peeking) memory location 53770. 

If you look at the code for the Random_Byte function in RANDOMS.I, you will see that this is exactly what the function is doing and this is why Random_Byte ignores the SEED procedure.

Here is the code for the Random_Byte function from RANDOMS.I:

FUNCTION Random_Byte: Integer;
    BEGIN
        RANDOM_BYTE:=0;
#A
  LDA $D20A    ;get RANDOM (53770)
  LDY #5       ;offset to ISO_Var
  STA (_SP), Y ;...store it
#
END;(* Random Byte function *) 

If you need a random number between 0 and 255, just use the Random_Byte function in the RANDOMS.I include file on the Kyan Pascal Utility Disk #1 directly. No need to seed. You can use the result := (Random_Byte mod (high - low)) + low; equation to get within a specific range as long as the numbers are all below 255. 


RAND7


If you need another type of random number, use the Random_Byte function to generate numbers for the SEED procedure.

SEED(Random_Byte,Random_Byte,Random_Byte,Random_Byte);

Source Code


(* RAND7.P *)
(* Kyan Pascal / Atari 8-bit *)

Program RAND7(Input,Output);
var
   r : real;
   i : integer; 
   x : integer;

#i randoms.i

begin
   write(chr(125)); (* clear screen *)

   seed(random_byte,random_byte,random_byte,random_byte);

   for x := 1 to 10 do
   begin
      r := rnd;
      i := random(1,10000);
      writeln('Real: ',r:2:2,' Int: ',i:5); 
   end;
end.
   

Sample Run #1



Sample Run #2




One additional note, if you try to use the PEEK function mentioned above with a memory address greater than 32,767, you will get an error. Kyan Pascal can only handle integers from -32,768 to +32,767. 

For example, if you try to use RESULT =: PEEK(53770); you will get an error. 

I reached out to the Atari gods for some ideas and ...

You can peek at location 53,770, but you have to use the 2’s complement of 53,770 to do so.

Here is how:

  • 53,770 = 1101 0010 0000 1010 in binary.
  • Start from the right and move left until you find the first ‘1’. 
  • Every number AFTER the first ‘1’ (again, moving right to left) gets flipped. 
  • The result of the flipping should be 0010 1101 1111 1010. 
  • Convert this binary number back to its decimal equivalent, 11,766. 
  • Then negate it to -11,766. 

So RESULT =: PEEK(-11766); gives you the value in memory location 53,770.

No comments:

Post a Comment