Basing on your answers in comments I still don't understand what exactly you need. AFAIU you have two pieces of data: scenes and heights; and you want to generate permutations of compound (scene, height) elements. I assume that you either need:
Generate a random list of all possible permuations exactly once
Generate a long (possibly infinite) stream of random different permuations
So here is some code that might help.
First let's define some boilerplate:
public class Scene
{
public readonly string Something;
public Scene(string something)
{
Something = something;
}
// something else
}
public struct CompoundSceneData
{
public readonly Scene Scene;
public readonly float Height;
public CompoundSceneData(Scene scene, float height)
{
Scene = scene;
Height = height;
}
}
Of course your Scene
class is most probably more complicated. CompoundSceneData
is a struct representing single item of scene + height.
#1 Generate a random list of all possible permuations exactly once:
// Fisher–Yates shuffle of indices 0..size
int[] GenerateRandomIndicesPermutation(int size)
{
int[] permutation = Enumerable.Range(0, size).ToArray();
Random rnd = new Random();
for (int cur = size; cur >= 2; cur--)
{
int swapPos = rnd.Next(cur);
int tmp = permutation[swapPos];
permutation[swapPos] = permutation[cur - 1];
permutation[cur - 1] = tmp;
}
return permutation;
}
List<CompoundSceneData> GenerateAllRandomPermutationsOnce(Scene[] scenes, float[] heights)
{
int scenesCount = scenes.Length;
int heightsCount = heights.Length;
int totalCount = scenesCount * heightsCount;
List<CompoundSceneData> permutations = new List<CompoundSceneData>(totalCount);
foreach (var compoundIndex in GenerateRandomIndicesPermutation(totalCount))
{
int sceneIndex = compoundIndex % scenesCount;
int heightIndex = compoundIndex / scenesCount;
permutations.Add(new CompoundSceneData(scenes[sceneIndex], heights[heightIndex]));
}
return permutations;
}
void TestUsageAllOnce()
{
Scene[] scenes = new Scene[] { new Scene("Scene #1"), new Scene("Scene #2") };
float[] heights = new float[] { 0.1f, 0.2f, 0.3f };
// this is effectively endless loop
foreach (CompoundSceneData sceneData in GenerateAllRandomPermutationsOnce(scenes, heights))
{
// will be called excactly 2*3 = 6 times
DrawScene(sceneData);
}
}
There are a few key ideas there:
If we have N scenes and M heights there will be NM permutations and given a number in range [0, NM-1] you can select a pair. For example, 2*N + 5 means 5-th scene + 2-nd height (in 0-based indices(!)).
Thus if we want to generate a sequence of different pairs of N scenes and M heights, it is enough to generate a random permuation of numbers [0, N*M-1] and use it as sequence of indices
There is a well known Fisher–Yates shuffle algorithm to create a random permutation.
#2 Generate an infinite stream of random different permuations:
IEnumerable<CompoundSceneData> GenerateInfiniteRandomStream(Scene[] scenes, float[] heights)
{
Random rnd = new Random();
while (true)
{
int sceneIndex = rnd.Next(scenes.Length);
int heightIndex = rnd.Next(heights.Length);
yield return new CompoundSceneData(scenes[sceneIndex], heights[heightIndex]);
}
}
void TestUsageInfinite()
{
Scene[] scenes = new Scene[] { new Scene("Scene #1"), new Scene("Scene #2") };
float[] heights = new float[] { 0.1f, 0.2f, 0.3f };
// this is effectively endless loop
foreach (CompoundSceneData sceneData in GenerateInfiniteRandomStream(scenes, heights))
{
DrawScene(sceneData);
// this is the only thing that will stop the loop
if (IsEndOfGame)
break;
}
}
void TestUsageInfinite2()
{
Scene[] scenes = new Scene[] { new Scene("Scene #1"), new Scene("Scene #2") };
float[] heights = new float[] { 0.1f, 0.2f, 0.3f };
List<CompoundSceneData> fixedSizeList = GenerateInfiniteRandomStream(scenes, heights).Take(100).ToList();
foreach (CompoundSceneData sceneData in fixedSizeList)
{
// this will be called 100 times (as specified in Take)
DrawScene(sceneData);
}
}
The only interesting thing here is a usage of a C# feature yield return
. This feature allows creating streams of data (IEnumerable
) from code that looks sequential.
Note that for the solution #2 there is no guarantee that each combination (scene+data) will occur only once per (N*M) items. It just generates random combinations that will have good statistical properties only in long run. It is possible to achieve this guarantee as well but it significantly complicates the code and probably the user will not notice anyway.