0

I have a list of 7 colors : Red, Blue, Green, Maroon, Brown, Aqua and Black.

In my program I have it so you click on a box, and then the box gets filled with a colour. I want it to be a random colour (from my list of 7 colours) for every box, which I have managed to do below:

    Random random = new Random();
        int randomColour = random.Next(0,6);

        if (e.Button == MouseButtons.Left)
        {
            //got the y values of the grid

            //got the x values of the grid

            //Randomize The Colours
            if (randomColour == 0)
            {
                Form.GetTile(x, y).FrontColour = Color.Red;
                Score = Score + 1;
            }
            else if (randomColour == 1)
            {
                Form.GetTile(x, y).FrontColour = Color.Blue;
                Score = Score + 2;
            }
            else if (randomColour == 2)
            {
                Form.GetTile(x, y).FrontColour = Color.Maroon;
                Score = Score + 5;
            }
            else if (randomColour == 3)
            {
                Form.GetTile(x, y).FrontColour = Color.Aqua;
                Score = Score + 10;
            }
            else if (randomColour == 4)
            {
                Form.GetTile(x, y).FrontColour = Color.Black;
                Score = Score - 3;
            }
            else if (randomColour == 5)
            {
                Form.GetTile(x, y).FrontColour = Color.Brown;
                Score = Score - 1;
            }
            else if (randomColour == 6)
            {
                Form.GetTile(x, y).FrontColour = Color.Green;
                Score = Score + 3;
            }

However, I want to set up my code so there can only be a maximum of 20 red boxes, 20 blue boxes, 5 green, 5 brown, 4 aqua, 5 maroon, and 5 black.

This should be the output, except more shuffled.

The Form.GetTile(x,y).FrontColour is a property that I am accessing from another class that changes the colours of the box

J. Doe
  • 13
  • 3
  • so the problem is getting random colors or setting up limits of your colors??? – Bilal Nov 18 '15 at 05:08
  • You can add this into a dictionary and declare your own type as the key which contains the color and the number of appearances. Every time you randomly select a color you should subtract it from the appearances – Dave Nov 18 '15 at 05:12
  • @Dave you'd have to use weighted random values to avoid pile of red/blue at the end (assuming each color selected with equal probability). It is easier to generate somewhat random number of colors first and than simply shuffle. – Alexei Levenkov Nov 18 '15 at 05:21
  • @Bilal the problem is setting up the limits on my colours – J. Doe Nov 18 '15 at 05:34

2 Answers2

3

You can pre-shuffle a list of the numbers you want to apply

public class ShuffledChoices<T>{
   private readonly List<T> Choices;
   private Random rng = new Random();
   public ShuffledChoices(IEnumerable<T> choices){
     Choices = new List<T>(choices);
   }
   public T PickNext(){
     var i = rng.Next(Choices.Count); // lock may not be a bad idea
     var pick = Choices[i];
     Choices.RemoveAt(i);
     return i;
   }
}

Using it:

var baseChoices = Enumerable.Repeat(Colors.Red, 20)
  .Union(Enumerable.Repeat(Colors.Blue, 20))
  .Union(Enumerable.Repeat(Colors.Green, 5))...;

var shuffledColors = new SuffledChoices<Color>(baseChoices);

...
if (e.Button == MouseButtons.Left){
   Form.GetTile(x, y).FrontColour = shuffledColors.PickNext();
}
Sten Petrov
  • 10,943
  • 1
  • 41
  • 61
  • Side note: using [standard shuffle](http://stackoverflow.com/questions/273313/randomize-a-listt-in-c-sharp) would avoid compacting list all the time (also it does not really matter that much for such a small number of items). – Alexei Levenkov Nov 18 '15 at 05:27
0

Create a dictionary containing your own colors and then keep track of the number of times you have selected a color

Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add("Red", 20);
dictionary.Add("Blue", 20);
dictionary.Add("Green", 5);

Then randomly select an item from the dictionary and subtract the occurrence

bool colourSelected = false;
    do
    {
        var rnd = new Random();
        var randomEntry = dictionary.ElementAt(rnd.Next(0, dictionary.Count));
        String randomKey = randomEntry.Key;
        String randomValue = randomEntry.Value;
        if(randomvalue > 0)
        {
            // Take color
            // could also add in logic to remove a color once it reaches 0, 
            // this way we don't select colors that are unavailable 
            dictionary[randomKey] = randomValue - 1;
            colourSelected = true;
        }
    }
    while(colourSelected == false);

The code might need a little work, I haven't run it at all so there might be a few thing you will need to fix.

Dave
  • 967
  • 10
  • 23
  • so what happens if you have a very long list of possible colors and there's only one pick left somewhere in the dictionary? do you keep picking random numbers until the last one comes up? – Sten Petrov Nov 18 '15 at 05:26
  • Sure, but there are only 7 colours, logic could be added to remove colours once they reach 0 – Dave Nov 18 '15 at 05:29
  • as Alexei pointed out in another comment you'll still usually end up with a bunch of reds and blues picked in the end as all colors have equal probability, so chances are the other colors are removed first – Sten Petrov Nov 18 '15 at 05:34
  • This would happen in most cases unless a better combination of colors is used, the ration is too high to really prevent this from happening. If you use weighted randoms that might lessen it but you will still end up with piles of Red and Blue – Dave Nov 18 '15 at 05:39
  • not true: if you remove *individual copies of a color* with equal probability then the probability of the last color remaining to be green is 5/64 (~7%). If you remove *color count* and all colors are with equal probability then chances of getting green last are... slim (~0.015%) - for sequential picking after 7colorsx5green=35hits you'd have depleted the green bank but there would still be 15 reds left – Sten Petrov Nov 18 '15 at 06:01
  • In theory what you are saying is true, but the ratio of colors is so big that even with a weighted list you could run into the problem of having 15 reds left. Just because something has a higher probability to be selected does not mean it will be selected. The only way to really make sure that you have an even spread would be to use an algorithm to combine a pattern and randomization effect. so for every 10 blocks you need 3 blue, 3 red, 1 green... – Dave Nov 18 '15 at 06:09
  • But yes @StenPerov the chances of your answer giving a better spread is higher than with my solution – Dave Nov 18 '15 at 06:13