Random Counter (Solved)

Started by Vincent, Wed 09/06/2021 11:18:41

Previous topic - Next topic

Vincent

Good day to all Agser, recently I was trying to write something but I realized that the approach I am using involves a really too long code to write so I hope someone can give me some useful advice or a better approach.

Initially the player starts on a screen where the game randomly chooses one of ten tasks to perform in a mission.

Code: ags

int r;
function room_Load()
{
   r = Random(9);
   if (r == 0) {lblTask.Text = "Find the apple";}
   else if (r == 1) {lblTask.Text = "Find the strawberry";}
   //etc
}


I have an int called "TasksCompleted" to keep track of how many tasks the player has completed, each time a task is finished it increases the int by one.
I also have for each task a boolean called "Task1Completed, Task2Completed... etc" which it turns to true when that specific task is completed.

Code: ags

function hHotspot3_Interact() //task 4 complete
{
   Task4Completed = true;
   TasksCompleted ++;
   player.ChangeRoom(1);
}


Now when a task is completed the player go to the initial screen, but now the game must choose 9 random tasks instead of 10 cause 1 task is completed. And so on, if 2 tasks are completed then the game must select 8 random tasks etc.

How do I tell to the game if a specific task is completed to remove that task from the random counter?
Any help is appreciated.

Matti

You can use an array to keep track of that.

Code: ags

// setting the variables at game start
bool TaskCompleted[10];
for (int i = 0; i < 10; i ++)
{
  TaskCompleted[i] = false;
}

// picking a random task that is still available
r = Random(9);
while (TaskCompleted[r]) r = Random(9);


If the tasks had more more parameters than a text and and a completion state I would've suggested using a struct.

eri0o

#2
You could also use a Set, and remove an element from the Set once it's task is done. Do the random using the elements of the Set (use GetItemsAsArray). You can convert strings back to int using AsInt and use String.Format ("%d",n) to store an int in a string.

Here's the manual page for Set: https://adventuregamestudio.github.io/ags-manual/Set.html

Vincent

#3
@Matti, thanks I'll test this and let you know if that works correctly or if there's something else. The tasks have only a text parameter so I dont think a struct is necessary.
With this approach how do I define that specific boolean is completed on the hotspot interact? Also anytime a task is completed the random counter should decrease by one, so if 9 tasks are completed the random counter should be random(8); and not random(9); anymore and it should erase the tasks completed from the random counter.

@eri0o thanks, I didn't think about the Set functions at all, never use it by the way. How would you do the random elements using this approach?

eri0o

#4
Well, you can extend the Set methods if you want things to be a little more intuitive (using integers instead of String for your case).

It would be something like, when you want to init it:

Code: ags
Set* tasks = Set.Create();
int max_tasks = 10;
for(int i=0; i<max_tasks; i++){
  tasks.Add(String.Format("%d",I));
}


Then when you need a new task to be picked

Code: ags
int GiveMeTask(){
  int remaining_tasks_count = tasks.ItemCount;
  int task_i = Random(remaining_tasks_count - 1); // random is inclusive
  String items[] = tasks.GetItemsAsArray();
  int task = items[task_i].AsInt;
  tasks.Remove (items[task_i]); // removes from list so it isn't here next time
  return task;
}


You need to handle in the above when there's only 1 or less tasks. Also I typed this on my phone, can have errors.

Matti

@Vincent: The way my code works you can't decrease the random variable, because task 6 may be completed while task 9 is not. It just repeats picking a random task until one is found that is not completed yet. This might not be the most elegant solution but should work without problems.

To set the specific task as completed just write TaskCompleted[4] = true; instead of Task4Completed = true; You could keep track of the current task with a variable and write TaskCompleted[CurrentTask] = true; I'm not sure how completing the tasks exactly works in you game.

@ eri0o: Ah. Set and Dictionary are still things I have to take a look at, as I'm not familiar with them yet. Thanks for reminding me ;)

eri0o

Hey Matti, your way works too. When I use something like this though, I try to use a seeded random, and then have this random initialized to a random small set (say 1 or 2 or 3 or 4), this way I can easily test all possible outcomes and guarantee none of them will stall - of course in this case the chance is negligible.

Vincent

@eri0o thanks, I'll test your approach and let you know what I can come up with.

@matti thanks, well it would not be necessary to decrease the random counter but only to not pick up a task which is already done, so it might work good in this way. I'll test it all of this and let you know. I was keeping track of the current task checking the text of that label (lblTask.Text == "Find the apple") so according with his text I knew which task the random counter selected and by doing so enable or disable a specific hotspot in room load, the task is completed simply once you click the enabled hotspot.

Thanks both guys for the input, I'll let you know.

eri0o

Hey Vincent, I may have misunderstood what you needed, so my code may not do exactly what you want but it may give you ideas. In the spirit of ideas, here's how I tracked objectives in I Rented a Boat (need to look into header too): https://github.com/ericoporto/i_rented_a_boat/blob/main/I_rented_a_boat/Objectives.asc

Vincent

#9
Thanks eri0o it surely gave me more ideas on how to approach this situation in a different way, thanks for sharing.
I'm taking some time right now to test your guys ideas and see what comes out of them.

Vincent

#10
Well before trying eri0o's approach I preferred first to test Matti's idea which I was a little more familiar with. I just finished testing and it's working 100% as I intended to be. I don't know how you have done that with a single line of code:

Code: ags
while (TaskCompleted[Task]) Task = Random(9);


Because one of the main problem in my mind was that if random had chosen the number 0 and 0 was already a completed task, it could fall back to 0 because of the random factor. So I was already thinking about doing a million checks if the random was the number 0 and if that mission was completed for every number from 0 to 9 etc.. luckily you avoided me this nightmare! :)

Thanks a lot Matti and eri0o for your support! I'll definitely keep in mind the Set functions for the future.

Khris

That's what a while loop does: it runs the loop code until the condition fails.

Which also means that when the final task is completed and you're running the loop again, the game will hang :-D

Vincent

Yes, sometimes I easily get lost on the most obvious things.
Btw yes, the game will hang when completing the final task, which is normal, but that's still easy to avoid.  :)

SMF spam blocked by CleanTalk