ACS - do things in a random order, but don't repeat

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

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.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
User avatar
Enjay
 
 
Posts: 27036
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland
Contact:

ACS - do things in a random order, but don't repeat

Post by Enjay »

Sorry about the title, couldn't come up with something better... much like with the script that I am trying to put together.

I'm sure that this is an easy thing for programmer-types but it's something that I have always struggled with, never adequately figured out and then always ended up doing things very, very inefficiently, or just giving up.

Basically, I want a repeating script to make a random choice from a limited number of values (easy enough) but once it has done that, the script repeats and it must choose again, but must not pick the value it already picked.

So, what I want is the script to basically be doing random (1,4) and if, for example, it picks 3, the next time it can only choose from 1, 2 and 4 but not 3. I have no idea how to do that.

In the specific case, what I want to happen are 4 random events. For the sake of simplicity, lets just say it's four different dynamic lights being activated (it basically is, but with other associated stuff going on too - sounds, texture changes etc).

The lights can come on in any order but each one must only happen once (because of the other stuff, like the sounds) until all four lights are on. Once all four lights are on, event number 5 needs to happen which is to switch all of the lights off again after a random delay, and then the whole process starts again.

I know how to get a script to pick one random event (use a random variable then get the script to execute whatever is in the relevant if (variable == whatever) {do stuff}, but what I don't know how to do is to have the choice repeat with the reduced choices for the second third and fourth choices before the whole thing zeroes itself and starts the choice process again.

I'm pretty sure that I know how to do the zeroing part too. Just increment a separate variable every time a choice is made and once that variable hits 4 another "if" section can execute to switch all the lights off again and *somehow* tell the random choice thing that all choices are now back on the table.

Could anyone please post a skeleton script that does this? I'll never figure it out on my own because I've literally attempted it on and off for years. :bang:

Thank you kindly. Image

[edit]Oh, and if the answer is "use an array" I'll need taken by the hand through that because arrays are something I have a mental block with. I know they are simple. I've been told lots of times, even by my son. But... mental block![/edit]
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

Re: ACS - do things in a random order, but don't repeat

Post by Matt »

Does ACS have dynamic arrays? The simplest option that comes to my mind would be to delete the option that's been selected.

Another would be to keep a list somewhere of what's already been done, and cross-check that list and if the randomizer has hit some prior art have it roll again. This does have the risk of the computer suddenly freezing though if you've got a large number of possibilities and you're nearing the end of the list making it less and less probable that a purely random selection would hit something still viable (but if we're just dealing with 4 possibilities that's not going to be much of an issue).

Since we're dealing with a set number of possibilities, an array of boolean values of the same size as the number of options can be used to track this.
User avatar
Rachael
Posts: 13944
Joined: Tue Jan 13, 2004 1:31 pm
Preferred Pronouns: She/Her
Contact:

Re: ACS - do things in a random order, but don't repeat

Post by Rachael »

What I would do is I would just fill an array with a sequential set of numbers, and then shuffle them like a deck of cards. Using that analogy I'll just use 0 to 51 in this example. The number can be changed to whatever you want.

Here's untested pseudocode to illustrate what I mean (this will have to be translated to real ACS, I haven't done it in years):

Code: Select all

int i[52];

// first, fill the array
for (int x = 0; x < 52; x++)
{
	i[x] = x;
}

// now, shuffle it
for (int x = 0; x < 52; x++)
{
	// pick a random spot in the deck to swap cards with
	int shufflewith = random(0, 51);

	// the following 3 statements perform a swap using shufflevalue as a temporary storage

	// what is that spot's card value?
	int shufflevalue = i[shufflewith];

	// now, take the value of my current position (no matter what it may be - it may have been shuffled previously!)
	i[shufflewith] = i[x];

	// and put shufflevalue in my place
	i[x] = shufflevalue;
}
Now, i[] contains a list of 52 pre-shuffled random values that can be used for whatever you need them for, and will never repeat. No need to track previously used values, it allows any code following it to run somewhat more cleanly.
Last edited by Rachael on Tue Apr 14, 2020 8:56 am, edited 1 time in total.
Reason: use square brackets for i
User avatar
Enjay
 
 
Posts: 27036
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland
Contact:

Re: ACS - do things in a random order, but don't repeat

Post by Enjay »

