Combination with Repetition

Hello Unity Community,

I am trying to take an array of 5 numbers, i.e. (0,1,2,3,4) and generate a list of 3(x) of them y number of times.

0,1,2
0,2,2
3,1,4
2,2,1
3,2,0
etc, etc

My variables I want to adjust are: Combination Length: x - 3, 4, and 5; Number of combinations: y - 20 to start.

To help understand what I am attempting to do, imagine painting a room and you have 5 colors to choose from that have been already determined for you. You can only use 3 colors in the room and you can choose the same color 3 times. How can you easily generate a list of the possible combinations with repetitions?

Later levels the combinations (x) will be 3, 4, and 5 colors long and randomly distributed throughout the list.

I am a designer and not really a coder and barely understand what I am attempting to create, so if anyone is kind enough to help me overcome this dilemma could you not use variables like “i” and “foo”. I find it difficult to abstract my ideas to arbitrary variables, plus I want to become a better coder so understanding the relationships using more literal terms is helpful.

If you have any questions about what I am asking please contact me. I am sorry I don’t have code to start with as it has been difficult to find anything related using Unity.

Nathaniel

I wrote a script for you that has a single function, GiveMeRandomColorCombinations(), which gives you an array of custom color structures of any length you want, with each combination containing between 3 - 5 colors, which you specify. Each combination structure contains five colors, color1/2/3/4/5. Only the first three are used if you ask for three per combination and only the first four are used if you ask for four per combination. Each color is a number 1 through 5: it’s up to you to decide what number is what color once you get the list of combination structures.

using UnityEngine;
using System.Collections;

//The consumer gets a list of these structures when they ask for color combinations.
public struct ColorCombination {
    public int color1;
    public int color2;
    public int color3;
    public int color4;
    public int color5;
}

/*Attatch this script to whatever object need its information. When that object comes into existence, this script will automatically create
 lists of every possible three, four and five color long combination, where every color can be one of five colors and repeats are allowed.*/
public class ColorCombinator : MonoBehaviour {
    
    [SerializeField] private bool logExample = false;
    
    //The lists of combinations
    private int[] threeColorCombinations;
    private int[] fourColorCombinations;
    private int[] fiveColorCombinations;
    
    //Build the lists of combinations automatically
    void Start () {
        //The indexes to keep track of where we are in each array of combinations while we fill them
        int threeColorArrayIndex = 0;
        int fourColorArrayIndex = 0;
        int fiveColorArrayIndex = 0;
        
        //The arrays of three, four and five color combinations using five colors, 1 through 5
        threeColorCombinations = new int[125];
        fourColorCombinations = new int[625];
        fiveColorCombinations = new int[3125];
        
        //The last combination we made, to be placed in the arrays
        //This is here to prevent us from making thousands of local integers inside the for loops
        int threeColorCombination;
        int fourColorCombination;
        int fiveColorCombination;
        
        //For every possible combination, add it to the correct array
        for(int color1 = 1; color1 <= 5; color1++){
            for(int color2 = 1; color2 <= 5; color2++){
                for(int color3 = 1; color3 <= 5; color3++){
                    
                    threeColorCombination = color1 + (color2 * 10) + (color3 * 100);
                    
                    threeColorCombinations[threeColorArrayIndex] = threeColorCombination;
                    
                    threeColorArrayIndex++;
                    
                    for(int color4 = 1; color4 <= 5; color4++){
                        
                        fourColorCombination = color1  + (color2 * 10) + (color3 * 100) + (color4 * 100);
                    
                        fourColorCombinations[fourColorArrayIndex] = fourColorCombination;
                    
                        fourColorArrayIndex++;
                        
                        for(int color5 = 1; color5 <= 5; color5++){
                            
                            fiveColorCombination = color1 + (color2 * 10) + (color3 * 100) + (color4 * 1000) + (color5 * 10000);
                    
                            fiveColorCombinations[fiveColorArrayIndex] = fiveColorCombination;
                    
                            fiveColorArrayIndex++;
                            
                        }
                    }
                }
            }
        }
        
        if(!logExample)
            return;
        
        //EXAMPLE OF USAGE. Stick this script on anything in your scene, run and examine the console.
        
        //This is how you obtain a list. The first number is the amount of colors per combination and the second is how many you need.
        ColorCombination[] tenThreeColorCombinations = GiveMeRandomColorCombinations(3, 10);
        
        //The combinations are structures. .color1 to .color5 give you the colors in a combination structure. It's up to you
        //to map each number to a meaningful color. In this example, 1 is red, 2 is blue, 3 is green, 4 is yellow and 5 is orange.
        //"nocolor" is color 0. You'll only get a 0 if you try to access color4 or 5 on a three color combination or color5 on a four color combination.
        string[] colors = {"nocolor", "red", "blue", "green", "yellow", "orange"};
        
        //Using the numbers in the combination to get the color we want and print a string of them
        //In example, a ColorCombination of 1 2 3 would print out "red blue green"
        foreach(ColorCombination combo in tenThreeColorCombinations){
            Debug.Log(colors[combo.color1] + " " + colors[combo.color2] + " " + colors[combo.color3]);
        }
    }

