Shop OBEX P1 Docs P2 Docs Learn Events
Controlling the Random operator... — Parallax Forums

Controlling the Random operator...

Kaos KiddKaos Kidd Posts: 614
edited 2006-07-13 14:38 in Propeller 1
I have this nifty little program that, while waiting for a keypress, it constantly reseeds the random number generator:

PRI RndNumber | InKey, Temp
  InKey := 0
  REPEAT WHILE Inkey == 0
    Temp := ?Temp
 
  RETURN Temp

Now this works great, but I can't seem to get "controll" of the random number.
Could someone please share with me either the 'insides' of Chips random code in the VGA HiRes Text Demo or Spell out what is needed to get a random number between X and Y.

Thanks!



▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Just tossing my two bits worth into the bit bucket


KK
·

Comments

  • cgraceycgracey Posts: 14,133
    edited 2006-07-10 15:43
    The ? operator operates on a LONG variable. It rewrites the previous value, so there is no need to do a discrete assignment.

    ?variable runs one direction.

    variable? runs the other direction.

    The value returned·and written back to the variable is a 32-step iteration of a maximal-length LFSR. In other words, every bit gets replaced and it will only repeat after 4,294,967,295 iterations (2 exp 32 - 1).

    The best way to 'randomize' the seed is to increment it, since sequential numbers are, on average, separated by huge runs of other numbers when doing ?variable or variable?.

    So, when sitting idle, do variable++. When wanting random numbers, use variable? or ?variable. Remember that where the ? is determines which direction to sequence in.

    You might want to experiment by printing out successive values onto a tv or vga monitor. They are best looked at in hex (term.hex(?variable, 8)).

    In case the initial value is 0, it will be set to 1 before being iterated.

    Have fun!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔


    Chip Gracey
    Parallax, Inc.
  • Graham StablerGraham Stabler Posts: 2,507
    edited 2006-07-10 16:00
    At first I thought this thread refered to the guy behind the keyboard [noparse]:)[/noparse]
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2006-07-10 16:24
    KK,

    To answer your last question, the following subroutine will return a random integer between x and y, inclusive:

    PUB randxy(x, y)
    
      return (?seed >> 1 ** (y + 1 - x) << 1) + x
        
    
    


    seed is a long VAR, which you can initialize (randomize) if you like. x and y can be negative, positive, or mixed; but x should always be less than or equal to y.
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-07-10 18:23
    Thanks Chip and Phil.· I'm trying to grasp the math your using, but it's evading me.· But, on to the program, from what I'm reading, this would be a better method:
    PUB WaitForStart | InKey, Rnd, Seed, Max, Min
      Inkey := 0                                                                  'Init the keyboard return
      Seed  := ?                                                                  'This is the Seed for the random number
      Min   := 1                                                                  'Smallest random number permitted
      Max   := 13                                                                 'Largest random number permitted
      
      VGA.PSET(2,2)                                                               'Put text cursor where we want it
      VGA.STR(STRING("Press any key to start!")                                   'Tell User what to do
      
      KBD.Clearkeys                                                               'Clear the current keyboard buffer
      REPEAT WHILE Inkey == 0                                                     'While no keys are being pressed
        Inkey := KBD.GetKey                                                       'Try to get a key
        Seed++                                                                    'Increment the random seed value
      
      RETURN RandomXY(Min,Max,Seed)                                               'Return the random number between Min and Max
     
    PRI RandomXY(MinValue, MaxValue, SeedValue)                                   'ROutine that takes Seed and returns a random
      RETURN (?SeedValue >> 1 ** (MaxValue + 1 - MinValue) << 1) + MinValue       'Number that is <= Max and => Min
    
     
      
    

    I cant prove this yet, but I'll get some time on it tonight.

    Thanks again !

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2006-07-10 18:59
    KK,

    Don't use Seed as an argument to the subroutine: it's a pass-by-value parameter that won't get updated. Just let the subroutine access it as a global variable.

    As to the math:

    1. ? returns a random number between 1 and 2^32-1. Since this could be negative, and ** is a signed operation, we shift it right by one to make sure it's positive.
    2. The values we want to return range over a span of MaxValue - MinValue + 1, so we need to compute a random number covering that range.
    3. ** in effect treats one of its multipliers as a fraction less than 1. But since we've already shifted the random number right by one, it'll be less than 0.5.
    4. Because the random multiplier ranges to 0.5, we have to shift the multiplicand left by 1.
    5. Once we do the multiply, we have a number that ranges between 0 and (MaxValue - MinValue).
    6. Adding MinValue, that number will now range from MinValue to MaxValue.

    -Phil
  • Paul BakerPaul Baker Posts: 6,351
    edited 2006-07-10 20:01
    KK, do you really need the whole keyboard entry thing? I know coming from the BS envirnment this would be the logical choice, but the current counter value serves as an appropriate seed value for the random function, ie:

    Seed := cnt

    whether you ask for user input only depends on if you want the program to wait on the user. Ive found that using the counter produces sufficently random seeds for everyday needs.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Life is one giant teacup ride.
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-07-11 01:12
    Thanks Phil, that makes it good. Real good. I downloaded the newest version of the docs, and was trying to work through it at lunch today, taking a number and doing the math. I lost it around the ** operator..
    Thanks for the light...

    Paul: By using cnt, you will always get the SAME random number set every time the chip is restarted. This would equate to a very unrandom outcome within the app. By having the app start, then wait for the user to press a key (and I use the clear the keyboard buffer), you force a different seed, be it cnt or a var. I like your idea to use cnt tho, it would save 3 longs in ram, better RAM usage...

    I would still force the user to press something to start, therefore forcing a different value into cnt every time the app is started. And, as you metioned, it's part of the "start up" for the app...

    I discovered that by using a DAT {Name} BYTE {value},{value},{value}... i basically have a pre init array accessed with a simple {Name}[noparse][[/noparse]Index] This is great! I have one quick question about indexing on the DAT lables:

    Can a negitive Index be used?

    Example:
    DAT
    TOP BYTE 1,2,4,8,16,32,64,128,256
    MID BYTE 5,7,9,13,17,19,23,27,29
    BOT BYTE 2,4,6,8,10,12,14,16,18,20

    Index := -5
    Value = MID[noparse][[/noparse]Index]

    Would value = 16 ?

    Thanks again every one...

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • Paul BakerPaul Baker Posts: 6,351
    edited 2006-07-11 13:14
    While I haven't exhaustedly tested it, I have not experienced cnt being identical everytime on startup, though my memory may be off. It may be that pressing reset gen's good seeds whereas a cold start doesn't, but yes you can just grab the cnt value after a key has been pressed, that will provide a unique number.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Life is one giant teacup ride.
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-07-11 13:49
    Update: Phil, your routine works great! I'll have to post the code that I'm using, it's (IMHO) sorta cool( I can't post it right now, I'm at work and the laptop is at home)
    Paul:
    When ever I use the CNT, I get the exact same sequence of numbers. This is because the Prop runs at the same speed, and executes the exact number of instructions each time leading up to the point where CNT is caputured for the random number seed. Clearing the keyboard, and forcing the user to press a key, forces a underterminable number of extra "cycles" before it's captured, thus usable. I ran the test 100 times, testing for identicle numbers, and it proved true 100 times.

    The end result is this:
    I clear the keyborad buffer, then wait for a key press, each time through the keypress loop I increment the seed value (which was pre-seeded before the loop with cnt).
    Once I have my random number seed, it remains throughout this segment of the run.
    I drop into a loop 52 times...
    I get a random number between 1 & 13 (Value) and a second random number between 0 & 3 (suit)
    I use a case statement like:

    CASE Suit
    0: Card := %0000_0000 + Value
    1: Card := %0001_0000 + Value
    2: Card := %0010_0000 + Value
    3: Card := %0100_0000 + Value

    Next, I run through the card array, checking for card, if found, I repeat the above "find" loop
    I insert the card into the array, increment the card count.
    Bits 0~3 are the card values, Bits 4~6 are the suites, Bit 7 is a flag for face up / down.
    Is there a better way of randomizing and getting the bits 4~6 like the case statement?

    (Sorry about the duplicated posts, my connection was timing out all night last night, anyway... I fixed that....)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • Paul BakerPaul Baker Posts: 6,351
    edited 2006-07-11 13:51
    Gotcha, it's clear I was confusing warm reset from cold reset.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Life is one giant teacup ride.
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-07-11 13:53
    Graham:
    Ya, you could be right, but then I would have used "user" and not "operator"...
    LOL..

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-07-12 10:57
    Here's a signet of the code so far...
    PUB Start | InKey
      VGA.START(16)                                                                   'Start the video
      KBD.Start(27,26)                                                                'Start the keyboard
      PileIndex := @GData[noparse][[/noparse]1]                                                          'Set for the first pile index
      StackIndex := @GData[noparse][[/noparse]68]                                                        'Set for the first stack index
      RndSeed := ?CNT                                                                 'Init the random number
      MakeDisplay                                                                     'Draw the inital screen
      MainPlay                                                                        'Jump to the main playing loop
    PRI MainPlay | InKey, Temp
      InKey := 0                                                                      'Set inital kbd value = 0 
      REPEAT UNTIL InKey <> 0                                                         'Start waiting for user to press any key 
        InKey := KBD.Key                                                              'reseeding the Rnd gen each pass
        RndSeed++                                                                     'Increment the random seed value
      InitVars                                                                        'Go init the deck and vars for this game
    PRI GetRandom(MinValue,MaxValue)                                                  'Routine to return a random number
      RETURN (?RndSeed >> 1 ** (MaxValue + 1 - MinValue) << 1) + MinValue             'Between Min and Max using RndSeed as the random seed value
    PRI InitVars | Index, Temp, Value,Card,Suit,CardCount
      REPEAT Index FROM 12 TO 275                                                     'Reset all piles, stacks and deck
        GData[noparse][[/noparse]Index] := 0                                                             'To 0 (no cards)
      REPEAT Index FROM 0 TO 12                                                       'Reset all indexes
        GData[noparse][[/noparse]Index] := IndexList[noparse][[/noparse]Index]                                              'To default values (no cards)
      CardCount := 0                                                                  'Init the card counter
      REPEAT UNTIL CardCount == 52
        REPEAT
          Value := GetRandom(1,13)                                                    'Get random value
          Suit  := GetRandom(0,3)                                                     'Get random suit    
          CASE Suit                                                                   'Build the card
            0: Card := %0000_0000 + Value                                             'Diamonds
            1: Card := %0001_0000 + Value                                             'Clubs
            2: Card := %0010_0000 + Value                                             'Hearts
            3: Card := %0100_0000 + Value                                             'Spades
          Temp := false                                                               'Set flag for false (card not present)
          REPEAT Index FROM DeckIndex TO DeckIndex + 51                               'Loop to see if this card
            IF GData[noparse][[/noparse]Index] == Card                                                   'if this card is present
              Temp := true                                                            'set flat to repeat random get
          IF Temp == false                                                            'if card is not here then
            quit                                                                      'exit inner loop                      
        GData[noparse][[/noparse]DeckIndex + CardCount] := Card                                          'Save this card into the deck  
        CardCount++                                                                   'increment the card count
    
    

    comments and crits are always welcome


    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • SSteveSSteve Posts: 808
    edited 2006-07-12 17:22
    If I'm reading your code correctly, you're creating a deck of cards by generating a random card and checking to see if it's already in the deck. When you do it that way, the deck can take any number of cycles to generate. That 52nd card can take a while. Instead, it's much, much faster to build a deck in order and then go through each of the 52 cards and swap its position with another random card. Something like this:
    repeat idx from 0 to 3
      suit := |<idx >> 1  ' suit is 0, 1, 2, or 4
      repeat value from 1 to 13
        GData[noparse][[/noparse]idx * 13 + (value - 1)] := suit << 4 + value
    ' Now you have an in-order deck
    
    repeat idx from 0 to 51
      randpos := GetRandom(0, 51)
      temp := GData[noparse][[/noparse]idx]
      GData[noparse][[/noparse]idx] := GData[noparse][[/noparse]randpos]
      GData[noparse][[/noparse]randpos] := temp
    ' Now you have a randomized deck
    
    


    I wrote this code in my browser so I haven't tested to see if it works or even compiles.

    Also, I apologize if I completely misinterpreted what you're trying to do.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    OS-X: because making Unix user-friendly was easier than debugging Windows

    links:
    My band's website
    Our album on the iTunes Music Store
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-07-12 18:56
    Sweet reply SSteve, Your right about making a deck of cards.
    I hadn't thought of that approch, much better usage of ram and time...
    Basically I'v packed the deck into a byte with the value of 0 reserved for no card, bit 7 is reserved for face up/down
    01~13 = D %X000_0001 ~ %X000_1101
    16~29 = C %X001_0001 ~ %X001_1101
    32~45 = H %X010_0001 ~ %X010_1101
    64~77 = S %X100_0001 ~ %X100_1101
    It took about 2 or 3 seconds to make a deck, but from what I see in your code, it would take a fraction of a second or so.
    I'll put that in place tonight and play with it...
    I see how your moving suite into the right bit place with the << 4 and adding the value (nice...) , but what ia the | <idx >> 1 doing?
    I think its taking the value of IDX and making it a bit. So if it's 3, then it would set idx = %00000000_00000000_00000000_00000100.
    If it was 7 (for example), then it would set idx = %00000000_00000000_00000000_01000000, not %00000000_00000000_00000000_01111111, right?
    Ok, that's fine, thats what the docs say, but what is the >> 1, right shifting it back 1?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • SSteveSSteve Posts: 808
    edited 2006-07-12 21:33
    Kaos Kidd said...
    So if it's 3, then it would set idx = %00000000_00000000_00000000_00000100.
    No, |< 3 gives %00000000_00000000_00000000_00001000. The rightmost bit starts counting at zero.

    The |< operator is basically the same as "2 to the power of". So |< 0 is 1 (2 ^ 0) and |< 3 is 8 (2 ^ 3). Using |< idx gives the values 1, 2, 4, & 8 for the values 0-3. Since your suit value is 0, 1, 2, or 4, you have to shift the result right one bit.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    OS-X: because making Unix user-friendly was easier than debugging Windows

    links:
    My band's website
    Our album on the iTunes Music Store
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-07-13 14:38
    Ahhh..... I see.
    Your code works as it stands, and it does run in fractions of a second. Cool.
    Next thing up is getting the array indexing right...
    Thanks again everyone...

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
Sign In or Register to comment.