Randomly Selecting From a List

Archive of the old editing forum
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. This forum is archived - please use this set of forums to ask new questions.
User avatar
Slasher
Posts: 1160
Joined: Sun Mar 16, 2008 11:17 pm
Location: California

Randomly Selecting From a List

Post by Slasher »

This may be common knowledge to some of the more advanced ACS coders around here, but I recently discovered a way to randomly select an item from a list of items, and an easy way to keep it from selecting that item again from the list.

This could have many applications, such as Enjay mentions here.
Enjay wrote:I had a level where the player returned repeatedly to a single point in the map to be given a key by an NPC. There were 6 areas in the map and the access to each was controlled by one of the 6 keys. The areas could be completed in any order so, to mix it up, I wanted the keys to be given in a random order so that the game could play differently each time the player tackled the map. And that's where my difficulty came: I needed the player to be given a different key six times in a single play of the map. The keys needed to be given at random but once a key had been given it should never be given again.
Ok, so here's my idea:

Code: Select all

int list[6] = {1,2,3,4,5,6};
int last = 5; // this will be used later on
So here we have the list of items, or whatever is needed. In this example, we'll use 6 items, like in Enjay's situation above.

Code: Select all

int max = 5; // (size of array - 1)
Here we create a variable that will represent the end of our list. When we make a random selection, we don't want to select any further along the array than this number. This simulates the array being shorter than it was originally. Ideally a dynamic array that can have elements deleted would be preferred, but ACS doesn't have this, so we'll make due with this method.

Code: Select all

int choice;
choice = list[Random(0,max)];
So now we make the first random selection. This will be any one of the 6 available numbers. For the sake of this example, let's say it chooses 3. (The code to make something happen based on the number chosen is beyond the scope of this demonstration.)

Code: Select all

int temp;
temp = list[choice];
list[choice] = list[last]; // this is where we need the "last" variable.
list[last] = temp;         // it represents the last element of the full sized array
max--;
This code swaps the last element of the array with the random value that was chosen. Then it decreases max by 1 to simulate removing the last element of the array from the list. (By denying access to it when Random() is called subsequently.)

So in this example, the array would now look like this: {1,2,4,5,6,3}
When Random() is called again with Random(0,max), it will have access to {1,2,4,5,6}, thus allowing for another random choice to be made, and making it easy to keep track of the remaining possible values in the list.
The script can be made to continue in this fashion, moving selected numbers to the end of the list and decreasing the upper bound by 1, until all possibilities have been selected.

I hope this helps anyone who has needed a situation like this but had difficulty creating one. May it help you in your future projects.
User avatar
Legend
Posts: 158
Joined: Fri Jun 11, 2010 12:32 am
Location: Antioch, CA

Re: Randomly Selecting From a List

Post by Legend »

Ok, so here's my idea:

Code: Select all • Expand view
int list[6] = {1,2,3,4,5,6};
int last = 5; // this will be used later on

So here we have the list of items, or whatever is needed. In this example, we'll use 6 items, like in Enjay's situation above.

Code: Select all • Expand view
int max = 5; // (size of array - 1)

Here we create a variable that will represent the end of our list. When we make a random selection, we don't want to select any further along the array than this number. This simulates the array being shorter than it was originally. Ideally a dynamic array that can have elements deleted would be preferred, but ACS doesn't have this, so we'll make due with this method.

Code: Select all • Expand view
int choice;
choice = list[Random(0,max)];

So now we make the first random selection. This will be any one of the 6 available numbers. For the sake of this example, let's say it chooses 3. (The code to make something happen based on the number chosen is beyond the scope of this demonstration.)

Code: Select all • Expand view
int temp;
temp = list[choice];
list[choice] = list[last]; // this is where we need the "last" variable.
list[last] = temp; // it represents the last element of the full sized array
max--;

This code swaps the last element of the array with the random value that was chosen. Then it decreases max by 1 to simulate removing the last element of the array from the list. (By denying access to it when Random() is called subsequently.)

So in this example, the array would now look like this: {1,2,4,5,6,3}
When Random() is called again with Random(0,max), it will have access to {1,2,4,5,6}, thus allowing for another random choice to be made, and making it easy to keep track of the remaining possible values in the list.
The script can be made to continue in this fashion, moving selected numbers to the end of the list and decreasing the upper bound by 1, until all possibilities have been selected.

