Hi all!
I have in my game a minigame which mechanics works basically like this: There are four spheres on the screen, red, blue, green and yellow.
When the minigame starts, the spheres lit up for a second in a random order, for example blue, green, red, yellow. Then the player needs to click the spheres in the same sequence. If the sequence is the same as shown before, you win, otherwise you lose.
I have already coded this few years back, and it works, but the code is not that good (I was just starting with AGS then). Now I'm plan to modify the minigame, so that you can advance to next stage, where the spheres appear more quickly, and the number of the spheres will also grow. So I would like to have a nice, clean code that I can adjust quite easily.
So what would be the simplest, cleanest way to code this? What is a good method of creating random sequences, where you shuffle certain objects to certain order?
Thanks in advance!
Isn't AGS programming similar to another language? What you are looking for is known as "Simon says", maybe you'll find stuff somebody made on a programming site.
Quote from: rongel on Sat 25/11/2017 08:58:02
What is a good method of creating random sequences, where you shuffle certain objects to certain order?
First thing that comes to the mind:
// Function Shuffle, gets integers from "numbers" array at random, and puts them into "result" array.
// numbers - source array;
// numbers_count - the length of source array;
// result - output array (contains shuffled integers
// results_size - the length of result array
void Shuffle(int numbers[], int numbers_count, int result[], int result_size)
{
int i;
while (i < result_size)
{
result[i] = numbers[Random(numbers_count - 1)];
i++;
}
}
Use example:
function Room_AfterFadeIn()
{
// Prepare arrays
int numbers[] = new int[5];
int result[] = new int[10];
// Fill in numbers, from 1 to 5
int i;
while (i < 5)
{
numbers[i] = i + 1;
i++;
}
// Shuffle
Shuffle(numbers, 5, result, 10);
// Display resulting array
i = 0;
while (i < 10)
{
Display("Result #%d = %d", i + 1, result[i]);
i++;
}
}
PS. If the standart Random function won't produce interesting results, it may be replaced by something else, but that's another topic.
You may also increase the probability of certain numbers by
putting them multiple times into the source array (e.g. put number 2 several times there, instead of just once, and it will be appearing in shuffled result more often).
UPD:
If you want a function that does the same, but makes sure that same number appears
only once, then -
// Function ShuffleUnique shuffles an array that contains presumably unique numbers by swapping them between each other.
// numbers - array of source numbers
// result - resulting array
// length - length of numbers and result arrays (they must be equal length)
// shuffle_time - how much to shuffle
void ShuffleUnique(int numbers[], int length, int result[], int shuffle_time)
{
if (length < 2)
return; // no sense to shuffle this
// First, fill result array with source numbers
int i;
while (i < length)
{
result[i] = numbers[i];
i++;
}
// Now, shuffle
int times = shuffle_time;
while (times > 0)
{
int index1 = Random(length - 1);
int index2 = Random(length - 1);
int temp = result[index1];
result[index1] = result[index2];
result[index2] = temp;
times--;
}
}
Thanks for the quick answers!
Quote from: Crimson Wizard on Sat 25/11/2017 10:13:34
If you want a function that does the same, but makes sure that same number appears only once, then -
I think this is what I need to do. I want to shuffle the order in which the red, blue, green and yellow colors appear, but use only the specified four colors so that each one will flash only once.
I'll test the code soon, and report back!
Ok, the Unique shuffle seems to do the job the thing I was looking for! I don't fully understand it, but it works and I can change the numbers to get more or less results.
I'll use my previous work with the minigame as an example. I have five objects, oBlue, oYellow, oGreen, oRed and oPurple and I want to shuffle their appereance order. So I could make something like this:
while (i < 5)
{
if (i +1 && result[i] ==1) Flash_Blue();
if (i +1 && result[i] ==2) Flash_Yellow();
if (i +1 && result[i] ==3) Flash_Green();
if (i +1 && result[i] ==4) Flash_Red();
if (i +1 && result[i] ==5) Flash_Purple();
i++;
}
But what would be the smartest way to check if player clicks them in the right order? How can I compare the shuffle results to the player clicking the objects?
Quote from: rongel on Sat 25/11/2017 12:26:46
I don't fully understand it, but it works
All it does is just randomly swapping numbers between two places N times :)
Quote from: rongel on Sat 25/11/2017 12:26:46
But what would be the smartest way to check if player clicks them in the right order? How can I compare the shuffle results to the player clicking the objects?
First of all, I would suggest to store object IDs in the array, not arbitrary 1-5 numbers, since Object.ID is already a number which you may also use to reference an object.
Secondly, you need to make this shuffled array a global variable in your room (if its just in one room), or global script / another script module (if same minigame may occur in other places).
Now, when you have an array of correct order (randomly shuffled), as player begins to click on the objects you record number of times he/she clicked and compare to corresponding array slot:
#define MAX_SPHERES 5
int ShuffledSpheres[MAX_SPHERES]; // let's pretend this is your shuffled array
int TimesClicked; // variable to store number of clicks
// Call this function when player interacts with sphere, passing sphere Object pointer as an argument
function WhenClicked(Object *o)
{
if (o.ID != ShuffledSpheres[TimesClicked])
{
// Wrong!! show some indication that player failed
TimesClicked = 0; // reset clicked counter
return; // exit
}
TimesClicked++; // correct, increase clicked counter
if (TimesClicked == MAX_SPHERES)
{
// Completed full sequence!
}
}
Uh-oh... seems I can't follow this.
I copied the new code where it should go (I think). Then I made five objects (oBlue, oRed and so on) and they have IDs from 1 to 5. I use this script when clicking them:
function oBlue_Interact()
{
WhenClicked(oBlue);
}
But I can't seem to connect this new checking-code to those previous results. I can't complete the sequence, every click gives a "Wrong!" answer. So how to use this new code so that it connects with the results that I got working before? I hope I make some sense explaining this!
Do you fill ShuffledSpheres array?
You need to pass it to ShuffleUnique function to make it write result in there.
Like:
ShuffleUnique(object_ids, 5, ShuffledSpheres, how much to shuffle number);
Quote from: Crimson Wizard on Sat 25/11/2017 13:40:21
Do you fill ShuffledSpheres array?
You need to pass it to ShuffleUnique function to make it write result in there.
Like:
ShuffleUnique(object_ids, 5, ShuffledSpheres, how much to shuffle number);
Sorry, but I still need more detailed help, I'm not sure where to write that, should I modify this original code:
Void ShuffleUnique(int numbers[], int length, int result[], int shuffle_time)
or this in room_AfterFadeIn():
ShuffleUnique(numbers, 5, result, 5);
I tried both, but got just errors. This seems a bit out of my league, but I'd hate to turn back now, because it seems it's close to working. Thanks for the help & patience!
You need to change the call of the function of course, not the declaration, because you need to define which array you are filling.
Example of room script
#define MAX_SPHERES 5
int ShuffledSpheres[]; // let's pretend this is your shuffled array
int TimesClicked; // variable to store number of clicks
// Call this function when player interacts with sphere, passing sphere Object pointer as an argument
function WhenClicked(Object *o)
{
if (o.ID != ShuffledSpheres[TimesClicked])
{
// Wrong!! show some indication that player failed
TimesClicked = 0; // reset clicked counter
Display("Wrong!");
return; // exit
}
TimesClicked++; // correct, increase clicked counter
if (TimesClicked == MAX_SPHERES)
{
// Completed full sequence!
TimesClicked = 0; // reset counter
Display("Correct!");
}
}
function room_AfterFadeIn()
{
TimesClicked = 0; // reset counter in case this is not the first time you enter the room
ShuffledSpheres = new int[MAX_SPHERES];
int object_ids[] = new int[MAX_SPHERES];
object_ids[0] = oBlue.ID;
object_ids[1] = oRed.ID;
object_ids[...] = ... and so on
ShuffleUnique(object_ids, MAX_SPHERES, ShuffledSpheres, 20); // 20 is an arbitrary number of shuffles
// Flash them to show sequence
int i;
while (i < MAX_SPHERES)
{
if (ShuffledSpheres[i] == oBlue.ID) Flash_Blue();
if (ShuffledSpheres[i] == oYellow.ID) Flash_Yellow();
if (ShuffledSpheres[i] == oGreen.ID) Flash_Green();
if (ShuffledSpheres[i] == oRed.ID) Flash_Red();
if (ShuffledSpheres[i] == oPurple.ID) Flash_Purple();
i++;
}
}
function oBlue_Interact()
{
WhenClicked(oBlue);
}
function oRed_Interact()
{
WhenClicked(oRed);
}
< ... and so on ... >
I just found there is a mistake in my script. Since function Shuffle requires a dynamic array, ShuffledSpheres should be a dynamic array and not fixed-sized:
// declaration
int ShuffledSpheres[];
function room_AfterFadeIn()
{
ShuffledSpheres = new int[MAX_SPHERES];
....
all the same code
}
Thanks, it seems to work now perfectly!
The code is pretty advanced to my standars, but I think I can work with it. Really appreciate the help!