---------------------------------------------------------------------------
Brosco's Liberty Basic Resource Newsletter 
Issue 2 - April 1998
---------------------------------------------------------------------------

This issue is devoted to two topics:

1)  Subroutine: Random Number Generator (RNG)
             and
2)  Building reusable code

I will be using the RNG as my example for producing the reusable code, so
first we will describe what a RNG is. 

Until you have the need for one, its hard to imagine why anyone would
even want a RNG.  Well, it is commonly used in games programs, 
simulating rolling a dice or shuffling a deck of cards.  Its also used
in business.  When an accountant audits a company's books, they rarely
look at every entry - particularly with large companies.  Can you
imagine how many auditors would be required to check Microsoft's accounts
if every entry was to be scrutinized?  So they use a RNG to get a
representative sample from the books and just audit those.

You can use a RNG in two ways.  The first is to let it provide a purely
random set of numbers.  The second is when you want to be able to repeat
a specific set of random numbers.  Why would you want to repeat a set of
random numbers you ask? Have you every noticed some games give you the
option of 'playing the same game again' or 'play game number xxxxx'?
This is common in games of Solitaire - and the 'xxxxx' is a starting
point for the RNG.  For this value - the cards will always be shuffled
into the same sequence.  This starting point is refered to as a SEED.

This algorythm for a RNG was posted by Omar Sigurdsson on the LB4ALL
Message Board.  Thanks Omar.

    "Here is a simple random number generator that gives numbers 
     distributed uniformly over the interval A to B and repeats itself
     for the same seed.

    X(n+1) = ( C1 * X(n) + C2 ) mod C3
     where
       X(0) = seed number (0 <= seed <= 199017)
       C1 = 24298
       C2 = 99991
       C3 = 199017

The algorythm requires that you provide a SEED, to produce the first
number, and that number produced is also the Seed for the next, and so on.

The code to perform this in Liberty Basic is:

    Seed = 100
    C1=24298
    C2=99991
    C3=199017
    SeedTmp = C1 * Seed + C2
    Seed = SeedTmp - int( SeedTmp / C3 ) * C3  ' *** Note 1
    RN = Seed / C3

    DiceThrow = int(RN*6) + 1               ' *** Note 2
    print DiceThrow

' *** Note 1
'  The result of the calculation is stored to be the new SEED for the
'  next calculation
'
' *** Note 2
'  The RNG returns a number between 0 and 1, so to convert to a Throw
'  of the Dice, we multiply by 6 and add 1.  If we wanted to pick a
'  card from a deck of playing cards the code would be:
      Card = int(RN*52) + 1


Because I have set Seed = 100, this code will produce the same set of
numbers every time.  If we want to get a different set of random 
numbers, we need to change the value of Seed.  If we want to get different
random numbers every time that we run the program, we must find a way
of setting Seed to a random value.  We can do this in a number of ways.
First, we could get the TimerTicks from the CPU and just use the 
milliseconds portion - that would give us a reasonably random number.
Another method is to use Liberty Basic's RND function.  RND is a RNG,
but unfortunately, its not a very good one.  In fact, in the documentation
it is stated that it is really an Arbitary Number Generator.  What's the
difference?

If I use a RNG to simulate rolling 2 dice 10000 times, I should get a 
result where each number (one thru six) comes up roughly the same 
amount of times. Plus, it should also throw a double one sixth of 
the times.  With the LB Rnd fuction, the results vary too much from
what would happen in the real world, and the players of the game 
would say that the game is RIGGED!  In fact, some of the older 
Arcade games were avoided for exactly this reason.  They did not
use a good RNG that simulated a real world result. 

With the tests I've done with the algorythm posted by Omar, I got
good results for all combinations that I tried.

Back to the code.  Here's the code to SEED our RNG with a random 
number at the start.  Plus, I've made it into a subroutine so that
you can call it from anywhere in your program.

    Seed = rnd(1) * 199017
    gosub [RandomN]
    DiceThrow = int(RN*6) + 1               ' *** Note 2
    print DiceThrow
    
    input var$

[RandomN]
    C1=24298
    C2=99991
    C3=199017
    SeedTmp = C1 * Seed + C2
    Seed = SeedTmp - int( SeedTmp / C3 ) * C3
    RN = Seed / C3
    return



You could take this code and paste it into your programs, but you
need to be careful that the variable names I use, aren't also used 
somewhere in your program - otherwise you may get unexpected results.

To avoid this problem, I try to write subroutines in such a way that
they can be 'cut and pasted' directly into my new program without
fear that the subroutine may corrupt variables used by my program.

