Randomly Selecting From a List

Discuss all aspects of editing for ZDoom.
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Randomly Selecting From a List

Postby Slasher » Mon Mar 01, 2010 10:54 am

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 allExpand 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 allExpand 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 allExpand 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 allExpand 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.
User avatar
Slasher
It smells like luncheon meat that's been walking around in the sun too long.
 
Joined: 17 Mar 2008
Location: California

Re: Randomly Selecting From a List

Postby Legend » Fri Jun 11, 2010 12:36 am

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
Legend
I Am Hellbound
 
Joined: 11 Jun 2010
Location: Antioch, CA

Re: Randomly Selecting From a List

Postby Slasher » Sun Jun 13, 2010 1:49 pm

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 allExpand view
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
Slasher
It smells like luncheon meat that's been walking around in the sun too long.
 
Joined: 17 Mar 2008
Location: California

Re: Randomly Selecting From a List

Postby Legend » Mon Jun 14, 2010 3:38 pm

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
I Am Hellbound
 
Joined: 11 Jun 2010
Location: Antioch, CA

Re: Randomly Selecting From a List

Postby Legend » Fri Jun 18, 2010 1:13 pm

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

Code: Select allExpand view
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
Legend
I Am Hellbound
 
Joined: 11 Jun 2010
Location: Antioch, CA

Re: Randomly Selecting From a List

Postby DBThanatos » Fri Jun 18, 2010 3:08 pm

Well, just for the sake of contributing, here's how I did it for my Music randomizer.
Spoiler:
User avatar
DBThanatos
Guns, explosions, gore.
 
Joined: 14 Apr 2006
Location: in "the darkness that lurks in our mind"

Re: Randomly Selecting From a List

Postby Slasher » Fri Jun 18, 2010 3:20 pm

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 allExpand view
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
Slasher
It smells like luncheon meat that's been walking around in the sun too long.
 
Joined: 17 Mar 2008
Location: California

Re: Randomly Selecting From a List

Postby Legend » Fri Jun 18, 2010 3:51 pm

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
I Am Hellbound
 
Joined: 11 Jun 2010
Location: Antioch, CA

Re: Randomly Selecting From a List

Postby Legend » Fri Jun 18, 2010 3:54 pm

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
Legend
I Am Hellbound
 
Joined: 11 Jun 2010
Location: Antioch, CA

Re: Randomly Selecting From a List

Postby Slasher » Fri Jun 18, 2010 4:08 pm

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
Slasher
It smells like luncheon meat that's been walking around in the sun too long.
 
Joined: 17 Mar 2008
Location: California

Re: Randomly Selecting From a List

Postby Legend » Fri Jun 18, 2010 4:18 pm

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
Legend
I Am Hellbound
 
Joined: 11 Jun 2010
Location: Antioch, CA

Re: Randomly Selecting From a List

Postby Slasher » Fri Jun 18, 2010 4:29 pm

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: ACS
User avatar
Slasher
It smells like luncheon meat that's been walking around in the sun too long.
 
Joined: 17 Mar 2008
Location: California

Re: Randomly Selecting From a List

Postby Legend » Fri Jun 18, 2010 4:41 pm

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
Legend
I Am Hellbound
 
Joined: 11 Jun 2010
Location: Antioch, CA

Re: Randomly Selecting From a List

Postby Slasher » Fri Jun 18, 2010 5:16 pm

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
It smells like luncheon meat that's been walking around in the sun too long.
 
Joined: 17 Mar 2008
Location: California

Re: Randomly Selecting From a List

Postby Slasher » Fri Jun 18, 2010 10:15 pm

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. :(
User avatar
Slasher
It smells like luncheon meat that's been walking around in the sun too long.
 
Joined: 17 Mar 2008
Location: California

Next

Return to Editing (Archive)

Who is online

Users browsing this forum: No registered users and 5 guests