Thank you for that. I'm going to have to sit down with a clear head to look at that tomorrow (it's late here). Like I said, arrays = mental block for me. For some reason the penny has just never dropped with them so I don't have a basic "I understand how this works" feeling about them. I have tried so any times, and I know that they are simple but... I just can't get to grips with them. One day the penny will drop and I won't be able to figure out why they gave me so much grief. Maybe tomorrow will be that day.

I'll try to ACS-ify the above to see if I can get it to work for me.

Thanks again.
Kzer-Za
Posts: 521
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

Re: ACS - do things in a random order, but don't repeat

Post by Kzer-Za »

How about this variant? No arrays :) However, you'll have to check the syntax yourself, I'm pretty sure it's incorrect, it's just to convey the idea:

Code: Select all

while true
{
	do //start selection process
	{
		CurrentChoice = random (1, 4)
		
		switch(CurrentChoice)
		{
			case 1:
			if Choice1 == true //if this choice was selected before
				CurrentChoice == 0; //make it unavailable
			else
				Choice1 == true; //else, remember that it was selected now
			break;
			
			case 2:
			if Choice2 == true
				CurrentChoice == 0;
			else
				Choice2 == true;
			break;
			
			case 3:
			if Choice3 == true
				CurrentChoice == 0;
			else
				Choice3 == true;
			break;
			
			case 4:
			if Choice4 == true
				CurrentChoice == 0;
			else
				Choice4 == true;
			break;
		}
	}
	until ( CurrentChoice > 0 ); //repeat the process until something is selected
	
	if Choice1 && Choice2 && Choice3 && Choice4 //If all options are exhausted, make them available again
	{
		Choice1 == 0;
		Choice2 == 0;
		Choice3 == 0;
		Choice4 == 0;
	}
	
	//put the code for doing what you want to do with the CurrentChoice here
}
User avatar
Rachael
Posts: 13944
Joined: Tue Jan 13, 2004 1:31 pm
Preferred Pronouns: She/Her
Contact:

Re: ACS - do things in a random order, but don't repeat

Post by Rachael »

That code is going to infinite loop when all 4 choices is exhausted.

The safety check to clear the choices when all have been made is outside of the loop where it should've been.
Kzer-Za
Posts: 521
Joined: Sat Aug 19, 2017 11:52 pm
Graphics Processor: nVidia (Modern GZDoom)

Re: ACS - do things in a random order, but don't repeat

Post by Kzer-Za »

Rachael wrote:That code is going to infinite loop when all 4 choices is exhausted.

The safety check to clear the choices when all have been made is outside of the loop where it should've been.
Well, the idea was to make the selection loop less resource-consuming. I don't think this will cause problems, since the safety check is executed before the code handling the CurrentChoice, and the selection loop doesn't start until after the code handling the CurrentChoice.

I may be wrong, I'm not a programmer, then correct me, please. But I think that two variants are possible here.

Schematically:

Variant 1

Code: Select all

outer loop
{
	inner loop
	{
		selection process
		
		safety check
	}
	
	CurrentChoice handling
}
Variant 2

Code: Select all

outer loop
{
	inner loop
	{
		selection process
	}
	
	safety check
	CurrentChoice handling
}
In either case, the inner loop doesn't get to be executed until after the check is done. But in the first variant the inner loop has to calculate the safety check in addition to calculating the selection process, each time it is repeated.
User avatar
Enjay
 
 
Posts: 27036
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland
Contact:

Re: ACS - do things in a random order, but don't repeat

Post by Enjay »

I certainly find it easier to follow what Kzer-Za posted, (and it's far more efficient than any array-avoidance code I have written in the past) but I can also see why using an array - if I could just figure them out - would be even more efficient.

However, I can't really check either ATM. Something has come up that's preventing me checking them properly. Maybe later on today.

Thanks again for the input. If there's more, keep it coming. ;)
User avatar
Enjay
 
 
Posts: 27036
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland
Contact:

Re: ACS - do things in a random order, but don't repeat

Post by Enjay »

OK, progress. I have a script that does what I want, but it looks inefficient/excessively lengthy for something that is just doing "pick one of these and don't pick the same one twice". Surely there must be an efficient way to do that? Surely other people must have wanted to do it in ACS other than just me?

Anyway...

I tried for a good long while (I mean around an hour to maybe 90 minutes) to convert Rachael's code to ACS but I failed over and over. I just couldn't get it to compile and my lack of sufficient understanding of syntax meant that my guesses (and they were just that) for fixing it were a bit random and rarely worked out well. So, I moved on to Kzer-Za's suggestion instead.

Eventually I got something that compiled but I got a runaway script error with it. I (temporarily) put in a delay that cured that and noticed that the script wasn't doing what I wanted (the same number could be selected more than once).

I added some debug messages in there that could tell me when I was getting sent to a repeated case in the switch (quite interesting in itself) but, eventually, after some reconfiguring, I have the following. It does what I want but I'm worried that the runaway script may not be properly cured (is it?). I also know from looking at it that if I did want to do this with a larger number of choices (e.g. Rachael's suggested 52 card deck) it would be a horribly inefficient bit of code with many, many repeats.