To achieve this I use a very simple naming convention.  I've called
this subroutine [RandomN].  Now, every variable that I use in the 
subroutine will have a name of 'RandomN.xxxxx'.  Just check through
my new code for the function - it does exactly the same as the 
previous example - I've just changed the variable names.

    RandomN.Seed = rnd(1) * 199017

    gosub [RandomN]
    DiceThrow = int(RandomN.RN*6) + 1
    print DiceThrow

    input Var$

[RandomN]
    RandomN.C1=24298
    RandomN.C2=99991
    RandomN.C3=199017
    RandomN.SeedTmp = RandomN.C1 * RandomN.Seed + RandomN.C2
    RandomN.Seed = RandomN.SeedTmp - _
            int( RandomN.SeedTmp / RandomN.C3 ) * RandomN.C3
    RandomN.RN = RandomN.Seed / RandomN.C3
    return



The period (fullstop) in the variable name has no special meaning,
its just another valid character you can use when you are making up
variable names.  If this function had I needed a FOR....NEXT loop, I 
would have coded it like this:
    For RandomN.i = 1 to ...
    ....
    Next RandomN.i

So now that we have a working subroutine that won't corrupt program 
variables, can we save it?  NO!  It may be 6 months or more before you 
require this routine.  You will forget how it works, and what the Input 
and Output are of the function.  You may also forget to SEED the RNG 
before issuing the GOSUB. This means that you will waste valuable time
studying this code to see what variable names are used.  So what we do 
now is add some comments at the start to describe each Function within 
the subroutine, and each of the Input and Output variables.  We also 
embed the SEED code within the function.



    gosub [RandomN]
    DiceThrow = int(RandomN.RN*6) + 1
    print DiceThrow

    input Var$ 

' Random Number Generator
' Functions
'    [RandomN]  Generates Random Numbers starting with the SEED value
'               supplied in RandomN.Seed.  If RandomN.Seed = 0 then
'               a random seed will be selected.
'    INPUT:  RandomN.Seed value of 0 to 199017 to SEED the RNG. 
'    OUTPUT: RandomN.RN   Returns a random value between 0 and 1.
'
[RandomN]
    if RandomN.Seed = 0 then gosub [RandomN.SeedRNG]
    RandomN.C1=24298
    RandomN.C2=99991
    RandomN.C3=199017
    RandomN.SeedTmp = RandomN.C1 * RandomN.Seed + RandomN.C2
    RandomN.Seed = RandomN.SeedTmp - _
            int( RandomN.SeedTmp / RandomN.C3 ) * RandomN.C3
    RandomN.RN = RandomN.Seed / RandomN.C3
    return
[RandomN.SeedRNG]
    RandomN.Seed = rnd(1) * 199017
    return


Now I add one more thing.  I put some code at the start that demonstrates
the function.  So the function will now run as a standalone program and I 
can 'remind' myself about the way that it works.  I can also modify the
parameters here and test that it will work the way that I need in my new
program.  You can, of course, remove this part of the code when you are
'pasting' it to your own program.  You could remove the comments as well,
but I would recommend that you leave them there.

' Test Random Number Generator routine.
    for i = 1 to 10
        gosub [RandomN]
        DiceThrow = int(RandomN.RN*6) + 1
        print DiceThrow
        next i

    input Var$ 

' Random Number Generator
' Functions
'    [RandomN]  Generates Random Numbers starting with the SEED value
'               supplied in RandomN.Seed
'    INPUT:  RandomN.Seed value of 0 to 199017 to SEED the RNG. If a
'                         value of 0 is given, the LB Rnd function is
'                         called to give a Random Seed.
'    OUTPUT: RandomN.RN   Returns a value between 0 and 1.
'
[RandomN]
    if RandomN.Seed = 0 then gosub [RandomN.SeedRNG]
    RandomN.C1=24298
    RandomN.C2=99991
    RandomN.C3=199017
    RandomN.SeedTmp = RandomN.C1 * RandomN.Seed + RandomN.C2
    RandomN.Seed = RandomN.SeedTmp - _
            int( RandomN.SeedTmp / RandomN.C3 ) * RandomN.C3
    RandomN.RN = RandomN.Seed / RandomN.C3
    return
[RandomN.SeedRNG]
    RandomN.Seed = rnd(1) * 199017
    return


So, the above is the code that you can save in your subroutine library. 
What we have done, is effectively extended the Liberty Basic language with
another command.  I will be providing more reusable subroutines in future
issues of the newsletter.  

When Release 2.0 of Liberty Basic is available, it is expected to
include a facility for User Defined Functions.  I imagine that functions
written in the way described here will be easily converted.

              ************************************

 Newsletter written by: Brosco.
 Comments, requests or corrections mailto:brosco@orac.net.au

 Translated from Australian to English by an American:
 Alyce Watson.  Thanks Alyce.

 Assistance with the RNG code by: Omar Sigurdsson.  Thanks Omar. 



