AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Code Snippets/Tutorials (https://forums.alliedmods.net/forumdisplay.php?f=83)
-   -   [HOW TO] Retrieve random values from an array without retreiving the same twice (https://forums.alliedmods.net/showthread.php?t=74666)

Exolent[jNr] 07-22-2008 18:30

[HOW TO] Retrieve random values from an array without retreiving the same twice
 
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.

atomen 07-23-2008 14:31

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
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 


Exolent[jNr] 07-23-2008 14:35

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
lol, woops. I wrote the whole tutorial in the post, so I wasn't paying attention that much :D

P34nut 07-23-2008 14:59

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
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

Arkshine 07-23-2008 15:29

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
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

Lee 07-23-2008 17:16

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
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.

Exolent[jNr] 07-23-2008 17:40

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
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.

Arkshine 07-23-2008 17:48

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
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 ^^ )

Cheap_Suit 07-23-2008 19:22

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
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



Hawk552 07-23-2008 20:16

Re: [HOW TO] Retrieve random values from an array without retreiving the same twice
 
The chance of getting 1 in your code is much higher than the chance of getting 30 in their code.


All times are GMT -4. The time now is 21:31.

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