Game Development Community

dev|Pro Game Development Curriculum

TScriptArray Object

by James Ford · 07/17/2008 (9:24 am) · 16 comments

Download Code File

//------------------------------------------------------------------------------
// TScriptArray Resource
// Provided for free use by James C Ford
// 
// Versions
//
// 1.2   July 26, 2008   Added reverse, getRandom, and sort
//
// 1.1   July 17, 2008   Added push_front, pop_front, and insert
//
// 1.0   July 15, 2008
//
//------------------------------------------------------------------------------


// 
// Create and return a new TScriptArray object.
//
function TScriptArray::create()
{
   %obj = new ScriptObject()
   {
      class = "TScriptArray";
      elementCount = 0;
   };
   
   return %obj;
}

//
// Add a new element to the end of the array with the given value.
//
function TScriptArray::push_back( %this, %val )
{
   %this.array[%this.elementCount] = %val;
   %this.elementCount++;   
}

//
// Remove the last element of the array and return its value.
//
function TScriptArray::pop_back( %this )
{
   if ( %this.elementCount < 1 )
   {
      echo( "TScriptArray::pop_back, invalid with zero elements" );
      return;
   }
   
   %val = %this.array[%this.elementCount - 1];
   %this.array[%this.elementCount - 1] = "";
   %this.elementCount--;
      
   return %val;
}

//
// Add a new element to the front of the array with the given value,
// pushing all other elements back one in the array.
// Note that this is more expensive than push_back.
//
function TScriptArray::push_front( %this, %val )
{      
   for ( %i = %this.elementCount; %i > 0; %i-- )    
   {
      %this.array[%i] = %this.array[%i-1];  
   }
   
   %this.elementCount++;
   
   %this.array[0] = %val;   
}

//
// Remove the first element of the array and return its value.
// Preserves the order of all other elements in the array.
// Note that this is more expensive that pop_back.
//
function TScriptArray::pop_front( %this )
{
   if ( %this.elementCount < 1 )
   {
      echo( "TScriptArray::pop_front, invalid with zero elements" );
      return;
   }
   
   %val = %this.array[0];
   
   for ( %i = 0; %i < %this.elementCount; %i++ )
   {
      %this.array[%i] = %this.array[%i+1];
   }
   
   // Not technically necessary to clear this,
   // but protects against someone manipulating elementCount
   %this.array[%this.elementCount-1] = "";
   
   %this.elementCount--;
   
   return %val;
}

//
// Insert a new element with the given value at the specified
// element index.  The existing element at that index and all those
// after it will be pushed to the "right" one element in the array.
//
function TScriptArray::insert( %this, %idx, %val )
{
   if ( %idx >= %this.elementCount )
   {
      echo( "TScriptArray::insert, " @ %idx @ " is outside of the array bounds" );
      return;
   }
         
   for ( %i = %this.elementCount; %i > %idx; %i-- )
   {
      %this.array[%i] = %this.array[%i-1];   
   }
   
   %this.elementCount++;
   
   %this.array[%idx] = %val;      
}

// 
// Set the value of an existing element in the array.
//
function TScriptArray::set( %this, %idx, %val )
{
   if ( %idx > %this.elementCount - 1 )
   {
      echo( "TScriptArray::setElement, " @ %idx @ " is outside of the array bounds" );
      return;
   }
   
   %this.array[%idx] = %val;   
}

//
// Return the value of an existing element in the array.
//
function TScriptArray::get( %this, %idx )
{
   if ( %idx < 0 || %idx > %this.elementCount - 1 )
   {
      echo( "TScriptArray::getElement, " @ %idx @ " is outside of the array bounds" );
      return;      
   }
   
   return %this.array[%idx];
}

//
// Returns the number of elements in the array.
//
function TScriptArray::size( %this )
{
   return %this.elementCount;  
}

//
// Search for a value in the array and return the element
// index of the first occurance.
//
function TScriptArray::find( %this, %val )
{
   for ( %i = 0; %i < %this.elementCount; %i++ )
   {
      if ( %this.array[%i] $= %val )
         return %i;
   }
   
   return -1;
}