So I welcome comments, suggestions for improvements, alternatives (e.g. how would an array version look?), anything I've added that doesn't need to be there?, anything really. (I know that the ints at the start should really be moved inside the script.)

Code: Select all

#include "zcommon.acs"

int CurrentChoice;
int Choice1;
int Choice2;
int Choice3;
int Choice4;

script 100 OPEN
{

while (true)
	{
	do //start selection process
	{
		//delay(1);  //without this, I got a runaway script error - but it seems to be OK now!
		CurrentChoice = random (1, 4);

		switch(CurrentChoice)
		{
			case 1:
			if (Choice1 == true) //if this choice was selected before
				{
				//print(s:"\cGCase 1");
				//delay(16);
				CurrentChoice = 0; //make it unavailable
				break;
				}
			else
				{
				Choice1 = true; //else, remember that it was selected now
				break;
				}

			case 2:
			if (Choice2 == true)
			{
				//print(s:"\cGCase 2");
				//delay(16);
				CurrentChoice = 0;
				break;
			}

			else
				Choice2 = true;
				break;

			case 3:
			if (Choice3 == true)
			{
				//print(s:"\cGCase 3");
				//delay(16);
				CurrentChoice = 0;
				break;
			}
			else
			{
				Choice3 = true;
				break;
			}

			case 4:
			if (Choice4 == true)
			{
				//print(s:"\cGCase 4");
				//delay(16);
				CurrentChoice = 0;
				break;
			}
			else
			{
				Choice4 = true;
				break;
			}
		}
   }

   until ( CurrentChoice > 0 ); //repeat the process until something is selected

   //put the code for doing what you want to do with the CurrentChoice here
   print(d:CurrentChoice);
   delay(35);


   if (Choice1 && Choice2 && Choice3 && Choice4) //If all options are exhausted, make them available again
		{
			Choice1 = 0;
			Choice2 = 0;
			Choice3 = 0;
			Choice4 = 0;
			print(s:"\cDDone!");
			delay(35*2);
		}
	}
}
The result of the above running is that I get numbers 1 to 4 printed to screen in a random order with no repeats. When all four numbers have been displayed, the message "Done!" is displayed and the process starts again. i.e. it does what I want, I'd just like to see if it can be done more efficiently now.
User avatar
Ichor
Posts: 1784
Joined: Wed Jul 23, 2003 9:22 pm

Re: ACS - do things in a random order, but don't repeat

Post by Ichor »

This may not be very good for large numbers (such as the deck shuffling), but this will be sufficient for your needs. It will choose 4 numbers, but as long as one is equal to another, it will restart. Don't worry about a runaway script. I've ran it numerous times and that hasn't happened. Maybe it could happen with 50+ numbers, but I'm not about to write a script to test that, heh.

Code: Select all

script 1 (void)
{
    Choice1 = random(1, 4);
    Choice2 = random(1, 4);
    Choice3 = random(1, 4);
    Choice4 = random(1, 4);

	if((Choice1 == Choice2)
	|| (Choice1 == Choice3)
	|| (Choice1 == Choice4)
	|| (Choice2 == Choice3)
	|| (Choice2 == Choice4)
	|| (Choice3 == Choice4))
	{
		restart;
	}
	print(d:Choice1,s:" ",d:Choice2,s:" ",d:Choice3,s: " ",d:Choice4); // This just prints out the numbers
}
User avatar
Enjay
 
 
Posts: 27036
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland
Contact:

Re: ACS - do things in a random order, but don't repeat

Post by Enjay »

I like the simplicity of that idea.

To use it, I think I'd have to reconfigure things in my script a bit though. I'm currently using one variable that can have four different values sequentially. Once it has its value. the script goes to the appropriate "if" section, does what it does and then the variable gets a new (different) value. This code gives me four different values simultaneously. So, while this bit may be simpler, it may make other parts of the script more complicated. I'll need to take a proper look.
Post Reply

Return to “Scripting”