ActionScript 3

Currently Being Moderated

Random Array without Sequential Repeats

Sep 12, 2012 6:02 AM

Hi,

Continuing to build the eLearning exercise (much help from kglad). Ran into a problem with sequential repeats in the random array, needing a solution. The AS3 script (much help here from kglad) to generate and then shuffle the array, often (since there are only 9 potential elements for an array length of 70) produces sequential repeats. For example, using 1,2,3,4,5,6,7,8,9 as elements, the shuffled final array can produce 2,2,2,1,4,5,9,3,3.......

Problem is that when these repeats are used in  textbox.text, there is no transition between same (repeated) "words." If a word changes (for example from 5 to 3), the user sees a visiable change. However, with these repeats (for example, 3 to 3), there is no transition: the repeat appears to just stay the same for a longer time. Then the user isn't sure if they've already chosen this word.

In any  event, it would be better, since I can't seem to add a visible transition for repeats, to simply avoid sequential repeats, if possible. But cannot figure out a way to add this to the script.

Any help appreciated.

The current random array and shuffle scripts:

``````var index:int = 0;
var numToDisplay:int = 10;
var groupA:Array = ["1","2","3"]
var groupB:Array = ["4","5","6"]
var groupC:Array = ["7","8","9"]
var word_array:Array = [];
var i:int;
for(i=0;i<3;i++){
word_array.push(groupA[Math.floor(groupA.length*Math.random())]);
}
for(i=0;i<3;i++){
word_array.push(groupB[Math.floor(groupB.length*Math.random())]);
}
for(i=0;i<3;i++){
word_array.push(groupC[Math.floor(groupC.length*Math.random())]);
}

shuffle(word_array);

function shuffle(a:Array) {
var i:int;
var j:int;
var e:*;
var len:int = a.length;
for (i = len-1; i>=0; i--) {
j=Math.floor((i+1)*Math.random());
e = a[i];
a[i] = a[j];
a[j] = e;
}
}
``````

Replies
• Currently Being Moderated
Sep 12, 2012 6:57 AM   in reply to saratogacoach

Are you literally doing the arrays as small as above or is that pseudo? If so you should just shuffle groupA/B/C and add them iteratively (word_array.push(groupA[i])). That will shuffle the 3 numbers and guarantee no duplicates because you're not randomly picking a number, you're using them all, in their shuffled order.

I'm not familiar with exactly what you're trying to do besides what I see there which is apparently generate an array with 1-9 shuffled. I don't understand why you're breaking them up into 3 groups unless this is pseudo and not actually the real code. I presume you want to add a lot more, potentially duplicate numbers to each group to go this route because it's overkill to just generate a random list of numbers 1-9.

|
Mark as:
• Currently Being Moderated
Sep 12, 2012 7:36 AM   in reply to saratogacoach

Can you explain the relevance of breaking it into 3 groups?

|
Mark as:
• Currently Being Moderated
Sep 12, 2012 9:56 AM   in reply to saratogacoach

Ah, ok.. I'd approach it a little different with array splice and cloning. Something that would allow you to add in more values to any group would be like so:

// content

var groupA:Array = ["1","2","3"];

var groupB:Array = ["4","5","6"];

var groupC:Array = ["7","8","9"];

// master list

var wordOrder:Array = new Array();     // words list

var helperArr:Array = new Array();    // helper array

// generate all 70 elements

while (wordOrder.length < 70)

{

var cloneArr:Array = new Array();

// select proper group to get content from based on amount in wordOrder

if (wordOrder.length < 21)

{

cloneArr = groupA.concat();

}

else if (wordOrder.length < 41)

{

cloneArr = groupB.concat();

}

else

{

cloneArr = groupC.concat();

}

// return a shuffled array of values guaranteeing the first item does

// not duplicate the last word added

helperArr = shuffle(cloneArr);

// push returned shuffled values in, stopping if length is met

while ((helperArr.length > 0) && (wordOrder.length < 70))

{

wordOrder.push(helperArr.splice(0,1));

}

}

trace(wordOrder);

function shuffle(sourceArr:Array):Array

{

// new shuffled array to return after filling

var destArr:Array = new Array();

// splice the sourceArr into the destArr randomly

while (sourceArr.length > 0)

{

destArr.push(sourceArr.splice(int(Math.random() * sourceArr.length), 1)[0]);

}

// assure first value does not match last value added, otherwise shift

if (destArr[0] == wordOrder[wordOrder.length - 1])

{

// remove last value off array, replace with first

destArr.unshift(destArr.pop());

}

return destArr;

}

Any size array can be used in any group and it'll shuffle them. Lastly before completing and returning a shuffled array it will check the last value added to wordOrder and make sure it doesn't match the first item being returned or that would be a duplicate. If that's the case it just pops the last value off the array and adds it to the beginning to assure it's not a duplicate.

If for whatever reason you do add in the same value multiple times in a single group the pop/shift won't work because you chance the last value being identical to the first and I'm not testing for that in the code above. You can paste this in a new AS3 document and it will trace out the list it generates at the end.

Just out of best practices I originally added a second argument to shuffle so you could pass in the last added value and it would compare against the value you sent. I simplified it by directly reading from wordOrder inside the function. That's hard coding or coupling the function directly rather than keeping it generic and it's not best practice, but for your purposes I don't think it will harm you.

|
Mark as:
• Currently Being Moderated
Sep 12, 2012 10:10 AM   in reply to saratogacoach

I get 70 every time. Try to copy and paste it again. Examples:

3,1,2,1,3,2,1,2,3,1,2,3,1,3,2,3,1,2,3,1,2,5,4,6,4,5,6,4,6,5,4,5,6,4,5, 6,4,6,5,6,4,5,9,8,7,9,7,8,9,7,8,9,8,7,8,9,7,8,9,7,8,9,7,8,9,7,9,7,8,9 [70]

3,1,2,3,2,1,2,3,1,2,1,3,1,3,2,1,2,3,1,2,3,5,4,6,4,6,5,4,5,6,5,6,4,6,4, 5,6,4,5,4,6,5,9,7,8,7,8,9,8,9,7,8,7,9,8,7,9,8,7,9,7,9,8,9,8,7,8,9,7,9 [70]

1,2,3,1,3,2,3,1,2,3,1,2,1,3,2,3,1,2,1,3,2,4,6,5,6,5,4,5,6,4,5,4,6,4,6, 5,4,5,6,5,4,6,8,9,7,9,7,8,9,8,7,9,7,8,7,9,8,9,8,7,8,7,9,7,9,8,9,8,7,8 [70]

|
Mark as:
• Currently Being Moderated
Sep 12, 2012 10:24 AM   in reply to saratogacoach

|
Mark as:
• Currently Being Moderated
Sep 13, 2012 7:37 AM   in reply to saratogacoach

Remember you have an array shuffle function already that scales to any size array. In the example above you only need to add one line of code to shuffle the final array:

wordOrder = shuffle(wordOrder);

It will perform the "check for duplicate" function unneededly at that point but it shouldn't matter.

edit:

Oops I won't erase it but you can't do that. It suffers from what I mentioned above where no group can contain duplicates and the array produced definitely has duplicates in it.

If they need to be mixed together I suggest you not break them into 3 different groups. There's no purpose at that point. You should make a single array full of the 9 different words and add them randomly. You had previously mentioned you needed 20 from the first group, 20 from the second and 30 from the 3rd. I just followed that recipe. What you want requires a bit more code but I think you have enough code now to understand how you can achieve what you want. Post back if you have any questions on your attempt.

I'll give you a lead but I'm not going to complete the logic so you can try to see if you can finish it yourself. Always helps to learn. The shuffle's splice() randomization in the while(){} loop can be rewritten to check for a duplicate like this:

while (sourceArr.length > 0)

{

var randPos:int = int(Math.random() * sourceArr.length);

// check if current word is the same as the last word added, if so ignore and continue

if (sourceArr[randPos] == destArr[destArr.length - 1]) { continue; }

destArr.push(sourceArr.splice(randPos, 1)[0]);

}

Note: please do not add that and leave it at that. It will most likely create an infinite loop and hang you.

Here's the challenge. You can randomly pick from the source array to add to the destination array and eventually get down to the last few numbers. Picture it in your mind. You may end up with the destArr's last word as "1" and all you have left in sourceArr is 5 "1"'s. Therefore the test will fail every time because all you have is a bunch of duplicate "1"s and the "continue;" will put you in an infinite loop.

Your challenge is to sense when all you have left is a handful of the same word and then sprinkle them into the destArr in a random place. You can do that by looping over the values of sourceArr to see if they're all the same. If they are, and they match the last value added to destArr, then you need to handle sprinkling these into destArr.

|
Mark as:
• Currently Being Moderated
Sep 13, 2012 9:37 AM   in reply to saratogacoach

If you pick it back up and have more questions feel free to open a new thread. You're welcome and good luck!

|
Mark as:
Actions

More Like This

• Retrieving data ...