//
// Remove an element from the array and preserve order.
//
function TScriptArray::erase( %this, %idx )
{
   if ( %idx > %this.elementCount - 1 )
   {
      echo( "TScriptArray::erase, " @ %idx @ " is outside of the array bounds" );
      return;  
   }
   
   %this.array[%idx] = "";
   
   for ( %i = %idx; %i < %this.elementCount - 1; %i++ )
   {
      %this.array[%i] = %this.array[%i+1];
   }
   
   %this.elementCount--;
}

//
// Remove an element from the array faster, but does not
// preserve order.  Copies the last element into the hole.
//
function TScriptArray::erase_fast( %this, %idx )
{
   if ( %idx > %this.elementCount - 1 )
   {
      echo( "TScriptArray::erase_fast, " @ %idx @ " is outside of the array bounds" );
      return;  
   }
      
   %this.array[%idx] = "";
      
   %this.array[%idx] = %this.array[%this.elementCount - 1];
   
   %this.array[%this.elementCount - 1] = "";
   
   %this.elementCount--;
}

//
// Print all array elements to the console.
//
function TScriptArray::print( %this )
{
   echo( "---Printing ScriptArray---" );
   
   if ( %this.elementCount == 0 )
      echo( "   [EMPTY]" );
         
   for ( %i = 0; %i < %this.elementCount; %i++ )
   {      
      echo( "   element " @ %i @ ", " @ %this.array[%i] );
   }
}

//
// Clears all array elements, setting the size to zero.
//
function TScriptArray::clear( %this )
{
   // Clearing the strings is not really necessary,
   // But is someone screws with the elementCount,
   // it could be a good idea.
   for ( %i = 0; %i < %this.elementCount; %i++ )
   {
      %this.array[%i] = "";
   }
   %this.elementCount = 0;  
}

//
// Reverses the order of all elements in the array
//
function TScriptArray::reverse( %this )
{  
   %i = 0;    
   %j = %this.elementCount - 1;
   
   while ( %i <= %j )
   {      
      %temp = %this.array[%i];
      %this.array[%i] = %this.array[%j];
      %this.array[%j] = %temp;
      
      %i++;
      %j--;
   }
}

//
// Returns the value of a random element in the array.
//
function TScriptArray::getRandom( %this )
{
   if ( %this.elementCount == 0 )
   {
      error( "TScriptArray::getRandom, not valid with zero elements!" );
      return;      
   }
   
   %idx = getRandom( 0, %this.elementCount - 1 );
   return %this.array[%idx];
}

//
// Sort the array using the passed comparison function.
//
// Example(s):
// $array.sort( "compareStrings", true );
// $array.sort( "compareDigits", true );
//
// %compare must be the name of a function you implement
// which takes two parameters and returns -1 if the first
// is less than the second, 1 if the first is greater than
// the second, and 0 if they are equal. Look below for
// examples that work for sorting strings and digits.
//
// Notes: this is is a bubble sort algorithm and is NOT
// suitable for a very large number of elements. Also if your 
// comparison function does not return the proper values
// it CAN get stuck in an infinite loop.
//
function TScriptArray::sort( %this, %compare, %ascending )
{
   if ( !isFunction( %compare ) )
   {
      error( "TScriptArray::sort, %compare is not a valid function!" );
      return;
   }
   
   for ( %loop = true; %loop; )
   {
      %loop = false;
      
      for ( %i = 0; %i < %this.elementCount - 1; %i++ )
      {
         %result = call( %compare, %this.array[%i], %this.array[%i+1] );  
         
         // The elements are equal
         if ( %result == 0 )
            continue;
            
         // Swap the sign if necessary
         if ( %ascending )
         {
            %result = (%result > 0) ? -1 : 1;
         }
            
         // Swap the elements
         if ( %result < 0 )
         {
            %temp = %this.array[%i];
            %this.array[%i] = %this.array[%i+1];
            %this.array[%i+1] = %temp;  
            
            // We didn't make it through the list without a swap,
            // so keep looping
            %loop = true;
         }         
      }
   }
}

