Kenth Joshua Espina
Kenth Joshua Espina

Reputation: 87

Checking if color is already in used from array list C#

Hello i would like to check if the certain color in my array is already used then it will not use again. Im using currently random colors from the array, so sometimes it gets the same color.

// Add random block color
            Color[] colors = new Color[] { Color.Red, Color.Blue, Color.Green, Color.Purple, Color.Black, Color.Aqua };
            Random ra = new Random();
            int rnum = ra.Next(0, 5);
            p.block.brush_color = new SolidBrush(Color.FromArgb(100, colors[rnum]));
// Add new generated process to list
            processes.Add(p);

How I can achieve this?

Upvotes: 0

Views: 675

Answers (3)

Rufus L
Rufus L

Reputation: 37020

One way to handle this would be to shuffle the items in the array, then choose the items sequentially. The Fisher-Yates shuffle is typically regarded as the best algorithm for this:

private static Color[] colors =
{
    Color.Red, Color.Blue, Color.Green, Color.Purple, Color.Black, Color.Aqua
};

private static Random random = new Random();

private static void ShuffleColors()
{
    for (int index = 0; index < colors.Length; index++)
    {
        var randomIndex = index + random.Next(colors.Length - index);
        var temp = colors[randomIndex];
        colors[randomIndex] = colors[index];
        colors[index] = temp;
    }
}

But what happens when you get to the end of the array? It seems there are 3 possibilities:

  1. Just start at the beginning again (but then you'll have a non-random pattern)
  2. Shuffle the array and start at the beginning again
  3. Shuffle the array but make sure the last color used is not the first item

I think the last one may be preferable, since it will give the most random output, so let's implement that one.

What we can do is keep track of the last index we displayed, and then when it represents the last item in the array, we can shuffle the array again. Then, before we continue, we want to check to make sure the first item in the array isn't the last item we displayed. If it is, then we swap it with some other random element, so we never have a repeating item.

private static int nextIndex = 0;

private static SolidBrush GetNextColoredBrush()
{
    // If we've displayed all the items, shuffle the deck
    if (nextIndex == colors.Length)
    {                
        var lastColorUsed = colors[nextIndex - 1];
        ShuffleColors();

        // If the first color is the same as the last color
        // we displayed, swap it with a random color
        if (colors[0].Name == lastColorUsed.Name)
        {
            var rndIndex = random.Next(1, colors.Length);
            colors[0] = colors[rndIndex];
            colors[rndIndex] = lastColorUsed;
        }

        // Reset our index tracker
        nextIndex = 0;
    }

    // Return the next color and increment our index
    return new SolidBrush(colors[nextIndex++]);
}

Now we can call this method in a loop to see what the output will look like:

private static void Main()
{
    // Output our random colors. Note that even though we
    // only we only have 6 colors, they are consistently 
    // output in a random order (but with no duplicates).
    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine(GetNextColoredBrush().Color.ToKnownColor());
    }


    GetKeyFromUser("\nDone! Press any key to exit...");
}

Output

Notice that there are no repeating elements until 6 have been displayed, and then the next six elements are in a different order:

enter image description here

Usage in your code

Oh, finally, to use this in your code, you would just do something like:

p.block.brush_color = GetNextColoredBrush();

However you may notice that I changed the output slightly so that it output a KnownColor. If you want to maintain your original code, you should modify the line that returns a value in the GetNextColoredBrush method:

return new SolidBrush(Color.FromArgb(100, colors[nextIndex++]));

Upvotes: 2

Sach
Sach

Reputation: 10393

There's multiple approaches you can take.

Method 1 : Shuffle a List

Based on Rufus' link.

static void Main(string[] args)
{
    List<Color> ColorList = new List<Color>() { Colors.Red, Colors.Blue, Colors.Green, Colors.Pink };

    Shuffle(ColorList);

    while (ColorList.Count > 0)
    {
        Console.WriteLine(ColorList[0]);    // Equivalent of using the color.
        ColorList.RemoveAt(0);              // Remove from the list once used.
    }

    Console.ReadLine();
}

static void Shuffle<T>(IList<T> list)
{
    Random rng = new Random();

    int n = list.Count;
    while (n > 1)
    {
        n--;
        int k = rng.Next(n + 1);
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
    }
}

This is not a bad method if you know you're going to work with only a handful of colors. Because this method works only for as many colors as you add and run the risk of running out of colors.


Method 2 : Get a lot of colors.

You can create colors by specifying R, G, B, and A values of a color. First three corresponds to Red, Green, and Blue components of a color, and A which is Alpha is basically the opacity of color.

So you can create a color by

var color = Color.FromArgb(12, 23, 213, 44);

Here, range of each of the parameters is 0-255.

Now, this method will give you a LOT of colors which you're unlikely to run out of. I put a limit of 16 between the increments just so you don't get too many similar colors.

private static List<Color> GetLotsOfColors(int gap)
{
    gap = gap < 16 ? 16 : gap;

    List<Color> ColorList = new List<Color>();
    for (int r = 0; r < 256; r += gap)
    {
        for (int g = 0; g < 256; g += gap)
        {
            for (int b = 0; b < 256; b += gap)
            {
                for (int a = 0; a < 256; a += gap)
                {
                    ColorList.Add(Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b));
                }
            }
        }
    }
    return ColorList;
}

Now you can use the previous Shuffle() method to shuffle them and then get them one by one.

Upvotes: 1

Flydog57
Flydog57

Reputation: 7111

Taking all the comments into account, how about something like:

Color[] colors = new Color[] { Color.Red, Color.Blue, Color.Green, Color.Purple, Color.Black, Color.Aqua };
List<Color> randomColors = new List<Color>();
var random = new Random();

//fill the queue with a bunch of colors, careful to take fewer than exist
do
{
    var index = random.Next(colors.Length);
    var newColor = colors[index];
    if (!randomColors.Contains(newColor))
    {
        randomColors.Add(newColor);
    }
} while (randomColors.Count < colors.Length - 2);

foreach (var color in randomColors)
{
    Trace.WriteLine(color.ToString());
}

If you have many colors, you might want to have a HashSet<Color> that you use to track your used colors (rather than traversing the list over and over (a lot)).

Upvotes: 1

Related Questions