I hope this helps anyone who has needed a situation like this but had difficulty creating one. May it help you in your future projects.
Can this be tailored and used to load game levels randomly?
User avatar
Slasher
Posts: 1160
Joined: Sun Mar 16, 2008 11:17 pm
Location: California

Re: Randomly Selecting From a List

Post by Slasher »

This might be useful if you want to load random maps that must all be completed, but in any random order. If you just want to load random maps though, this script wouldn't be necessary.

I had to make a slight alteration to my original code, as I discovered a logic error. But I've fixed it here.

Code: Select all

int list[6] = {1,2,3,4,5,6};
int last = 5; // this will be used later on

int max = 5; // (size of array - 1)

int choice;
int rand;
rand = Random(0,max); // The array element chosen
choice = list[rand];  // The value in that element

int temp;
temp = list[rand];
list[rand] = list[last]; // this is where we need the "last" variable.
list[last] = temp;       // it represents the last element of the full sized array
max--;


switch (choice)
{
case 1:
   // Send player to map 1
   break;
case 2:
   // Send player to map 2
   break;
case 3:
   // Send player to map 3
   break;
}
You can probably alter that to fit your needs.
User avatar
Legend
Posts: 158
Joined: Fri Jun 11, 2010 12:32 am
Location: Antioch, CA

Re: Randomly Selecting From a List

Post by Legend »

What I'm specifically looking to do is play a set of 20 levels (with a couple secret episodes) with each map being loaded randomly from the start of a new game. No levels repeating each other. Possibly specify one specific level to always be last with ending text. I also want each level to start with only the pistol.

Unfortunately, I'm not too sure how to put the maps into an array though. I'm very new to zdoom/mapinfo editing.

Can I put this into a map info for the wad or do I have to put it into an acs script?
User avatar
Legend
Posts: 158
Joined: Fri Jun 11, 2010 12:32 am
Location: Antioch, CA

Re: Randomly Selecting From a List

Post by Legend »

So how would I put the maps in?
Like this?:

Code: Select all

int list[6] = {map01,map02,map03,map04,map05,map06};
int last = map05; // this will be used later on

int max = 5; // (size of array - 1)
Do I have to create a separate array with all the map names in it?

Would I put it in mapinfo? Or somewhere else?
User avatar
DBThanatos
Posts: 3101
Joined: Fri Apr 14, 2006 3:17 pm
Location: in "the darkness that lurks in our mind"
Contact:

Re: Randomly Selecting From a List

Post by DBThanatos »

Well, just for the sake of contributing, here's how I did it for my Music randomizer.
Spoiler:
User avatar
Slasher
Posts: 1160
Joined: Sun Mar 16, 2008 11:17 pm
Location: California

Re: Randomly Selecting From a List

Post by Slasher »

Legend wrote:What I'm specifically looking to do is play a set of 20 levels (with a couple secret episodes) with each map being loaded randomly from the start of a new game. No levels repeating each other. Possibly specify one specific level to always be last with ending text. I also want each level to start with only the pistol.

Unfortunately, I'm not too sure how to put the maps into an array though. I'm very new to zdoom/mapinfo editing.

Can I put this into a map info for the wad or do I have to put it into an acs script?
Unless these 20 maps are part of a mod you can't modify, you could create a small map that you start in, and return to after each map you complete. This map would only hold the script that selects the next level. To do this properly, you want to make a few changes to my code:

Code: Select all