function compareStrings( %a, %b )
{
   return strcmp( %a, %b );
}

function compareDigits( %a, %b )
{
   return ( %a - %b );
}

#1
07/15/2008 (6:01 pm)
Actually this is essentially the same as another resource, but this has some additional methods like clear, erase, erase_fast, pop_back, and find.

You can find the other ScriptArray resource Here.

I anyone would like to add additional methods, or would like me to, feel free to email me.

jamesf42[at]gmail
#2
07/15/2008 (6:06 pm)
Awesome! This could be very useful.
#3
07/17/2008 (8:12 pm)
Added push_front, pop_front, insert, and also uploaded it as a file.
#4
07/25/2008 (11:25 pm)
Added reverse, getRandom, and sort. Sort can be easily adapted to your use by writing a new comparison function (in script).
#5
07/29/2008 (12:46 am)
I didn't really mean to rate my own resource, well, I didn't think it would really work. Oops, at least I picked a 5!
#6
08/07/2008 (4:55 am)
wooow... its like a charm
#7
01/03/2009 (5:55 am)
Hi Everyone and thanks James for a very useful resource.
#8
02/18/2009 (11:13 am)
I'm a little unsure about what the proper method is of instantiating a new TScriptArray.
%newArray = TScriptArray::create();
Is that right?
#9
02/18/2009 (1:06 pm)
Yep, that is correct. Also don't forget to do %newArray.delete() when you are done with it, it will 'not' be deleted automatically when it goes out of scope.
#10
02/18/2009 (5:54 pm)
Thanks for the awesome resource James! I'd rate it a 5 but it seems that is not a possibility after the site relaunch. :-/
#11
02/26/2009 (6:09 pm)
Thanks for a great resource James ! Its exactly what I was looking for.
#12
03/05/2009 (5:39 am)
For those who have a big array to search, I wrote a binary search for James' resource. Be sure your array is sorted from lowest value to highest or the binary search won't work. I hadn't written one of these in years. So, it was quite a geek moment for me :-)

- Tim

//
// Binary Search for a value using the passed comparison function
// and return the index of the first occurance.
//
// Example(s):
// $array.find_binary( "compareStringsNocase", "hello" );
// $array.find_binary( "compareDigits", 123 );
//
// %compare must be the name of a function you implement
// which takes two parameters and returns -1 if the first
// is less than the second, 1 if the first is greater than
// the second, and 0 if they are equal. Look below for examples.
//
function TScriptArray::find_binary( %this, %compare, %val )
{
   // set starting high and low values
   %low = 0;
   %high = %this.elementCount - 1; 
   
   // loop through the array
   while (%low <= %high) {
      
      // calculate the midpoint for this value
      %mid = mFloor( %low + ( (%high - %low) / 2 ) );

      // call compare function
      %result = call( %compare, %this.array[%mid], %val );
      
      // check if we are too hot or too cold or just right      
      if (%result > 0)
         %high = %mid - 1; // too high
      else if (%result < 0)
         %low = %mid + 1;  // too low
      else
         return %mid;      // found
    }
    
    return -1;             // not found
}

And also a string no case compare. Im not sure a one line function counts as writing but what the heck...

function compareStringsNocase( %a, %b )
{
   return stricmp( %a, %b );
}
#13
03/05/2009 (11:52 pm)
Whats with the weird naming conventions? what ever happend to pop, push, shift, and unshift
#14
03/06/2009 (9:38 am)
Are you referring to push/pop_front/back? I took that naming convention from the engine tVector class. What is shift/unshift the equivalent of?
#15
03/06/2009 (10:54 am)
push_front and push_back are totally sensible function names, imo.
plain push & pop are only useful for a strict stack.
#16
08/30/2009 (2:06 pm)
Note that for T3D this resource is probably redundant as you can now use ArrayObject. The original resource for ArrayObject can also be found here.