Raised This Month: $32 Target: $400
 8% 

[HOW TO] Retrieve random values from an array without retreiving the same twice


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Exolent[jNr]
Veteran Member
Join Date: Feb 2007
Location: Tennessee
Old 07-22-2008 , 18:30   [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #1

So, the main purpose of this tutorial is to show how to retrieve values from an array from random slots, without ever getting the same slot twice.

Note: This is intended for AMX Mod X scripters who are fluent in scripting plugins. (No learners, please.)

Code:
// Overview of the tutorial: // players[] = all players in the server // total = amount of players in the players[] array // retreive = amount of players to retrieve from the players[] array // selected[] = random players taken from players[] array // count = amount of players saved in the selected[] array // rand = random loop number to retrieve random players // I will just use the players array, // since this is what most people want this for new players[32], total; get_players(players, total); // this will be how many random we want new retrieve = 5; // check if it is larger than total // because you cannot get more players than the ones that exist if( retrieve > total ) retrieve = total; // this will be the counter of how many random we have gotten new count; // this will hold all the random players // note: if this isn't for players, //       make sure that this array size is the same as the array you are getting values from new selected[32]; // lets start a loop // we get a random number each time to randomize the selected // then, we delete the random index from the original players list new rand; do {     rand = random(total);         // save the player id in the selected array,     // and increase the amount we saved     selected[count++] = players[rand];         // this replaces the random one we used with the last one in the array     // also, it decreases the total since we got rid of the random one     players[rand] = players[--total]; } while( count < retrieve ); // you now have an array (selected[]) that holds "count" number of players // they are all random players that are currently in the server // Note: this is only an example, and can be used for any random array retrieving.

Now, for all you people that want to know how to use this with the dynamic array system.

Code:
// Overview of the tutorial: // players = all players in the server // total = amount of players in the players array // retreive = amount of players to retrieve from the players array // selected = random players taken from players array // count = amount of players saved in the selected array // rand = random loop number to retrieve random players // player = random player selected from players array new Array:players = ArrayCreate(1, 32); new total; // this code will fill the players array will all players in the server // if you aren't using this tutorial for players, skip this part, assuming your array is filled new max_players = get_maxplayers(); for( new id = 1; id <= max_players; id++ ) {     if( is_user_connected(id) )     {         ArrayPushCell(players, id);         total++;     } } // this will be how many random we want new retrieve = 5; // check if it is larger than total // because you cannot get more players than the ones that exist if( retrieve > total ) retreive = total; // this will be the counter of how many random we have gotten new count; // this will hold all the random players new Array:selected = ArrayCreate(1, retrieve); new player; // lets start a loop // we get a random number each time to randomize the selected // then, we delete the random index from the original players list new rand; do {     rand = random(total);         player = ArrayGetCell(players, rand);         // save the player id in the selected array,     // and increase the amount we saved     ArrayPushCell(selected, player);     count++;         // now we need to remove the index from the list     // so, we will shift all slots above the player id slot down 1     ArrayDeleteItem(players, rand);         // decrease total player ids since we removed one     total--; } while( count < retrieve ); // you now have an array (selected) that holds "count" number of players // they are all random players that are currently in the server // Note: this is only an example, and can be used for any random array retrieving.

This is a more advanced way which uses the Sort*() functions from AMXX.
I'm not experienced in this area. This was shown by P34nut and arkshine (a few posts after this one).
Code:
YourFunction() {     new players[32], pnum;     get_players(players, pnum); // you can use any flags you wish         SortCustom1D(players, pnum, "HandleRandomSort"); } HandleRandomSort(elem1, elem2, const array[], const data[], const dataSize) {     switch( random(61) )     {         case 0 .. 29: return -1;         case 30: return 0;     }         return 1; }

Here is an example for a function that retrieves a random player.
Code:
YourFunction() {     new player = GetRandomPlayer("a"); } GetRandomPlayer(const flags[]="", const teamORname[]="") {     new players[32], pnum;     get_players(players, pnum, flags, teamORname);         return (pnum > 0) ? players[random(pnum)] : 0; }

Here is an example for a function that retrieves an array of random players.
Code:
YourFunction() {     new players[32], pnum;     GetRandomPlayers(players, pnum, "a"); } GetRandomPlayers(players[32], &pnum, const flags[]="", const teamORname[]="") {     new original[32], total;     get_players(original, total, flags, teamORname);         pnum = 0;         new rand, i;     do     {         rand = random(total);                 players[pnum++] = original[rand];                 original[rand] = original[--total];     }     while( total > 0 ); }

EDIT:

Here are some useful functions that I wrote up.
It allows you to just copy these functions and implement anything you needed from the first post.

Code:
GetRandomNumberRange( const minn, const maxx );
GetRandomNumbersRange( numbers[ ], const size, const minn, const maxx );
any:GetRandomNumberCArray( const Array:array );
GetRandomNumbersCArray( any:numbers[ ], const size, const Array:array, bool:dont_modify );
any:GetRandomNumberArray( const any:array[ ], const array_size = sizeof( array ) );
GetRandomNumbersArray( any:numbers[ ], const size, const any:array[ ], const array_size = sizeof( array ) );
Code:
// Returns a random number from 'minn' to 'maxx' // Note: Only works for integers #define GetRandomNumberRange random_num //#define GetRandomNumberRange(%1,%2) random_num(%1, %2) /*stock GetRandomNumberRange( const minn, const maxx ) {     return random_num( minn, maxx ); }*/ // Gives an array of random numbers ranging from 'minn' to 'maxx' // Note: Only works for a integers // Note2: If the size of the array is larger than the range, the array will not be completely filled, since numbers are not duplicated. // Note3: If the size of the array is smaller than the range, not all of the numbers in the range will be given. // Note4: Requires  GetRandomNumbersCArray() to work. stock GetRandomNumbersRange( numbers[ ], const size, const minn, const maxx ) {     new Array:array = ArrayCreate( 1 );     for( new i = minn; i <= maxx; i++ )     {         ArrayPushCell( array, i );     }         new count = GetRandomNumbersCArray( numbers, size, array, .dont_modify = false );         ArrayDestroy( array );         return count; } // Returns the value from a random index in a cell array #define GetRandomNumberCArray(%1) ArrayGetCell( %1, random( ArraySize( %1 ) ) ) /*stock any:GetRandomNumberCArray( const Array:array ) {     return any:ArrayGetCell( array, random( ArraySize( array ) ) ); }*/ // Gives an array of values from random indexes in a cell array // Note: dont_modify - true: don't delete items from the array that were used, false: delete items that were used // Note2: If the size of the array is larger than the size of the cell array, the array will not be completely filled, since indexes are not duplicated. // Note3: If the size of the array is smaller than the size of the cell array, not all of the indexes will be given. stock GetRandomNumbersCArray( any:numbers[ ], const size, const Array:array, bool:dont_modify ) {     new Array:temp;     new array_size = ArraySize( array );         if( dont_modify )     {         temp = ArrayCreate( 1 );             for( new i = 0; i < array_size; i++ )         {             ArrayPushCell( temp, any:ArrayGetCell( array, i ) );         }     }     else     {         temp = array;     }         new count;     new rrandom;         while( count < size && array_size > 0 )     {         rrandom = random( array_size );         numbers[ count++ ] = any:ArrayGetCell( temp, rrandom );         ArrayDeleteItem( temp, rrandom );         array_size--;     }         if( dont_modify )     {         ArrayDestroy( temp );     }         return count; } // Returns a random number from an array of numbers // Note: If your given array size is undefined (like from a function), then don't pass the 'array_size' argument and it will be auto-determined. //#define GetRandomNumberArray(%1) %1[ random( sizeof( %1 ) ) ] stock any:GetRandomNumberArray( const any:array[ ], const array_size = sizeof( array ) ) {     // this is not a #define because using a function can always find the array size     // but in a #define, it replaces the function with it's defined action     // if it is used on an array that is passed to a function with no size, it will give a compiler error     // this will prevent that     return any:array[ random( array_size ) ]; } // Gives an array of values from random indexes in a given array // Note: If your given array size is undefined (like from a function), then don't pass the 'array_size' argument and it will be auto-determined. // Note2: If the size of the array is larger than the size of the given array, the array will not be completely filled, since indexes are not duplicated. // Note3: If the size of the array is smaller than the size of the given array, not all of the indexes will be given. // Note4: Requires  GetRandomNumbersCArray() to work. stock GetRandomNumbersArray( any:numbers[ ], const size, const any:array[ ], const array_size = sizeof( array ) ) {     new Array:CArray = ArrayCreate( 1 );     for( new i = 0; i < array_size; i++ )     {         ArrayPushCell( CArray, array[ i ] );     }         new count = GetRandomNumbersCArray( numbers, size, CArray, .dont_modify = false );         ArrayDestroy( CArray );         return count; }

Here is an example for those people who want random players.

Code:
// only used for readability const MAX_PLAYERS = 32; new iPlayers[ MAX_PLAYERS ], iNum; // get your players how you want them // example for all: get_players( iPlayers, iNum ); // example for all alive: get_players( iPlayers, iNum, "a" ); // look here for more possibilities //http://www.amxmodx.org/funcwiki.php?go=func&id=174 new iRandomPlayers[ MAX_PLAYERS ]; GetRandomNumbersFromArray( iRandomPlayers, MAX_PLAYERS, iPlayers, iNum ); for( new i = 0; i < iNum; i++ ) {     log_amx( "iPlayers[%i] = %i, iRandomPlayers[%i] = %i", i, iPlayers[ i ], i, iRandomPlayers[ i ] ); }

That is all!

Feel free to point out any suggestions/errors.
__________________
No private work or selling mods.
Quote:
Originally Posted by xPaw View Post
I love you exolent!

Last edited by Exolent[jNr]; 11-27-2010 at 15:42.
Exolent[jNr] is offline
atomen
Veteran Member
Join Date: Oct 2006
Location: Stockholm, Sweden
Old 07-23-2008 , 14:31   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #2

Fine tutorial :]

About the error part : notice the "you know have an array"
PHP Code:
// you know have an array (selected) that holds "count" number of players 
__________________
atomen is offline
Send a message via MSN to atomen
Exolent[jNr]
Veteran Member
Join Date: Feb 2007
Location: Tennessee
Old 07-23-2008 , 14:35   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #3

lol, woops. I wrote the whole tutorial in the post, so I wasn't paying attention that much
__________________
No private work or selling mods.
Quote:
Originally Posted by xPaw View Post
I love you exolent!
Exolent[jNr] is offline
P34nut
AMX Mod X Beta Tester
Join Date: Feb 2006
Location: Netherlands
Old 07-23-2008 , 14:59   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #4

This is another way:
Code:
// Get players new Players[32], iNum get_players(Players, iNum, "h") //Shuffle it SortCustom1D(Players, iNum, "fnSortFunc") public fnSortFunc(elem1, elem2, const array[], const data[], data_size) {     new iNum = random_num(0, 60)     if (iNum < 30)         return -1     else if (iNum == 30)         return 0         return 1 } // Players now contains all the players randomized
Credits goes to MaximusBrood
__________________
All you need to change the world is one good lie and a river of blood

Last edited by P34nut; 07-23-2008 at 14:59. Reason: OMG Small tags!
P34nut is offline
Arkshine
AMX Mod X Plugin Approver
Join Date: Oct 2005
Old 07-23-2008 , 15:29   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #5

By the way, better to do :

Code:
    switch ( random_num(0, 60) )     {         case 0 .. 29 : return -1;         case 30      : return 0;     }         return 1;

But your function seems to not be updated properly. :p
__________________

Last edited by Arkshine; 07-23-2008 at 15:31.
Arkshine is offline
Lee
AlliedModders Donor
Join Date: Feb 2006
Old 07-23-2008 , 17:16   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #6

Just a few tiny things.. You used 'overhead' when you meant 'overview'.

Code:
new selected[32] //from all three snippets new bool:used[32] //from the second version
..should be..

Code:
new selected[retrieve] new bool:used[retrieve]

You don't need 'count' and 'retrieve'.

Code:
while( retrieve ) {     // get a random player     rand = random(total);         // if we already used this value,     // skip it.     if( used[rand] )         continue;         // save the player id and increment the count     selected[--retrieve] = players[rand];         // show that we used the player id     used[rand] = true; }
You can obviously do the same thing with 'for' if you count backwards.

I wish I spent more time writing useful code rather than pointing out the tiniest inefficiencies in other people's.
__________________
No support via PM.

Last edited by Lee; 07-23-2008 at 17:24.
Lee is offline
Exolent[jNr]
Veteran Member
Join Date: Feb 2007
Location: Tennessee
Old 07-23-2008 , 17:40   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #7

If I were to create arrays with "retrieve" max slots, retrieve would also have to be a constant number. And it is only there for the example.
Also, I'm just going to remove the 2nd way anyway, since it isn't as good as the first.
__________________
No private work or selling mods.
Quote:
Originally Posted by xPaw View Post
I love you exolent!
Exolent[jNr] is offline
Arkshine
AMX Mod X Plugin Approver
Join Date: Oct 2005
Old 07-23-2008 , 17:48   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #8

Quote:
I wish I spent more time writing useful code rather than pointing out the tiniest inefficiencies in other people's.
I prefer to have the habit to write efficient code at first. I don't think it's a bad manner. Whatever what you say, it's useful since the final purpose is the same : efficient code.

Anyway, I understand what you say, but I'm too lazy atm to analyse the whole code.

( Good attempt also X-olent ^^ )
__________________

Last edited by Arkshine; 07-23-2008 at 17:54.
Arkshine is offline
Cheap_Suit
Veteran Member
Join Date: May 2004
Old 07-23-2008 , 19:22   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #9

Any reason why its 0 to 60? Would doing it like this would also give out same results?
PHP Code:
public fnSortFunc(elem1elem2, const array[], const data[], data_size
{
    switch(
random_num(02))
    {
      case 
0: return -1
      
case 1: return 0
      
default: return 1
    
}
    return 
1

__________________
HDD fried, failed to backup files. Sorry folks, just don't have free time anymore. This is goodbye.
Cheap_Suit is offline
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 07-23-2008 , 20:16   Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
Reply With Quote #10

The chance of getting 1 in your code is much higher than the chance of getting 30 in their code.
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 11:03.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode