This question already has answers here:
Random number generator only generating one random number
(15 answers)
Closed 8 years ago.
I m trying to fill a Canvas with some random shapes( Ellipse and Rectangle). I need to pick a random color to fill the shapes with it. The problem that I have always the same random color for all the shape.
When I debug my code I get random colors. Below is my Code :
public MainWindow()
{
InitializeComponent();
this.Loaded += delegate { InitializeSourceCanvas(); };
}
private void InitializeSourceCanvas()
{
var rnd = new Random();
const int height = 30, width = 30;
for (int i = 0; i < 25; i++)
{
var shape = rnd.Next(10) > 4 ? (Shape)new Ellipse() : (Shape)new Rectangle();
shape.Width = height;
shape.Height = width;
shape.Stroke = new SolidColorBrush(Colors.Black);
shape.StrokeThickness = 1;
shape.Fill = PickRandomBrush();
Canvas.SetLeft(shape, rnd.NextDouble() * (_source.ActualWidth - width));
Canvas.SetTop(shape, rnd.NextDouble() * (_source.ActualHeight - height));
_source.Children.Add(shape);
}
}
private Brush PickRandomBrush()
{
Brush result = Brushes.Transparent;
Random rnd = new Random();
Type brushesType = typeof(Brushes);
PropertyInfo[] properties = brushesType.GetProperties();
int random = rnd.Next(properties.Length);
result = (Brush)properties[random].GetValue(null, null);
return result;
}
Like Nuke suggests, new your Random object outside the for loop so it's only instantiated once, and then pass the same one in each time.
Random rnd = new Random();
for (int i = 0; i < 25; i++)
{
...
shape.Fill = PickRandomBrush(rnd);
...
}
Then edit your PickRandomBrush method to look like,
private Brush PickRandomBrush(Random rnd)
{
Brush result = Brushes.Transparent;
Type brushesType = typeof(Brushes);
PropertyInfo[] properties = brushesType.GetProperties();
int random = rnd.Next(properties.Length);
result = (Brush)properties[random].GetValue(null, null);
return result;
}
Edit:
k, sraboy made a good point about running this code in quick succession--if called fast enough this will have the same seed. Here's a solution that's not elegant, but reasonably guaranteed to be unique:
Random rnd = new Random(Guid.NewGuid().ToString().GetHashCode());
(this would replace the first line)
From MSDN:
[..] different Random objects that are created in close succession by a call to the default constructor will have identical default seed values and, therefore, will produce identical sets of random numbers. This problem can be avoided by using a single Random object to generate all random numbers.
The Random is because you are not binding the function to a timer, meaning it has the same conditions each time it is created.
One solution would be to tie the random to the Systemtime.Now
Another solution would be to use the same Random instead of creating a new one.
Related
I got stuck when i want to display my objects in a random way .
Let's say i got this pannel and let's say the rectangle and ellipse are the objects
What do i need to do so i can have them display in this form.
Do i need to use Random n = new Random ? or there is another way.
This is one of my attemps but i dont know how to integrate the random function to display them.
do
{
dr.DrawEllipse(Bpen, i + 30, 25, i + 30, 25);
i += 50;
}
while (i <= this.Width);
You can use an array of available items like { squareType, circleType }.
And then random 0..Length on that array to select and draw the relevant shape.
For example you can use this enum:
public enum Shapes
{
Circle,
Square
}
And declare these members:
static readonly Array ShapesValues = Enum.GetValues(typeof(Shapes));
static readonly int ShapesCount = ShapesValues.Length;
And this RNG:
static readonly Random random = new Random();
So you can write:
int posX = 0;
do
{
switch ( ShapesValues.GetValue(random.Next(ShapesCount)) )
{
case Shapes.Circle:
// ...
break;
case Shapes.Square:
// ...
break;
default:
throw new NotImplementedException();
}
posX += 50;
}
while ( posX <= Width );
By doing this, you can define and manage any necessary shape in a clean and maintainable way.
If the shapes can have different sizes, consider handling this with intermediate variables.
You could also write this to make the code more secure:
static readonly ReadOnlyCollection<Shapes> ShapesValues
= Array.AsReadOnly(Enum.GetValues(typeof(Shapes)).Cast<Shapes>().ToArray());
static readonly int ShapesCount = ShapesValues.Count;
And:
switch ( ShapesValues[random.Next(ShapesCount)] )
My program creates 5 different labels with a cube form and they just drop down. When I press on them, they come invisible. I want to check if all of them are invisible, but don't know how to do so. Tried going through this site, found a solution with bool, but it just doesn't work my way. Also when my labels appear,you can see only 4 of them.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Label [] kubeliai = new Label [5];
int poz = 100;
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < kubeliai.Length; i++)
{
kubeliai[i] = new Label();
Controls.Add(kubeliai[i]);
Random pos = new Random();
kubeliai[i].Top = 50;
kubeliai[i].Left = poz;
poz += pos.Next(50, 200);
kubeliai[i].BackColor = Color.Red;
kubeliai[i].Height = 20;
kubeliai[i].Width = 20;
kubeliai[i].Click += new EventHandler(kubelio_clickas);
}
Timer kritimo_laikrodis = new Timer();
kritimo_laikrodis.Interval = 10;
kritimo_laikrodis.Tick += new EventHandler(laikrodis);
kritimo_laikrodis.Enabled = true;
}
void kubelio_clickas (object sender, EventArgs e)
{
((Label)sender).Visible = false;
}
void laikrodis (object sender, EventArgs e)
{
for (int i = 0; i < kubeliai.Length; i++)
{
kubeliai[i].Top += 1;
if (kubeliai.All.Visible == false) // this is an error
{
kubeliai[i].Visible = true;
kubeliai[i].Top = 50;
Random pos = new Random();
poz += pos.Next(50, 200);
}
}
}
Using Linq you can check if all are invisible in this way
var areAllInvisible = kubeliai.All(l => l.Visible == false);
if (areAllInvisible)
{
// do something
}
when my labels appear you can see only 4 of them.
That's because the way you are picking random numbers is picking the same numbers each time and you are therefore placing your labels on top of each other. Read the first paragraph of the Random() documentation:
Different Random objects that are created in close succession by a call to the default constructor will have identical default seed values and, therefore, will produce identical sets of random numbers. This problem can be avoided by using a single Random object to generate all random numbers.
Use new Random() once in your class definition like this:
Label [] kubeliai = new Label [5];
Random pos = new Random();
And remove it everywhere else in your program.
This question already has answers here:
Random number generator only generating one random number
(15 answers)
Closed 7 years ago.
I am trying to create a dynamically added array of user-controls where each one will have a random color assigned to it to make the user more able to differentiate it from others, but when I do that it produces pattern of colors. It will create 10 of the user-controls with the same color then it will change the color for the next 10, I want each separate one to have a different color.
The code for the user-control:
public partial class EquationBox : UserControl
{
public EquationBox()
{
InitializeComponent();
this.panel4.BackColor = RandomColor();
}
private void button1_Click(object sender, EventArgs e)
{
this.Visible = false;
this.textBox1.Text = "";
}
private Color RandomColor()
{
Random rnd = new Random();
/*KnownColor[] names = (KnownColor[])Enum.GetValues(typeof(KnownColor));
KnownColor randomColorName = names[r.Next(names.Length)];
Color randomColor = Color.FromKnownColor(randomColorName);
return randomColor;*/
Color randomColor = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256));
return randomColor;
}
}
The Code for form1:
public partial class Form1 : Form
{
public static EquationBox[] EquationBoxArray = new EquationBox[100];
public Form1()
{
InitializeComponent();
for (int x = 0; x < 100; x++)
{
EquationBoxArray[x] = new EquationBox();
EquationBoxArray[x].Parent = flowLayoutPanel1;
EquationBoxArray[x].Visible = false;
}
}
private void add_line_Click(object sender, EventArgs e) //Add Line
{
for(int x = 0; x < 100; x++)
{
if(!EquationBoxArray[x].Visible)
{
EquationBoxArray[x].Visible = true;
EquationBoxArray[x].Refresh();
break;
}
}
}
private void clear_Click(object sender, EventArgs e) //Clear Lines
{
for (int x = 0; x < 100; x++)
{
EquationBoxArray[x].Visible = false;
EquationBoxArray[x].ResetText();
}
}
private void Form1_SizeChanged(object sender, EventArgs e) //Window Size Changed
{
}
}
Thanks in advance, any help would be much appreciated!
The Random class is only a pseudo-random number generator, controlled by the seed parameter in the constructor. To achieve a better distribution of random numbers, try putting the creation of the Random object outside of the loop, or seed it with a different value each time.
For example
public partial class EquationBox
{
private static Random rnd;
static EquationBox()
{
rnd = new Random();
}
public EquationBox()
{
this.panel4.BackColor = GetRandomColor();
}
private Color GetRandomColor()
{
Color randomColor = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256));
return randomColor;
}
}
You need to create global Random instance, or use another it constructor
https://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx
More the Random to a field, then use it in the RandomColor function, as below.
Random _rnd = new Random();
private Color RandomColor()
{
Color randomColor = Color.FromArgb(_rnd.Next(256), _rnd.Next(256), _rnd.Next(256));
return randomColor;
}
This fixes it because Random uses a seed to initialize the pseudorandom number generator, which is the number of milliseconds since the computer was started. Therefore, if you create more than one Random in the same millisecond, it will start with the same seed.
you should only use one instance of Random Class.The problem could be because you are creating new instance of Random every time you call RandomColor .
You can move it to another class
public class MyRandom
{
private static Random _randColor=new Random();
private Color GetRandomColor()
{
Color randomColor = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256));
return randomColor;
}
}
and in your user control call it using
public EquationBox()
{
InitializeComponent();
this.panel4.BackColor = MyRandom.GetRandomColor();
}
Your error comes from creating the Random Number Generator everytime you need a new random colour. The RNG is usually seeded with the current time and because you are creating new ones very quickly the get the same seed.
public partial class EquationBox
{
static Random rnd = new Random();
public EquationBox()
{
InitializeComponent();
lock (rnd)
{
this.panel4.BackColor = Color.FromArgb(rnd.Next(257), rnd.Next(257), rnd.Next(257));
}
}
}
Random.Next is not thread safe, so I have a lock around it. Also it returns a value LESS than the max parameter. So you most likely want to pass 257 instead of 256.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I wrote a quick implementation of Conway's Game of Life, but it ran awfully slow mostly because my method of checking for neighbouring cells involved looping through the entire grid of cells again, now I've changed my method of checking for neighbouring cells but unfortunately it's not updating correctly anymore, it seems to work fine except that it doesn't create nearly as many new cells as it should.
Now I have spent a good few hours manually debugging the code, going through it with breakpoints and trying to compare the values and calls, but it SEEMS as if my GetNeighbours() method is working, so I concede to you, I can't figure out what's wrong on my own. I've submitted the code below for help.
EDIT: Some of you have pointed out that I can't copy my Grid.cells array the way I am doing it. I've changed it to use Array.Copy() instead but unfortunately it still doesn't work completely. I can't figure it out but it still doesn't seem to create new cells in all cases where it should.
MainForm.cs
public partial class MainFom : Form
{
Grid formGrid;
CancellationTokenSource tokenSrc = new CancellationTokenSource();
public MainFom()
{
InitializeComponent();
}
private void MainFom_Load(object sender, EventArgs e)
{
formGrid = new Grid();
for (int i = 0; i < 50; i++)
{
int xCoord = 10 * i + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = i.ToString(),
Location = new Point(xCoord, 0),
Font = new Font(Font.FontFamily, 6)
});
for (int s = 0; s < 50; s++)
{
int yCoord = 10 * s + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = s.ToString(),
Location = new Point(0, yCoord),
Font = new Font(Font.FontFamily, 6)
});
}
}
}
private void MainFom_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(formGrid.toBitmap(), 0, 0);
e.Graphics.Dispose();
}
private void startBtn_Click(object sender, EventArgs e)
{
Task tempTask = Task.Factory.StartNew(
(x) =>
{
while (!tokenSrc.IsCancellationRequested)
{
formGrid.UpdateGrid();
Graphics graphics = this.CreateGraphics();
graphics.Clear(this.BackColor);
graphics.DrawImage(formGrid.toBitmap(), 0, 0);
graphics.Dispose();
}
}, tokenSrc);
startBtn.Hide();
Button stopBtn = new Button() { Text = "Stop", Location = startBtn.Location, Size = startBtn.Size };
this.Controls.Add(stopBtn);
stopBtn.Click += new EventHandler(
(x, y) =>
{
tokenSrc.Cancel();
stopBtn.Hide();
startBtn.Show();
tempTask.Wait();
tokenSrc = new CancellationTokenSource();
});
}
}
Grid.cs
class Grid
{
#region Properties/Fields
const int MAX_CELLS = 50;
Random RNG = new Random();
Cell[,] cells;
int generations = new int();
#endregion
public Grid()
{
cells = new Cell[MAX_CELLS, MAX_CELLS];
for (int x = 0; x < MAX_CELLS; x++)
{
int xCoord = 10 * x + 12;
for (int y = 0; y < MAX_CELLS; y++)
{
int yCoord = 10 * y + 12;
Point point = new Point(xCoord, yCoord);
if (RNG.Next(100) < 20) {
cells[x, y] = new Cell(point, true); }
else {
cells[x, y] = new Cell(point, false);
}
}
}
}
public void UpdateGrid()
{
Cell[,] copy = cells;
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
int neighboursCtr = GetNeighbours(x, y);
//Rule 1: Any live cell with fewer than two live neighbours dies, as if caused by under-population.
if (cells[x, y].IsAlive && neighboursCtr < 2)
{
copy[x, y].Kill();
}
//Rule 2: Any live cell with more than three live neighbours dies, as if by overcrowding.
if (cells[x, y].IsAlive && neighboursCtr > 3)
{
copy[x, y].Kill();
}
//Rule 3: Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if (!cells[x, y].IsAlive && neighboursCtr == 3)
{
copy[x, y].Alive();
}
}
}
cells = copy;
generations++;
}
public Bitmap toBitmap()
{
Bitmap gridBmp = new Bitmap(1000, 1000); // TODO: Find optimal size for bmp
Size cellSize = new Size(10, 10);
using (Graphics gfxObj = Graphics.FromImage(gridBmp))
{
// Draw grid here and Dispose() on Pen, gfxObj is implicitly disposed
Pen myPen = new Pen(Color.LightGray);
SolidBrush myBrush = new SolidBrush(Color.Black);
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
if (!cells[x, y].IsAlive)
{
gfxObj.DrawRectangle(myPen, new Rectangle(cells[x, y].point, cellSize));
} else
{
gfxObj.FillRectangle(myBrush, new Rectangle(cells[x, y].point, cellSize));
}
}
}
myPen.Dispose();
myBrush.Dispose();
}
return gridBmp;
}
private int GetNeighbours(int column, int row)
{
int neighbours = new int();
int[] starts = new int[] { Math.Max(0 ,column - 1), Math.Max(0, row - 1) };
int[] ends = new int[] { Math.Min(49, column + 1), Math.Min(49, row + 1) };
double colAndRow = column + row/10;
for (int x = starts[0]; x < ends[0]+1; x++)
{
for (int y = starts[1]; y < ends[1]+1; y++)
{
double xAndY = x + y/10;
if (cells[x, y].IsAlive && xAndY != colAndRow)
{
neighbours++;
}
}
}
return neighbours;
}
}
Cell.cs
struct Cell
{
public bool IsAlive { get; private set; }
public readonly Point point;
public Cell(Point point, bool isAlive) : this()
{
this.point = point;
IsAlive = isAlive;
}
public void Alive()
{
IsAlive = true;
}
public void Kill()
{
IsAlive = false;
}
}
The problem is in your UpdateGrid() method. You're simply assigning the reference for your original array to a new variable:
Cell[,] copy = cells;
But this is still the same object; in particular, there's no difference between calling copy[x, y].Kill() and cells[x, y].Kill(). So you're modifying your state during calculations, this affects your code's logic.
Make a copy of the original using Array.Copy and it should work correctly (there doesn't seem to be anything else wrong with your algorithm).
Arrays are reference types, which means
Cell[,] copy = cells;
doesn't what you probably intent to do. It's not a copy of the source array, so it will manipulate this whilst analyzing the neighbors which will lead to wrong results.
Use Array.Copy.
There are a lot of improvements that can be done.
Take a look at Optimizing Conway's 'Game of Life' and Hashlife
You can start by using LockBits to work faster with the bitmap.
You can use parallel programming to improve the loops: Save time with parallel FOR loop
You can also improve the algorithm avoiding the whole matrix scan each time, and instead maintain a list of the alive cells, and only step thru these cells and it's neighbors.
I've implemented such algorithm in the following C# Game of Life Code.
I have a interface
interface Dot
{
// protected Random r = new Random();
void createdot(Rectangle clientrectangle, Control.ControlCollection Controls);
}
and I use this interface as a base class for my derived classes as stated
public class BlueDot : Dot
{
public List<Label> bluedot = new List<Label>();
Random r = new Random();
public void createdot(Rectangle ClientRectangle, Control.ControlCollection Controls)
{
for (int i = 0; i < 5; i++)
{
var temp = new Label();
temp.Location = new Point(r.Next(ClientRectangle.Right - 10), r.Next(ClientRectangle.Bottom - 20));
temp.Text = "?";
temp.Width = 10;
temp.Height = 10;
temp.ForeColor = System.Drawing.Color.Blue;
temp.BackColor = System.Drawing.Color.White;
Controls.Add(temp);
temp.Visible = true;
temp.Show();
bluedot.Add(temp);
}
}
}
and
public class RedDot:Dot
{
public List<Label> reddot = new List<Label>();
Random r = new Random();
public void createdot(Rectangle Clientrectangle,Control.ControlCollection Controls)
{
for (int z = 0; z < 10; z++)
{
var temp2 = new Label();
temp2.Location = new Point(r.Next(Clientrectangle.Right - 10), r.Next(Clientrectangle.Bottom - 20));
temp2.Text = "?";
temp2.Width = 10;
temp2.Height = 10;
temp2.ForeColor = System.Drawing.Color.Red;
temp2.BackColor = System.Drawing.Color.White;
Controls.Add(temp2);
temp2.Show();
reddot.Add(temp2);
}
}
and they are called here
BlueDot bdot = new BlueDot();
RedDot rdot = new RedDot();
private void Form1_Load(object sender, EventArgs e)
{
this.Activate();
bdot.createdot(this.ClientRectangle,this.Controls);
rdot.createdot(this.ClientRectangle, this.Controls);
}
Why is it that I keep getting only 5 red dots even if the loop performs 10 iterations?
here is the sample output https://www.facebook.com/photo.php?fbid=2372861522202&set=a.1600508493859.85328.1270463960&type=1 , i just cant figure out what happened to the other 5 red dots, it should be 10 red dots....
There is no inheritance problem here. The problem is the random number generator.
Try this line in your code:
temp2.Location = new Point(10 * z, 10 * z);
Replacing
temp2.Location = new Point(r.Next(Clientrectangle.Right - 10), r.Next(Clientrectangle.Bottom - 20));
You'll see your 5 blue "?"-labels and your 10 red "?"-labels
To solve the problem of the weak random number generator try seeding your random number generator. Example:
Random r = new Random((int)DateTime.Now.Ticks);
I am not that sure, but why aren't you making your temp2 visible ????
temp2.Visible=true
If this dowsn't work can you provide a screen shot of both your output. Sometimes due to the size of the window one dot may reside over another. I mean they actually overlap. Seeing your output may help to sort out your problem