global int 0:list[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
#define last 19; // this will be used later on

global int 1:max = 19; // (size of array - 1)

Script 1 ENTER
{
int choice;
int rand;
rand = Random(0,max); // The array element chosen
choice = list[rand];  // The value in that element

int temp;
temp = list[rand];
list[rand] = list[last]; // this is where we need the "last" variable.
list[last] = temp;       // it represents the last element of the full sized array
max--;

Teleport_NewMap (choice, 0, 0); // Sends the player to the chosen map.
}


Script 2 RETURN
{

TakeInventory();
GiveInventory("Pistol",1);
GiveInventory("Clip",50);
GiveInventory("Fist",1);

int choice;
int rand;
rand = Random(0,max); // The array element chosen
choice = list[rand];  // The value in that element

int temp;
temp = list[rand];
list[rand] = list[last]; // this is where we need the "last" variable.
list[last] = temp;       // it represents the last element of the full sized array
max--;

Teleport_NewMap (choice, 0, 0); // Sends the player to the chosen map.
}

The list will hold the numbers that correspond to the levelnums in your MAPINFO. I'm assuming 1 - 20. The Teleport_NewMap() call will then take you to the randomly selected map. Getting back to the start map would be up to you, assuming you are editing these maps.
User avatar
Legend
Posts: 158
Joined: Fri Jun 11, 2010 12:32 am
Location: Antioch, CA

Re: Randomly Selecting From a List

Post by Legend »

DBThanatos wrote:Well, just for the sake of contributing, here's how I did it for my Music randomizer.
Spoiler:
Thanks, but sorry, I'm not too sure how to implement this for level maps.
User avatar
Legend
Posts: 158
Joined: Fri Jun 11, 2010 12:32 am
Location: Antioch, CA

Re: Randomly Selecting From a List

Post by Legend »

Unless these 20 maps are part of a mod you can't modify, you could create a small map that you start in, and return to after each map you complete. This map would only hold the script that selects the next level. To do this properly, you want to make a few changes to my code:
The only problem with that is that I am not a level builder. I never tried before and have no idea how to do it. And I would just prefer not to use a hub-like starting level if possible.

There's no way to simply start the game, choose the difficulty, then load into a random map and make each map after be random as well?

I'm not editing the maps unless I need to to make them play randomly. The maps I'm using are the Master levels for doom II. I put them into a single wad file and want to make it so every time I load up the wad, the levels load in a random order.
User avatar
Slasher
Posts: 1160
Joined: Sun Mar 16, 2008 11:17 pm
Location: California

Re: Randomly Selecting From a List

Post by Slasher »

Might be able to put the script into a library and use LOADACS to make it work, and change it to an UNLOADING script... so that it will trigger when the exit is triggered, it can then choose the next map randomly.... that just might work.
User avatar
Legend
Posts: 158
Joined: Fri Jun 11, 2010 12:32 am
Location: Antioch, CA

Re: Randomly Selecting From a List

Post by Legend »

Slasher wrote:Might be able to put the script into a library and use LOADACS to make it work, and change it to an UNLOADING script... so that it will trigger when the exit is triggered, it can then choose the next map randomly.... that just might work.
So I would put the code into a library file, make a loadacs file to load the code from the library file when I select a new game, and change the loadacs file into an unloading script to make the next level load randomly?

How would I change the loadacs file into an unload script? Also, I'm not familiar with unload scripts, loadacs, or libraries. I'm very new to doom editing.

What utility would I use to do this? Just a text editor and name the files accordingly?
User avatar
Slasher
Posts: 1160
Joined: Sun Mar 16, 2008 11:17 pm
Location: California

Re: Randomly Selecting From a List

Post by Slasher »

It's a fairly detailed process, especially if you're not yet familiar with the common editing tools. Tell you what; I can't do it right now because I'm at work, but later I'll throw together a wad with the library and you can try it out. In the mean time, if you want to learn more about how this stuff works, you can read all about ACS on the wiki here: [wiki]ACS[/wiki]
User avatar
Legend
Posts: 158
Joined: Fri Jun 11, 2010 12:32 am
Location: Antioch, CA

Re: Randomly Selecting From a List

Post by Legend »

Thanks a lot Slasher. I've been checking out the zdoom wiki trying to figure some of this stuff out. Thanks again for helping me out. btw, all the maps in the wad are named mas01, mas02, etc.
User avatar
Slasher
Posts: 1160
Joined: Sun Mar 16, 2008 11:17 pm
Location: California

Re: Randomly Selecting From a List

Post by Slasher »

Their names are not important. It's the levelnums that are important. As long as Map 1 has levelnum 1, Map 2 levelnum 2, etc, the script should work properly. They most likely do.
User avatar
Slasher
Posts: 1160
Joined: Sun Mar 16, 2008 11:17 pm
Location: California

Re: Randomly Selecting From a List

Post by Slasher »

Upon further testing, I have come to the conclusion that using an UNLOADING script to send the player to a different map will not work. Unfortunately I don't think there will be any way to use my random selection script to select random maps the way you're trying to do. Maps that cannot directly call this script will not work in this method. Sorry about that, Legend. :(
Locked

Return to “Editing (Archive)”