Reputation: 87
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
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:
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:
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
Reputation: 10393
There's multiple approaches you can take.
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.
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
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