    //Returns an array of "number" integers out of "choices."
    //All choices are unique elements, unless "choices" contains duplicates.
    private int[] PickRandomUniqueIntegers(int[] choices, int number){

       //A safety check. Prevents us from going out of bounds if the array of choices is too small and such.
       if(choices.Length == 0 || number > choices.Length || number < 1){
          Debug.Log("The operation could not be completed.");
          return new int[0];
       }

       //The array to return once we are done picking integers
       int[] picked = new int[number];

        //For as many numbers are requested, grab a random number, place it in the new array.
        //We swap the chosen number with the last one in the array and shorten our random range
        //by one each iteration so that no element can be chosen twice.
        for(int i = 1; i <= number; i++){
            int index = Random.Range(0, choices.Length - i);

            int choice = choices[index];
            choices[index] = choices[choices.Length - i];
            choices[choices.Length - i] = choice;
            
            picked[i - 1] = choice;
        }

        return picked;
    }
    
    //Returns an array of ColorCombination structures "numberOfCombinationsRequested" long.
    //Each combination is made up of "numberOfColorsPerCombination" colors.
    public ColorCombination[] GiveMeRandomColorCombinations(int numberOfColorsPerCombination, int numberOfCombinationsRequested){
        //The raw integer form of our combinations at the start
        int[] rawCombinations;
        //The structured combinations we return at the end
        ColorCombination[] completeCombinations = new ColorCombination[numberOfCombinationsRequested];
        
        //We only handle combinations 3/4/5 colors long
        if(numberOfColorsPerCombination < 3 || numberOfColorsPerCombination > 5){
            Debug.Log ("We only can give out combinations three, four or five colors long.");
            return new ColorCombination[0];
        }
        
        //Get a list of unique combinations from one of the arrays
        if(numberOfCombinationsRequested == 3)
            rawCombinations = PickRandomUniqueIntegers(threeColorCombinations, numberOfCombinationsRequested);
        else if (numberOfCombinationsRequested == 4)
            rawCombinations = PickRandomUniqueIntegers(fourColorCombinations, numberOfCombinationsRequested);
        else
            rawCombinations = PickRandomUniqueIntegers(fiveColorCombinations, numberOfCombinationsRequested);
        
        //the index in the completeCombinations array that we are working on
        int completeCombinationsIndex = 0;
        
        //for every raw integer combination we had obtained, store it in the complete combination array correctly
        foreach(int raw in rawCombinations){
            //we can't alter the variable of a foreach loop, so copy it
            int combination = raw;
            
            //We use modulo arithmetic to obtain the colors in the order we put them in the integer
            //We take advantage of division + truncation to "slide" the integer to the next position
            
            completeCombinations[completeCombinationsIndex].color1 = combination % 10;
            combination /= 10;
            
            completeCombinations[completeCombinationsIndex].color2 = combination % 10;
            combination /= 10;
            
            completeCombinations[completeCombinationsIndex].color3 = combination % 10;
            combination /= 10;
            
            completeCombinations[completeCombinationsIndex].color4 = combination % 10;
            combination /= 10;
            
            completeCombinations[completeCombinationsIndex].color5 = combination % 10;
            
            //Move to the next combination
            completeCombinationsIndex++;
        }
        
        //Return the completed list of combinations
        return completeCombinations;
    }
}

In your project, wherever you store scripts, make a new C# script and name it ColorCombinator. Copy and paste the above code over the whole thing. Next, drag the script to whatever object needs the color combinations to add it as a component. When the object gets created, this script will automatically create and store three arrays containing all 3875 color combinations, three, four and five colors long, where a color is signified by a number from 1 to 5. Here’s an example of usage, which can be put in the Start() function of any other script on the same object the ColorCombinator is attached to.

        //First, we get a reference to the ColorCombinator.
        ColorCombinator combinator = GetComponent<ColorCombinator>();

        //This is how you obtain a list. The first number is the amount of colors per combination and the second is how many combinations you need.
        ColorCombination[] tenThreeColorCombinations = combinator.GiveMeRandomColorCombinations(3, 10);
        
        //The combinations are structures. color1 to color5 give you the colors in a combination structure. It's up to you
        //to map each number to a meaningful color. In this example, 1 is red, 2 is blue, 3 is green, 4 is yellow and 5 is orange.
        //"nocolor" is color 0. You'll only get a 0 if you try to access color4 or 5 on a three color combination or color5 on a four color combination.
        string[] colors = {"nocolor", "red", "blue", "green", "yellow", "orange"};
        
        //Using the numbers in the combination to get the color we want and print a string of them
        //In example, a ColorCombination of 1 2 3 would print out "red blue green"
        foreach(ColorCombination combo in tenThreeColorCombinations){
            Debug.Log(colors[combo.color1] + " " + colors[combo.color2] + " " + colors[combo.color3]);
        }

This exact example is in the ColorCombinator itself already. If you want to demo it as quickly as possible, stick it on any game object in your scene and check the “Logs Example” box on the script in the inspector, then hit run. It’ll log out ten random three color combinations.

This script has to rebuild all its lists every time you destroy it and create another one, so it’s a good idea to stick it on some object you know will persist through multiple levels/etcetera. It does take a little over 7KB of memory to keep the colors cached, but it’s very quick if you need a large list of possible combinations. The only alternative I could think of would be to randomly generate them, but this would be very slow if you wanted, say, a list of a hundred unique three color combinations. If you ask for a list of combinations, the list will not contain duplicates at very little computational cost. So, if the list has the combination 1 1 1, which we’ll say means red red red, you won’t see it again in the array. Obviously, you could get it again in another call to the function since that would give you a new array/list.

The maximum amount of combinations you can get in one array is 125 for three color combinations, 625 for four color combinations and 3125 for five combinations. This is because there’s only that many possible combinations, period, so asking for that many actually gives you an array of every single possible permutation.

This was an interesting script to write. I hope it helps you. :slight_smile: