Game Development Community

Random Sequence Generator

by Gibby · in Torque 3D Professional · 03/08/2014 (10:58 am) · 11 replies

Greets all;

I'm submitiing an entry for the PDL game jame and as the title suggests, I'm wondering if anyone has implemented a Random Sequence Generator in TorqueScript.

What I need to do is:

Define range of numbers range

generate n unique numbers within the range .

What I need to do is pick 12 random models from a selection of 36 without duplicates.
I thought of adding this function:

www.cplusplus.com/reference/algorithm/random_shuffle/

but if I could do it in TorqueScript it would be easier to share with non-coders...


#1
03/09/2014 (3:27 pm)
That algorithm doesn't seem too difficult to write in TorqueScript. The swap function is your most important part.

function swapWords(%string, %w0, %w1) {
   %stash = getWord(%string, %w0);
   %string = setWord(%string, %w0, getWord(%string, %w1));
   %string = setWord(%string, %w1, %stash);
   return %string;
}

$str = swapWords("hello world", 0, 1);
#2
03/09/2014 (3:33 pm)
Adjust my second code sample in this resource to return the values you need, making use of the engine's random deck will help you out here. :)

EDIT:

Here's the code sample if you're too lazy to link (I've edited it a little)

String generateNRNumberString(S32 min, S32 max, S32 amount, String tokenStr) {
	if(((max - min)+1) < amount) {
		Con::errorf("generateNRNumberString: Cannot generate NR number string with given max/min/amount parameters");
		return "";
	}
   String out;
	//Generate the number list.
   MRandomDeck<S32> mVals;
	for(S32 i = min; i <= max; i++) {
	   mVals.addToPile(i);
	}
	mVals.shuffle();

	//Pull 'amount' values from the list.
	S32 currentItem;
	for(S32 i = 0; i < amount; i++) {
		mVals.draw(&currentItem);
		out += String::toString("%i", currentItem);
		if(i != amount-1) {
		   out += tokenStr;
		}
	}

	return out;
}
#3
03/10/2014 (6:57 am)
Wow, thanks guys!

Before I had a chance to try either solution I changed my needs. What I've done is create 24 tiles, now I need to choose 16 unique tiles out of the 24...

Last night I tried using an ArrayObject to generate the sequnce, but in my over-caffienated state have had no luck. Any suggestions on how to do this in torqueScript?

[edit]FWIW I thought about using the ArrayOject because I want to procedurally ruin the city later ;P
#4
03/10/2014 (7:29 am)
doh!

Forgot to post my ArrayObject attempt. This generates the array just fine, I just can't figure out how to generate the unique numerical names. I guess the hardest part of a game jam is staying conscious...

function generateTiles(%range, %count)
{
   //%range is total number of tiles
   //%count is number of tiles to be used
   echo( " >> randomCity.cs->generateTiles range " @ %range @ " count: " @%count); 
   
   //create the array
   %array = new ArrayObject(tileArray) {};
	MissionCleanup.add(%array);
   
   //Atile is a 128 x 128 section of city - we'll assign it a prefab number here.
   for (%tile = 1; %tile <= %count; %tile++)
   {
      //generate a random number within the range
      %random = getRandom(1, %range);
      echo( " >> randomCity.cs->generateTiles value for random is: " @%random);
      
      
      //tileArray.add(%tile, %random);//Obviously, adding here works but the numbers aren't unique

      //check for duplicates
      for (%i = 1; %i <= %tile; %i++)
      {
         %val = tileArray.getValue(%i);
         echo( " >> randomCity.cs->generateTiles index is: "@%i@" value is: " @%val);
         //If we have a dupe, skip and go to the next
         if(%val $= %random)
         {
            echo( " >> randomCity.cs->generateTiles val "@%val@" $= random "@%random);
            continue;
         }
         else
         {
            //Add the unique number to the array
            tileArray.add(%tile, %random);
            %test = tileArray.getValue(%tile);
            echo( " >> randomCity.cs->generateTiles tileArray index "@%tile@" value is  "@%test);
         }
      }
   }

}
#5
03/10/2014 (10:03 am)
Try this out. You load your array with the tile numbers, pick a random one and save it to a sequence, then erase it from the array.

function generateTiles(%range, %count)
{
   //%range is total number of tiles
   //%count is number of tiles to be used
   
   %array = new ArrayObject(tileArray);
   MissionCleanup.add(%array);
   
   //fill the array with tile numbers
   for(%tile = 1; %tile <= %range; %tile++)
   {
      tileArray.add(%tile, %null);
   }
   
   for(%i = 0; %i < %count; %i++)
   {
      %index = getRandom(0, %range-1);
      %sequenceList[%i] = tileArray.getKey(%index);
      tileArray.erase(%index);
      %range--;
   }
   
   for(%i = 0; %i < %count; %i++)
   {
      echo(%sequenceList[%i]);
   }
}
#6
03/10/2014 (11:49 am)
@Joseph:

hmmm I still get duplectes from this...
#7
03/10/2014 (12:00 pm)
template <class RandomAccessIterator, class RandomNumberGenerator>
  void random_shuffle (RandomAccessIterator first, RandomAccessIterator last,
                       RandomNumberGenerator& gen)
{
  iterator_traits<RandomAccessIterator>::difference_type i, n;
  n = (last-first);
  for (i=n-1; i>0; --i) {
    swap (first[i],first[gen(i+1)]);
  }
}
You understand this, right? Sort the array randomly, swapping the elements to the top of the range - then when it's done take n elements from the start of your range as your set. If you're swapping array members you can't possibly get duplicates unless there are duplicate elements in the array.
#8
03/10/2014 (12:11 pm)
@Richard:

Yeah, I understand it, swapping is the foolproof way but after adding the code to my project I couldn't seem to access it from TorqueScript...
#9
03/10/2014 (12:42 pm)
Ah - how are you accessing it from script? Do you have an engine-side data structure you're iterating through? Is it exposed to script?
#10
03/10/2014 (12:47 pm)
@Richard:

yes, I defined an engine method yet I can't seem to get to it
#11
03/10/2014 (2:42 pm)
Okay, after considerable help from Sean Rice, and borrowing from Joseph Mueller, I got it working, and added extensibility to it so I can make it more procedural later...

function generateCity(%range, %count)
{
   //echo( " >> randomCity.cs->generateCity called: range " @ %range @ " count: " @%count);

   //scriptObject here that we'll unhook later and add to theLevelInfo
   //or alternately, parse the file names from a folder and assemble into this list...
   
   if(theLevelInfo.tileList $= "")
      %tileList = new ScriptObject(TileList)
      {
            key[1] = "tileA.prefab";
            key[2] = "tileB.prefab";
            key[3] = "tileC.prefab";
            key[4] = "tileD.prefab";
            key[5] = "tileE.prefab";
            key[6] = "tileF.prefab";
            key[7] = "tileG.prefab";
            key[8] = "tileH.prefab";
            key[9] = "tileI.prefab";
            key[10] = "tileJ.prefab";
            key[11] = "tileK.prefab";
            key[12] = "tileL.prefab";
            key[13] = "tileM.prefab";
            key[14] = "tileN.prefab";
            key[15] = "tileO.prefab";
            key[16] = "tileP.prefab";
            key[17] = "tileQ.prefab";
            key[18] = "tileR.prefab";
            key[19] = "tileS.prefab";
            key[20] = "tileT.prefab";
            key[21] = "tileU.prefab";
            key[22] = "tileV.prefab";
            key[23] = "tileW.prefab";
            key[24] = "tileX.prefab";
      };
   else
      %tileList = theLevelInfo.tileList;
      
   //setup a list of placeholders we'll delete later
   for(%i = 1; %i <= %count;%i++)
   {
      %temp[%i] = %tileList.key[%i];
   }
   
   //crate the array
   if(!isObject(cityArray))
      %cityArray = new ArrayObject(cityArray) {};
      
   MissionCleanup.add(%cityArray); 
   
   //add new elements to the array for each number of the count
   %dex = 0;
   while(%dex < %count)
   {
      //cycle through the numbers within our range
      for (%r = 1;%r <= %range;%r++) 
      {             
         %number = getRandom(1, %range);
       
         if (%temp[%number] $= "")
            continue;
         else
         {  
            %dex++;
            if(%dex > %count)
               break;    
            cityArray.add(%dex,%temp[%number]);      
            %temp[%number] = "";            
         }
      }
   }
}

I now have to randomly rotate and then spawn them in-game, but that'll be after a long nap...