I'm working on a small C# Winforms application that replicates several visual experiments concerning cognitive psychology. Instead of using premade images, I was trying to create them programmatically to train my skills.
Now I'm working on the Stroop Effect and I'm attempting to reproduce the following image:
I prepared two distinct arrays, one containing the colors and one containing the name of the colors:
Color[] colors =
{
Color.Blue,
Color.Red,
Color.Black,
Color.Green
};
String[] names =
{
"BLUE",
"RED",
"BLACK",
"GREEN"
};
The first step is easy. All I have to do is to draw the strings using their respective ForegroundColor, such that names[0] will be printed using colors[0], names[1] will be printed using colors[1] and so on...
And here comes the hard part of the task. Once the string array has been shuffled:
Random r = new Random();
String[] namesRandom = names.OrderBy(x => r.Next()).ToArray();
I have to shuffle the colors array too. But this must be achieved making sure that no color is being matched with its correct name. So, for example, this will be correct:
"BLACK" Color.Green
"GREEN" Color.Red
"RED" Color.Blue
"BLUE" Color.Black
and this will be wrong:
"BLACK" Color.Black -> Matching Color/Name
"GREEN" Color.Red
"RED" Color.Blue
"BLUE" Color.Green
Any idea about how to accomplish this efficiently? I would like to avoid the shuffle until condition is met approach.
P.S. = drawing strings on a Bitmap is not a problem and I don't need help on this development task.
[EDIT]
For 10k-ers that downvote for not being able to associate the concept of shuffle until condition is met with a few lines of code or because they think I'm trying to get my work done while I play Tetris, here is a quick implementation of my current algorithm:
public class Program
{
private static Random s_Random = new Random();
public static void Main(String[] args)
{
Color[] colors =
{
Color.Blue,
Color.Red,
Color.Black,
Color.Green
};
String[] names =
{
"BLUE",
"RED",
"BLACK",
"GREEN"
};
while (MatchingPairs(colors, names))
{
colors = Shuffle(colors);
names = Shuffle(names);
}
for (Int32 i = 0; i < colors.Length; ++i)
Console.WriteLine(colors[i].Name.ToUpperInvariant() + " | " + names[i]);
}
private static Boolean MatchingPairs(Color[] colors, String[] names)
{
for (Int32 i = 0; i < colors.Length; ++i)
{
if (colors[i].Name.ToUpperInvariant() == names[i])
return true;
}
return false;
}
private static T[] Shuffle<T>(T[] array)
{
return array.OrderBy(x => s_Random.Next()).ToArray();
}
}
So, as you can see, I can use my fingers to type code. I'm just wondering if there is a better approach to obtain the same result.
This is maybe not the best approach, but it should work fine. One way to solve this would be to assign random names until you have only two left (where index i == Count - 2). At that point, if the name of the item at index i + 1 is one of the remaining choices, use that for the item at index i.
Here's some sample code that uses a console application (and ConsoleColor), but the concept is the same. I've used a class that combines a ConsoleColor and a string, so if you want to use arrays it would need some modification.
First the class:
class NamedColor
{
public ConsoleColor Color { get; set; }
public string Name { get; set; }
public NamedColor(ConsoleColor color, string name)
{
Color = color;
Name = name;
}
}
And a method that prints out a list of these objects to the Console:
static void PrintColors(IEnumerable<NamedColor> colors)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.Write(new string(' ', 80));
foreach (var color in colors)
{
Console.ForegroundColor = color.Color;
Console.Write(color.Name + " ");
}
Console.Write(new string(' ', 139)); // Ugly, hard-coded length for simplicity
}
Then we can create a method that will take a list of these objects, shuffle all the names between them, and return that new list:
static NamedColor[] ShuffleNames(NamedColor[] colors)
{
var names = colors.Select(c => c.Name).ToList();
var shuffled = new NamedColor[colors.Length];
for (int i = 0; i < colors.Length; i++)
{
string name;
// If there are only two items left, and our list of names contains
// the *next* item's name, then we must take that name for this item
if (i == colors.Length - 2 && names.Contains(colors[i + 1].Name))
{
name = colors[i + 1].Name;
}
else
{
// Choose a random name from all names except this item's name
var candidateNames = names.Where(n => !n.Equals(colors[i].Name)).ToList();
name = candidateNames[rnd.Next(candidateNames.Count)];
}
shuffled[i] = new NamedColor(colors[i].Color, name);
names.Remove(name);
}
return shuffled;
}
Then we can shuffle our list, then shuffle the names, and then print out the list like so:
private static Random rnd = new Random();
static void Main(string[] args)
{
var correctColors = new NamedColor[]
{
new NamedColor(ConsoleColor.Blue, "BLUE"),
new NamedColor(ConsoleColor.Black, "BLACK"),
new NamedColor(ConsoleColor.Red, "RED"),
new NamedColor(ConsoleColor.Green, "GREEN"),
};
PrintColors(correctColors);
for (int count = 0; count < 10; count++)
{
// Shuffle the items, then shuffle the names
var shuffledColors = correctColors.OrderBy(c => rnd.NextDouble()).ToArray();
shuffledColors = ShuffleNames(shuffledColors);
PrintColors(shuffledColors);
}
Console.ResetColor();
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
Output
You could do the following:
Implement a rule of valid colors for each name:
Func<string, IEnumerable<Color>> validColorsRule = s =>
{
switch (s)
{
case "BLUE": return colors.Except(new[] { Color.Blue });
case "RED": return colors.Except(new[] { Color.Red });
case "BLACK": return colors.Except(new[] { Color.Black });
case "GREEN": return colors.Except(new[] { Color.Green });
default: throw new NotSupportedException();
}
};
Build the shuffled Color array with Enumerable.Aggregate:
Color[] colorRandom =
namesRandom.Aggregate(Enumerable.Empty<Color>(),
(acc, n) =>
acc.Concat(new[] {
validColorsRule(n).Except(acc)
.OrderBy(x => r.Next())
.FirstOrDefault() }))
.ToArray();
You could reformulate as a pseudo-random problem.
Generate a random integer between 1 and n-1(say i) and shift the color array(use a copy of it) by that i
Now generate a random sequence between 0 and n-1 without replacement and print the name and color
static void Main(string[] args)
{
ConsoleColor[] colors =
{
ConsoleColor.Blue,
ConsoleColor.Red,
ConsoleColor.Black,
ConsoleColor.Green
};
String[] names =
{
"BLUE",
"RED",
"BLACK",
"GREEN"
};
Random r = new Random();
int i = 0;
while(i==0)
i=r.Next() % (names.Length-1);
List<int> rndList = Enumerable.Range(0,names.Length).OrderBy(x => r.Next()).ToList();
Console.BackgroundColor = ConsoleColor.White;
foreach(int j in rndList)
{
int k = (j+i) % (colors.Length);
Console.ForegroundColor = colors[k];
Console.Write(names[j] + " ");
}
}
I think that you are trying to create a random derangement. A web search for this finds a paper about the efficient generation of such things at http://epubs.siam.org/doi/pdf/10.1137/1.9781611972986.7 - "Generating Random Derangements" by Martinez, Panholzer, and Prodinger. However the paper also shows that, even as n gets possibly very large, the chance of a randomly chosen permutation being a derangement remains very close to 1/e, so the simple solution of just generating a random permutation and checking to see if it is a derangement, repeating until this is true, may in practice be efficient enough for most purposes.
Related
Disclaimer: I have probably included too much information, so don't be scared off by the amount of info included.
I want to make a simple 2D game with graphics for the c# console application. To learn how I will handle the basics of the graphics I made a small project where I am playing around with the base Color class (and the library Colorful.Console, more info at: http://colorfulconsole.com/). Just trying to understand the basics of color representation and mixing.
However, I have run into an issue when it comes to painting multiple colors. Since the console only can keep 16 colors by default, it will return custom rgb colors until the 15th iteration (I assume one color is reserved for black) of the program, and then only return the 15th over and over. Is there any way of changing the value of one of the "KnownColor"s over and over again instead of filling all of them and then not being able to overwrite the new values?
My code (simplified) looks something like:
class Object{
Color color;
//for simplicity, let's say that I previously had byte a, r, g, b defined here;
public Object()
{
byte[] rgba = new byte[4];
Random random = new Random();
random.NextBytes(rgba);
color = Color.FromArgb(rgba[0], rgba[1], rgba[2], rgba[3]);
//previously I assigned a = rbga[0] and so on.
}
}
class Program
{
List<Object> objects = new List<Object>();
static void Main(string[] args)
{
while(true)
{
objects.Add(new Object()); //I have some user input here, but essentially.
foreach(Object o in objects)
{
PrintColor(o.color);
PaintColor(o.color);
}
}
}
void PrintColor(Color c)
{
Console.Write("R:" + c.R + ", G:" + c.G +", B:"+ c.B + ", A:" + c.A);
}
static void PaintColor(Color c)
{
Console.BackgroundColor = Color.FromArgb(c.R, c.G, c.B);
Console.Write(" ");
Console.BackgroundColor = Color.Black;
}
}
I also tried to make a program that is supposed to draw a diagonal gray gradient (and later on a full-rgb gradient), but in vain.
class Program
{
static void Main(string[] args)
{
GraphicsEngine.GetWindowSize();
while (true)
{
GraphicsEngine.FillWindowGradient();
Console.Clear(); //I am going to replace this with moving the cursor to 0,0
if (Console.KeyAvailable)
{
GraphicsEngine.GetWindowSize();
}
}
}
public static void FillWindowGradient()
{
int yMax = windowSize.GetLength(0);
int xMax = windowSize.GetLength(1);
for (int y = 0; y< yMax; y++)
for (int x = 0; x < xMax; x++)
{
PaintGray((byte)((y/yMax + x/xMax)*127));
}
}
public static void PaintGray(byte gray)
{
Console.BackgroundColor = Color.FromArgb(gray, gray, gray);
Console.Write(" ");
Console.BackgroundColor = Color.Black;
}
}
This example worked with any one color it started with, but couldn't handle the gradient, something I assume have something to do with the same issue as in the first example.
PS: it would also be nice to know if there's a "cheaper"/faster way of coloring a cell than:
Console.BackgroundColor = Color.Argb(a, r, g, b);
Console.Write(" ");
Your problem is that your are creating a new Random object every time you generate a new object of type Object.
From MSDN:
On the .NET Framework only, because the clock has finite resolution,
using the parameterless constructor to create different Random objects
in close succession creates random number generators that produce
identical sequences of random numbers.
Possible solution:
Create a single Random object in your Program class and use initialize it using the current timestamp.
Random _random = new Random((int)DateTime.UtcNow.Ticks);
Pass the instance of the random to the Object class.
OR, much cleaner -> Pass the actual random color to the Object in constructor.
class Object
{
private Color color;
public Object(Color c)
{
color = c;
}
}
var rgba = new byte[4];
random.NextBytes(rgba);
var obj = new Object(Color.FromArgb(rgba[0], rgba[1], rgba[2], rgba[3]));
Edit:
Following up on your comments.
I've used your example to generate Object instances in a loop, generating a random Color.
At the end, checking for duplicates using ToArgb as a key.
Running 1000 times in a loop, generated no duplicates.
Running 1M times, ~110 duplicates on average.
class Program
{
static List<Object> objects = new List<Object>();
static Random _random = new Random((int) DateTime.UtcNow.Ticks);
static void Main(string[] args)
{
var i = 0;
while (i<1000)
{
var rgba = new byte[4];
_random.NextBytes(rgba);
objects.Add(new Object(rgba));
i++;
}
foreach (Object o in objects)
{
PrintColor(o.color);
}
var query = objects.GroupBy(x => x.color.ToArgb())
.Where(g => g.Count() > 1)
.Select(y => y.Key)
.ToList();
Console.WriteLine($"Duplicates {query.Count}");
}
static void PrintColor(Color c)
{
Console.WriteLine("R:" + c.R + ", G:" + c.G + ", B:" + c.B + ", A:" + c.A);
}
}
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?
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:
Just start at the beginning again (but then you'll have a non-random pattern)
Shuffle the array and start at the beginning again
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:
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++]));
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)).
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.
I'm trying to change the elements of an array randomly, by changing the indexes. Thats ok. Now the problem is that as the random is always random, I can get two results that are the same.
for example:
Monday:
song 1
song 2
song 3
Tuesday:
song 2
song 1
song 3
Wednesday:
song 1
song 2
song 3
And so on...
And the list from
Monday
and
Wednesday
in this case is the same. I need to control that, but as you can see on the code, once I get the list from one day, I just print it. I thought about putting it on an array or Tuples and check if that tuple exists, but I think its too complicated. I thought that maybe I can make my own random function. But still, I'm not sure about that solution either. Any ideas of how can I solve this situation? Thanks!
Here is the code I have so far:
static string[] songs = new string[] { "song1", "song2", "song3" };
static string[] days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
private static Random random = new Random();
/* Random number between lower and higher, inclusive */
public static int rand(int lower, int higher)
{
int r = lower + (int)(random.Next(0, 2) * (higher - lower));
return r;
}
/* pick M elements from original array. Clone original array so that
7 * we don’t destroy the input. */
public static string[] pickMRandomly()
{
string[] subset = new string[songs.Length];
string[] array = (string[])songs.Clone();
for (int j = 0; j < songs.Length; j++)
{
int index = rand(j, array.Length - 1);
subset[j] = array[index];
array[index] = array[j]; // array[j] is now “dead”
}
return subset;
}
public static void playListCreation()
{
for (int j = 0; j < days.Length; j++)
{
var result =pickMRandomly();
System.Console.WriteLine(days[j]);
foreach (var i in result)
{
System.Console.WriteLine(i + " ");
}
System.Console.WriteLine("/n");
}
}
}
If I understand you correctly, you don't just want a random arrangement of songs for each day, you want a unique (and random) arrangement of songs each day.
The only way that I can think of to guarantee this is to work out all of the possible combinations of songs and to randomly sort them - then to pick out a different combination from the list for each day.
using System;
using System.Collections.Generic;
using System.Linq;
namespace StackOverflowAnswer
{
class Program
{
static string[] songs = new string[] { "song1", "song2", "song3" };
static string[] days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
static void Main(string[] args)
{
var rnd = new Random();
var allCombinationsInRandomOrder = GetCombinations(songs, songs.Length)
.Select(combination => new { Combination = combination, Order = rnd.Next() })
.OrderBy(entry => entry.Order)
.Select(entry => entry.Combination);
var dayIndex = 0;
foreach (var combination in allCombinationsInRandomOrder)
{
var day = days[dayIndex];
Console.WriteLine(day);
Console.WriteLine(string.Join(", ", combination));
dayIndex++;
if (dayIndex >= days.Length)
break;
}
Console.ReadLine();
}
private static IEnumerable<IEnumerable<string>> GetCombinations(IEnumerable<string> songs, int numberOfSongsInGeneratedLists)
{
if (songs == null)
throw new ArgumentNullException(nameof(songs));
if (numberOfSongsInGeneratedLists <= 0)
throw new ArgumentOutOfRangeException(nameof(numberOfSongsInGeneratedLists));
if (numberOfSongsInGeneratedLists > songs.Count())
throw new ArgumentOutOfRangeException("can't ask for more songs in the returned combinations that are provided", nameof(numberOfSongsInGeneratedLists));
if (numberOfSongsInGeneratedLists == 1)
{
foreach (var song in songs)
yield return new[] { song };
yield break;
}
foreach (var combinationWithOneSongTooFew in GetCombinations(songs, numberOfSongsInGeneratedLists - 1))
{
foreach (var song in songs.Where(song => !combinationWithOneSongTooFew.Contains(song)))
yield return combinationWithOneSongTooFew.Concat(new[] { song });
}
}
}
}
From what I understand, you want to create a random playlist and if this playlist has been created before, you want to generate another (until it's unique). One way you could do this is to add a hash of some sort to a HashSet and see if it's previously been generated. For example,
bool HashSet<int> playlistHashes = new HashSet<int>();
private bool CheckIfUnique(string[] playlist)
{
//HashSet returns false if the hash already exists
//(i.e. playlist already likely to have been created)
return playlistHashes.Add(string.Join("",playlist).GetHashCode());
}
Then once you've generated your playlist, you can call that method and see if it returns false. If it returns false, that playlist order has been created before and so you can generate again. Using the technique above means that song1, song2, song3 is different from song3, song2, song1 so the order is important.
As mentioned in my comment on the question, if you're testing with 3 songs, there are only 6 different permutations and 7 days of the week so you're going to get a duplicate.
Side note, GetHashCode can throw 'false-positives' but it's up to you to determine how likely it is, and if the impact is actually of any significance since a new playlist is generated anyway. Good thread for more information here. There are numerous hashing techniques possible with lower collision chances if GetHashCode would not suffice here.
Consider that you have 3 Songs in hand and want to assign unique Combination for each day of a week( 7days). It is not possible since you can made only six unique combinations with these three. So definitely there may be one repeating sequence. You will get 24 unique song sequence if you add another song(let it be "song4") to this collection. I have included a snippet that help you to get these combination of unique sequence of songs.
string[] songs = new string[] { "song1", "song2", "song3", "song4" };
int numberOfSongs = songs.Count();
var collection = songs.Select(x => x.ToString()); ;
for (int i = 1; i < numberOfSongs; i++)
{
collection = collection.SelectMany(x => songs, (x, y) => x + "," + y);
}
List<string> SongCollections = new List<string>();
SongCollections.AddRange(collection.Where(x => x.Split(',')
.Distinct()
.Count() == numberOfSongs)
.ToList());
Now the SongCollections will contains 24 unique sequence of 4 songs. (if you choose 3 songs then you will get 6 unique sequences). You can apply Random selection of sequence from these collection and assign to days as you wish.
Now Let me use a Dictionary<string, int> dayCollectionMap to map a collection to a day(Note : Here i use a collection of 4 songs since 3 is not enough for 7 days). Consider the snippet below:
Dictionary<string, int> dayCollectionMap = new Dictionary<string, int>();
string[] days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
Random randomCollection = new Random();
foreach (string day in days)
{
int currentRandom = randomCollection.Next(0, SongCollections.Count());
if (!dayCollectionMap.Any(x => x.Value == currentRandom))
{
dayCollectionMap.Add(day, currentRandom);
}
else
{
// The collection is already taken/ Add another random sequence
while (true)
{
currentRandom = randomCollection.Next(0, SongCollections.Count());
if (!dayCollectionMap.Any(x => x.Value == currentRandom))
{
dayCollectionMap.Add(day, currentRandom);
break;
}
}
}
}
So that you can select the song collection for Wednesday by using the code
var songCollectionForWed = SongCollections[dayCollectionMap["Wednesday"]];
I have an array of colors.
Black[0]
White[1]
Blue[2]
Green[3]
Red[4]
Purple[5]
Orange[6]
Pink[7]
Silver[8]
There is a for loop that iterates for the count of the array of colors and compares to a string that is passed in. In this case it is a single color in the string.
private ushort? FindColor(SomeObject colorArray, string name)
{
for (ushort i = 0; i < colorArray.Count; ++i)
{
SomeObject someObject = colorArray[i];
try
{
if (someObject.Name == name)
return i;
}
}
return null;
}
If the string name matches the color at [i] then it returns the array number where found.
What needs to happen is the name is going to be a comma separated string of colors. So it could be Red,Purple.
What I'd like to do is go through the colorArray and find out if each of the split string colors are found in the array beside each other.
So in this case Red is found at 4 and Purple is found at 5. Since they are beside each other I'd like to return 4. Otherwise, if the 2 colors are not found beside each other then just return null.
private List<string> GetColors(string colorName)
{
if (colorName == null)
return new List<string>();
string[] parts = colorName.Split(',');
return parts.Select(p => p.Trim()).ToList();
}
private ushort? FindColor(SomeObject colorArray, string name)
{
var colors = GetColors(name);
for (ushort i = 0; i < colorArray.Count; ++i)
{
SomeObject someObject = colorArray[i];
try
{
????
for(int j = 0; j < colors.Count; j++)
{
if (someObject.Name == colors[j])
{
// found the first color of Red at [4]
// store in a temp variable ????
// back into the loop and found Purple at [5]
// Purple at [5] was found to be beside Red at [4] so return [4]
return i; // i in this case would be 4
// if the colors are not found beside each other then
return null;
}
}
}
}
return null;
}
Can anyone recommend the best method to check for a case like this?
I think this might work for you
private void GetColors(string colors)
{
string[] colorArray = new string[] { "red", "green", "purple" };
int previousIndex = -1;
int currentIndex;
string[] myColors = colors.Split(',');
foreach (string s in myColors)
{
currentIndex = Array.IndexOf(colorArray, s);
if (previousIndex != -1)
{
if (previousIndex - currentIndex == 1 || previousIndex - currentIndex == -1)
{
//do stuff here
}
}
previousIndex = currentIndex;
}
}
In C#, returning a list of all matching indexes:
private IEnumerable<int> FindColor(List<System.Drawing.Color> colorArray, string name)
{
var colors = GetColors(name);
var colorStrings = colorArray.Select(y => y.Name.ToString()).ToList();
return colors.Select(x => colorStrings.IndexOf(x));
}
Usecase:
FindColor(ColorList, "White,Black,Pink,Polkadot");
// Returns { 1, 0, 7, -1 }
You could make it a bit more efficient, probably, by returning a list of System.Drawing.Color from GetColors() instead of a list of strings, but I don't think it'd make a big difference.
Also, if you just wanted what you originally asked for - to return the first index of a matching sequence, you can just sort what's currently in the return statement, then spin over the list to check for gaps. If you find a gap, return null, otherwise, return the first element.
I need to generate random color names e.g. "Red", "White" etc. How can I do it? I am able to generate random color like this:
Random randonGen = new Random();
Color randomColor = Color.FromArgb(randonGen.Next(255), randonGen.Next(255),
randonGen.Next(255));
but I need the names and not all colors generated like this have a known name.
Thanks
Use Enum.GetValue to retrieve the values of the KnownColor enumeration and get a random value:
Random randomGen = new Random();
KnownColor[] names = (KnownColor[]) Enum.GetValues(typeof(KnownColor));
KnownColor randomColorName = names[randomGen.Next(names.Length)];
Color randomColor = Color.FromKnownColor(randomColorName);
Take a random value and get from KnownColor enum.
May be by this way:
System.Array colorsArray = Enum.GetValues(typeof(KnownColor));
KnownColor[] allColors = new KnownColor[colorsArray.Length];
Array.Copy(colorsArray, allColors, colorsArray.Length);
// get a randon position from the allColors and print its name.
Ignore the fact that you're after colors - you really just want a list of possible values, and then take a random value from that list.
The only tricky bit then is working out which set of colors you're after. As Pih mentioned, there's KnownColor - or you could find out all the public static properties of type Color within the Color structure, and get their names. It depends on what you're trying to do.
Note that randomness itself can be a little bit awkward - if you're selecting multiple random colors, you probably want to use a single instance of Random`. Unfortunately it's not thread-safe, which makes things potentially even more complicated. See my article on randomness for more information.
Sounds like you just need a random color from the KnownColor enumeration.
Or you could try out this: For .NET 4.5
public Windows.UI.Color GetRandomColor()
{
Random randonGen = new Random();
Windows.UI.Color randomColor =
Windows.UI.Color.FromArgb(
(byte)randonGen.Next(255),
(byte)randonGen.Next(255),
(byte)randonGen.Next(255),
(byte)randonGen.Next(255));
return randomColor;
}
Copied code from Retrieve a list of colors in C#
CODE:
private List<string> GetColors()
{
//create a generic list of strings
List<string> colors = new List<string>();
//get the color names from the Known color enum
string[] colorNames = Enum.GetNames(typeof(KnownColor));
//iterate thru each string in the colorNames array
foreach (string colorName in colorNames)
{
//cast the colorName into a KnownColor
KnownColor knownColor = (KnownColor)Enum.Parse(typeof(KnownColor), colorName);
//check if the knownColor variable is a System color
if (knownColor > KnownColor.Transparent)
{
//add it to our list
colors.Add(colorName);
}
}
//return the color list
return colors;
}
Put the colors into an array and then choose a random index:
class RandomColorSelector
{
static readonly Color[] Colors =
typeof(Color).GetProperties(BindingFlags.Public | BindingFlags.Static)
.Select(propInfo => propInfo.GetValue(null, null))
.Cast<Color>()
.ToArray();
static readonly string[] ColorNames =
typeof(Color).GetProperties(BindingFlags.Public | BindingFlags.Static)
.Select(propInfo => propInfo.Name)
.ToArray();
private Random rand = new Random();
static void Main(string[] args)
{
var colorSelector = new RandomColorSelector();
var color = colorSelector.GetRandomColor();
// in case you are only after the *name*
var colorName = colorSelector.GetRandomColorName();
}
public Color GetRandomColor()
{
return Colors[rand.Next(0, Colors.Length)];
}
public string GetRandomColorName()
{
return ColorNames[rand.Next(0, Colors.Length)];
}
}
Note that the sample above simply looks up all static properties of the Color type. You might want to improve this by checking that the actual return type of the property is a Color.
I would build a lookup table. Especially since some colors are up to personal interpretation.
Go through each color value in the Color struct ( http://msdn.microsoft.com/en-us/library/system.drawing.color.aspx ) and map it to the RGB values. Then to convert back, lookup the RGB value to see if it has a named color.
I combined some of the answers in this thread and came up with more generic solution for generating a random KnownColor:
public class Enum<T> where T : struct, Enum
{
public static T Random
{
get => GetRandom();
}
public static T SeededRandom(int seed)
{
return GetRandom(seed);
}
private static T GetRandom(int? seed = null)
{
var enumValues = Enum.GetValues<T>();
Random r;
if(seed.HasValue)
{
r = new Random(seed.Value);
}
else
{
r = new Random();
}
var randomIndex = r.Next(enumValues.Length - 1);
return enumValues[randomIndex];
}
}
Then you can use this as:
public Color GetRandomColor()
{
var randomKnownColor = Enum<KnownColor>.Random;
return Color.FromKnownColor(randomKnownColor);
}
You can also use the SeededRandom(int seed) method which was more suited for my use-case. Where I wanted to assign a random color based on some Id and get the same color for the same Id when the process is repeated.
Works on C# >= 7.3
There is no way to Randomize an Enumeration, as you want to do, the most suitable solution would pass by setting a List with all the values of the colors, then obtain an integer randomizing it and use it as the index of the list.
private string getRandColor()
{
Random rnd = new Random();
string hexOutput = String.Format("{0:X}", rnd.Next(0, 0xFFFFFF));
while (hexOutput.Length < 6)
hexOutput = "0" + hexOutput;
return "#" + hexOutput;
}
Here, I'm generating colors based on profile completed.
public string generateColour(Decimal percentProfileComplete )
{
if(percent < 50)
{
return "#" + (0xff0000 | Convert.ToInt32(Convert.ToDouble(percentProfileComplete ) * 5.1) * 256).ToString("X6");
}
return "#" + (0xff00 | (255 - Convert.ToInt32((Convert.ToDouble(percentProfileComplete ) - 50) * 5.1)) * 256 * 256).ToString("X6");
}
I would have commented on Pih's answer; however, not enough karma. Anywho, I tried implementing this and ran into the issue of the same color being generated from multiple calls as the code was called repeatedly in quick succession (i.e. the randomGen was the same and since it is based on the clock = same results ensued).
Try this instead:
public class cExample
{
...
Random randomGen = new Random();
KnownColor[] names = (KnownColor[]) Enum.GetValues(typeof(KnownColor));
...
private Color get_random_color()
{
KnownColor randomColorName = names[randomGen.Next(names.Length)];
return Color.FromKnownColor(randomColorName);
}
...
}
To clear up the syntax errors in the original question, it's
Color.FromRgb, and
(Byte)random2.Next(255)
converts the integer to a byte value needed by Color:
Random random2 = new Random();
public int nn = 0x128;
public int ff = 0x512;
Color randomColor = Color.FromRgb((Byte)random2.Next(nn, ff), (Byte)random2.Next(nn, ff), (Byte)random2.Next(nn, ff));
If you wanted to get random color from specific colors list,
then try below one
Color[] colors = new[] { Color.Red, Color.Blue, Color.FromArgb(128, 128, 255) }; //Add your favorite colors list
Random randonGen = new Random();
Color randomColor = colors[Generator.Next(0, colors.Length)]; //It will choose random color from "colors" array
generate a random number and cast it to KnownColor Type
((KnownColor)Random.Next());