1

I have a class that randomly creates some objects on the stage, These objects appear at two random points. The problem is Random.Range has the probability of choosing a point several times in a row, that is, I pick the same point 5 or 6 times. I just want to limit myself somehow, that if Random.Range chose the same point at most 3 times, it would choose another point.

public ObjectSequence ObstacleSequence;
public float WaitSpawnTime;
public float DistanceFromObstacleIndex;
public Transform[] spawnPoints;
private List<int> Point = new List<int>();

void Start()
{
    InvokeRepeating("Spawn", WaitSpawnTime, DistanceFromObstacleIndex);
}

void Spawn()
{
    int spawnPointIndex;
    do
    {
        spawnPointIndex = Random.Range(0, spawnPoints.Length);
        Point.Add(spawnPointIndex);
    } while (SwitchPoint());
    if (SwitchPoint())
        Point.Clear();
    ObjectSequence obstacle = Instantiate(ObstacleSequence,
       spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation);
    obstacle.setRandomCurrentChildIndex();
    obstacle.CurrentChild.GetComponent<SpriteRenderer>().flipY = 
       (obstacle.transform.position == spawnPoints[1].position) ? true : false;
}

private bool SwitchPoint()
{
    if (Point.Count >= 2)
    {
        return Point[0].Equals(Point[1]) && Point[1].Equals(Point[2]);
    }
    return false;
}

I only have two points, so "shuffle" approach (like Randomize a List<T> or many other "unique random" posts) does not work.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Geo_Tek
  • 13
  • 5
  • 1
    So it would be not so random, right? – SᴇM Dec 13 '18 at 13:12
  • Take a look at this post [Random number generator with no duplicates](https://stackoverflow.com/questions/26931528/random-number-generator-with-no-duplicates). You can store them in array, then check if the appearance of some item is more than your limit. – SᴇM Dec 13 '18 at 13:13
  • Yeah, but I just need a Random.Range. Such as (0,0,1,0,1,1,1,0,0,0,1 ........). Just understand how to avoid giving me the same index more than 3 times – Geo_Tek Dec 13 '18 at 13:18
  • 1
    You can check if last 3 items are same, generate again until you will get another result. For that you can store last 3 items in array. – SᴇM Dec 13 '18 at 13:20
  • yes, I think I'm going to use it Do While – Geo_Tek Dec 13 '18 at 13:21
  • 1
    Limiting actually makes it less random.Don't know if that's a problem but it would make the game predictable in some situations. – Brian Rasmussen Dec 13 '18 at 13:31
  • Okay, I changed, but I do not know if that's ok,If I ask you to look over the code above. Thanks – Geo_Tek Dec 13 '18 at 13:32
  • 2
    Possible duplicate of [Avoiding random duplicates](https://stackoverflow.com/questions/13195738/avoiding-random-duplicates) – Draco18s no longer trusts SE Dec 13 '18 at 17:11
  • @Geo_Tek I've edited the post - please confirm that edit is inline with what you are looking for, especially title. – Alexei Levenkov Dec 13 '18 at 19:35

2 Answers2

0

The general idea, no matter how many people think it's inefficient, is to simply discard values until you get one that's good, and I guarantee this is more performant for a small number of selections than any solution based on shuffling a whole list. Anyway, to solve your problem, you need to store the last 3 generated numbers and if they're all the same, you can simply choose the other spawn point, like so:

if (generated[0] == generated[1] && generated[0] == generated[2])
    return 1 - generated[0]; // flip 0 to 1 and 1 to 0
else
    return Random.Range(0, 2);

However, should you have more than 2 numbers, here's what you'd do (still based on retrying, and still more efficient than shuffling):

if (generated[0] == generated[1] && generated[0] == generated[2])
{
    int newNumber;
    do
        newNumber = Random.Range(0, maximum);
    while (newNumber == generated[0]);
}
else
    return Random.Range(0, maximum);
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Arshia001
  • 1,854
  • 14
  • 19
  • This is very bad solution for general case (with many numbers) as it slows down significantly at the end... and completely unrelated to this question where OP has only 2 possibilities to chose from. – Alexei Levenkov Dec 13 '18 at 19:36
  • Well, this is not the general case, and as you say has *only two numbers*, so it looks good to me. The first one always succeeds and the second has only a 1/n chance to fail. You need to design your approach to problems around the data you'll be working with, not try to solve the most general problem all the time. – Arshia001 Dec 13 '18 at 19:53
  • Thanks for edit (I've removed completely unrelated previous part that you forgot to clean up)... I'm very surprised how you can " I guarantee this is more performant for a small number of selections than any solution based on shuffling a whole list" when generating single random number from the list with shuffling needs exactly one call to Random (can be done one at a time whenever new number is requested)... Now shuffling does not help in this question at all - so really not much value to bring it up. – Alexei Levenkov Dec 14 '18 at 00:55
  • Because the shuffle itself is an O(n) operation. And you can't just pre-shuffle a list and cache it, you'd need a new shuffled list every once in a while. – Arshia001 Dec 14 '18 at 08:09
0

Depending on your actual goal using hardcoded table of "random" choices may be good option. This is good choice for games where gameplay will benefit of correctly predicting next choice (i.e. "dumb robotic enemies" with fixed behavior like Left, Left, Right, Left, repeat).

Alternatively to get "true random from human point of view" you can count how often particular choice recently happen and if it is above threshold force the other choice (and discard counters). More complex approach would be weight choices based on some previous results.

Here is sample that will at most generate 3 of the same values in a row:

class NiceRandom
{
   static Random rand = new Random();
   int count = 0;
   int prevChoice = -1;
   public int Next()
   {
       int r = rand.Next(0,2);
       if (r == prevChoice)
       {
           count++;
           // switch our "random" result if there are 3 of the same rolls
           r = count < 3 ? r : (r == 0 ? 1 : 0);
       }

       if (r != prevChoice)
       {
           // reset count if we roll other value.
           prevChoice = r;
           count = 0;
       }

       return r;
   }
}

Warning: making "random" numbers to behave differently to feel more "natural" (like code above) will likely cause distributions to no longer be uniform/random - make sure result looks good for your game/app and you actually don't need true randomness. You may want to check requirements for randomness for slot machines What Type of Random Number Generator is Used in the Casino Gaming Industry? to learn more.

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179