Creating Non-Repeating Random Sequences Using Stacks
Table of Contents
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:
-
state Random
-
{
-
{
-
}
-
-
{
-
{
-
TempMovie = GetMovieInfo(data);
-
CurrentMovie = TempMovie;
-
NextState = "Random";
-
state InProgress;
-
}
-
{
-
{
-
NotecardLine = 0;
-
}
-
}
-
}
-
}
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.
-
state Random
-
{
-
{
-
integer i;
-
-
{
-
{
-
Randomizer += [i];
-
}
-
}
-
-
}
-
-
{
-
CurrentMovie = GetMovieInfo(data); // FORMAT THE NOTECARD DATA INTO A GLOBAL
-
NextState = "Random"; // COME BACK TO RANDOM MODE AFTER PLAYING
-
state InProgress; // PLAY THE MOVIE
-
}
-
}
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.