Liberty Basic is develeopped by Carl Gundel
Original Newsletter compiled by Alyce Watson and Brosco
Translation to HTML: Raymond Roumeas
 

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

 

This issue is devoted to two topics:

  1. Subroutine: Random Number Generator (RNG)
  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 isUntil 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.


1. Random Number Generator

"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
 


2. Building reusable code

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 an '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 to: 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.