Creating Non-Repeating Random Sequences Using Stacks

Table of Contents

Introduction
Repetitive Randoms
Using Stacks
Conclusion

 

Introduction

While working on the video player in my theater, I became concerned about the Random function I was using to shuffle videos. I was using the llFrand() function to choose which notecard line to
read and play, but I wasn’t happy with the distribution of the selections and the repeats I was observing.

Repetitive Randoms

Here’s the old code being used to pick a random notecard line and throw the selected film back to the state that plays it:

  1. state Random
  2. {
  3.     state_entry()
  4.     {
  5.         NotecardLine = (integer)llFrand(NotecardLines) + 1;
  6.         QueryID = llGetNotecardLine(NotecardName, NotecardLine);
  7.     }
  8.  
  9.     dataserver(key query_id, string data)
  10.     {
  11.         if (data != EOF) // not at the end of the notecard
  12.         {
  13.             TempMovie = GetMovieInfo(data);
  14.             CurrentMovie = TempMovie;
  15.         NextState = "Random";
  16.         state InProgress;
  17.         }
  18.         else
  19.         {
  20.             if (NotecardLine != 0)
  21.             {
  22.                 NotecardLine = 0;
  23.                 QueryID = llGetNotecardLine(NotecardName, NotecardLine);
  24.             }
  25.         }
  26.     }
  27. }

Using Stacks

I decided I needed to create a non-repeating random mode. The simplest way to do this is with a stack, implemented using a list. A stack is a structure (list) will a group of elements that is managed using a LIFO (last-in, first-out) or methodology (picture a stack of boxes, the last box you place on top of the stack is the first one you have to take off again). Items are “pushed” onto the front of the stack and then “popped” off the front of the stack as they are consumed, at which point the next element in the stack is exposed.

Let’s start with a look at my implementation code. My player operates by moving through a series of states. The other state of interest is my InProgress state, which reads the video information, plays the video, then returns the script to the calling state (in this case the Random state.

  1. state Random
  2. {
  3.     state_entry()
  4.     {
  5.         integer i;
  6.  
  7.         if ( Randomizer == [] )                     // IS THE STACK EMPTY?
  8.         {
  9.             for (i = 0; i < NotecardLines; ++i)                     // FILL THE STACK
  10.             {
  11.                 Randomizer += [i];
  12.             }
  13.             Randomizer = llListRandomize(Randomizer, 1);            // RANDOMIZE THE STACK
  14.         }
  15.  
  16.         NotecardLine = llList2Integer(Randomizer, 0);               // PULL THE LINE NUMBER FROM THE FRONT OF THE STACK
  17.         Randomizer = llDeleteSubList(Randomizer, 0, 0);             // POP OFF THE FRONT OF THE STACK
  18.         QueryID = llGetNotecardLine(NotecardName, NotecardLine);    // READ THE LINE INFORMATION
  19.     }
  20.  
  21.     dataserver(key query_id, string data)
  22.     {
  23.         CurrentMovie  = GetMovieInfo(data);         // FORMAT THE NOTECARD DATA INTO A GLOBAL
  24.         NextState = "Random";                       // COME BACK TO RANDOM MODE AFTER PLAYING
  25.         state InProgress;                           // PLAY THE MOVIE
  26.     }
  27. }

Each time the script enters the Random state it checks the size of the stack. If the stack is empty it re-populates it by looping as many iterations as there are notecard lines in the video list and adding the number of the iteration to the stack. This produces a stack of 0,1,2,3,4,5 for a six-line notecard.

After filling the stack the script calls llListRandomize() to shuffle the stack, producing a stack like 3,1,5,0,2,4.

The last three lines of the state_entry event run whether the stack was full or empty. The first element of the stack is converted into an integer and loaded into the NotecardLine global. Next the first element is deleted from the list, ‘popping’ it from the stack. Finally we read the notecard line represented by the stack value. The remainder of the code has the logic for detecting an EOF removed since the code was redundant anyway as we were not generating numbers greater than the number of notecard lines, and we are still reading the line and populating a global variable for the InProgress state to read.

Conclusion

The use of stacks, while rather basic, can be used to your advantage in certain scripting scenarios. With the introduction of a stack the random play mode on my video controller now operates without any repeats until every item on the playlist has been shown, keeping my viewers happy. Hopefully you too can find uses for the handy stack.

Update: Thanks to Osgeld Barmy over on the SL Scripting Tips forum for some advice on speeding up the code.

Leave a Reply