How to delete old curve in ZedGraph C# - c#

I have this method:
private void plotGraph(List<float> data)
{
GraphPane myPane = zedGraphControl1.GraphPane;
// Set the Titles
myPane.Title.Text = "Symulacja";
myPane.XAxis.Title.Text = "Czas";
myPane.YAxis.Title.Text = "Wartość sygnału";
myPane.XAxis.Scale.Max = 20;
myPane.YAxis.Scale.Max = 5;
myPane.YAxis.Scale.Min = -5;
PointPairList PairList = new PointPairList();
double x = 0;
for (int i = 0; i <= 1000; i++)
{
PairList.Add(x, data[i]);
x += 0.01;
}
LineItem ACurve = myPane.AddCurve("Team A", PairList, Color.Red, SymbolType.None);
zedGraphControl1.Refresh();
zedGraphControl1.AxisChange();
}
And the first time I call it everything is ok, the function plots the plot i want (the Y values of the points are from the list data). Now the second and every other time i call it, there is a new line drawn over the first one (the old one stays on the chart). I would like the old one to dissapear when a new one is plotted, what should I do to get this effect?

You have to remove the old curve first. If all you'll ever have is one curve, you would remove the curve with the label "Team A", then add the PairList to a new instance. However, if in the future you want multiple curves simultaneously, you'll need to pass in the label as a parameter to your method.
To simply remove a lone curve only each time, add the following code before the call to AddCurve. An index of -1 indicates the CurveList element does not exist.
int curveIndex = myPane.CurveList.IndexOfTag("Team A");
if (curveIndex != -1)
{
myPane.CurveList.RemoveAt(curveIndex);
}
You can remove/add any number of curves, but you have to have a tag to identify the curve that you wish to change. The tag variable will replace the "Team A" above.

Related

Battleships game, replace grid with character issues

I'm currently trying to create a battleships console application. I've very new to c# but I'm almost there now there's just issue.
What I'm trying to do is replace a string with H or M when a ship is hit the works fine the only issue I have is when the H or M is inserted it doesn't replace the character in its place and it just moves the characters alone, for example if I have 5 characters in a row it like so: 0 0 0 0 0 it would insert the H or M and show: M0 0 0 0 0
I've been trying all sorts to fix this but as I said my knowledge is very limited.
Ints and Grid creation:
int gridSize = 10;
int BattleShip = 5;
int Destroyer1 = 4;
int Destroyer2 = 4;
int Row;
int Column;
char[,] Vgrid = new char [gridSize, gridSize];
This generates the grid:
for (Row = 0; Row < gridSize; Row++)
{
Console.WriteLine();
Console.Write("{0} | ", GridNumber++);
for (Column = 0; Column < gridSize; Column++)
Console.Write(Vgrid[Column, Row] + "~ ");
}
This code controls the H or M replacing:
if (grid[temp, temp1] == Destroyer1 || grid[temp, temp1] == Destroyer2 || grid[temp, temp1] == BattleShip)
{
Console.WriteLine("HIT!");
Hit++;
Vgrid[temp, temp1] = 'H';
}
else
{
Console.WriteLine("MISS!");
Miss++;
Vgrid[temp, temp1] = 'M';
}
Is there a way I can do this? I just want to be able to replace the character in the grid with the H or M characters.
This grid is actually an overlay as the actual grid is an int and that is where the ships are plotted, this is the only way I thought I can use letters to signify a hit instead of numbers and keep the ships hidden to the players.
Attempting to alter the output of the Console window directly isn't necessarily the best approach for what you're wanting to achieve. Ideally, we should control what we write to the window, rather than try and control the window directly.
A simpler idea to achieve your goal might be to store two grids, one for your ships and another one for your shots. This allows you to separate what you draw to the console window from what you use to check if the player has landed a shot easily, keeping your ships hidden.
Firstly, let's create our setup:
int gridSize = 10;
int BattleShip = 5;
int Destroyer1 = 4;
int Destroyer2 = 4;
int[,] shipGrid = new int[gridSize, gridSize];
char[,] shotGrid = new char[gridSize, gridSize];
The big change is the grids - one for the ships, and another for the shots. We use the same gridSize variable to make sure that the grids are exactly the same size. This is important to make sure that the two grids line up exactly.
Then, we'll set our default values for our shotGrid. We can do this with a simple method that just sets every value to the default, in this case '~':
public void CreateShotGridDefaultValues ()
{
for (int y = 0; y < shotGrid.GetLength(1); y++)
{
for (int x = 0; x < shotGrid.GetLength(0); x++)
{
shotGrid[x, y] = '~';
}
}
}
Next, to check if a player has landed a shot, we use the shipGrid to check if the player's selection relates to a ship, but we update the shotGrid with the information we want to draw to the console window:
public void CheckPlayerShot(int xCoordinate, int yCoordinate)
{
if (shipGrid[xCoordinate, yCoordinate] == Destroyer1 || shipGrid[xCoordinate, yCoordinate] == Destroyer2 || shipGrid[xCoordinate, yCoordinate] == BattleShip)
{
Console.WriteLine("HIT!");
Hit++;
shotGrid[xCoordinate, yCoordinate] = 'H';
}
else
{
Console.WriteLine("MISS!");
Miss++;
shotGrid[xCoordinate, yCoordinate] = 'M';
}
}
Then, we draw out the shotGrid to the console window as follows:
public void DrawGrid()
{
Console.WriteLine();
for (int y = 0; y < shotGrid.GetLength(1); y++)
{
string currentLine = $"{y + 1} | ";
for (int x = 0; x < shotGrid.GetLength(0); x++)
{
char shot = shotGrid[x, y];
currentLine += shot.ToString() + " ";
}
Console.WriteLine(currentLine);
}
Console.WriteLine();
}
This method is a little different to yours, so let me explain a little further. The idea for this method is to build up the information we want to write to the console window one line at a time, rather than draw out every character one at a time. This prevents us from needing to change the console window output directly.
To achieve this, we use two loops, just like you did. The second for() loop iterates through the row's grid cells, and adds them to the currentLine string. Once we've finished a row, we just write out that string to the console window all at once.
With all that in place, you just need call the DrawGrid() method whenever you want to update the grid in the console window. To better understand when and where the best time and place to update the window might be, requires a better understanding of Game Loops. This page should be a terrific start on that path..
Edit: Updated answer to reflect comments.
You could use
Console.SetCursorPosition(Int32, Int32) Method
Sets the position of the cursor.
Or you could just keep everything in a matrix and redraw the screen on change

Chart with unscaled y-values

I'm trying to figure out if I can find any exist chart in winforms C# application.
I have several integer inputs a, b, c... and at the end of processing I got results with wide range of values like 12, 342, 445879 etc.
On chart I want to see only hierarchy from higher size to lower without equivalent scale distance between higher and lower line ends. Just stairway by downward curve for each input.
So two things is needed, if some kind of such chart tool is already exist, is a sorting of sequence from higher to lower with names attached to the lines and non-scaled value display in the ratio between each other.
For example I have inputs:
int a = 12989;
int b = 324;
int c = 23;
int d = 12;
int e = 3;
and second process results:
int a = 4;
int b = 25;
int c = 1;
int d = 4;
int e = 14;
I have tried to use different graphs in list of VS,
private void Size(int a, int b)
{
this.chart1.Series["Series1"].Points.AddXY("a1", a);
this.chart1.Series["Series1"].Points.AddXY("a2", b);
}
but seems like I need different thing.
Here is desired results:
Yes, this is possible.
For x-values there is in fact a Series property IsXValueIndexed to enforce an unscaled display. There is no such thing for the y-values, though.
(The reason is probably that unscaled y-values are not really useful, as they will arguably mislead the user about the data..(..which may or may not be true of x-values; x-values containing, say, zip codes or other IDs should be displayed unscaled!))
To workaround you need to set the x and y-values to a count value and set the Labels of each DataPoint explicitly to the actual values you have.
Example:
// create an odered list of data:
List<double> data = new List<double>();
for (int i = 0; i < 12; i++) data.Add(rnd.Next(100) * rnd.Next(i * 5));
data = data.OrderByDescending(x => x).ToList();
// now add with dummy x and y-values:
Series s = chart1.Series[0];
for (int i = 0; i < data.Count; i++)
{
int p = s.Points.AddXY(i, data.Count - i); // dummy values
DataPoint dp = s.Points[p];
dp.Label = data[i] +""; // real value, formatted
dp.Tag= data[i]; // real value
}
To get rid of the axes, which can't show ay meanigful values, disable them:
chart1.ChartAreas[0].AxisX.Enabled = AxisEnabled.False;
chart1.ChartAreas[0].AxisY.Enabled = AxisEnabled.False;
More styling:
s.SetCustomProperty("PixelPointWidth","15");
s.Color = Color.DarkSlateGray;
Note the as the y-values are really just dummies, if you need to access them, you will have to use the Labels and convert them from string to number type. Also note that if they are double or floats those strings will either be terribly long or formatted to a short string. To preserve the actual values you can store these additionally in the Tag field of each DataPoint..

Which crossing over method is best to give us quick changes in best values for TSP in GA?

I am trying to solve Travelling Salesman Problem using Genetic Algorithym in C#. But in my app best values changes so slowly. I have tried with different Crossing-Over methods such as classic, greedy and pmx but I have never got what I want. What is the most effective reason that causes slow approximation to local minimum in Genetic Algorithyms? Isn't it Crossing-Over methods?
I think my method for CO is correct, isn't it?.
My code:
Tour ClassicCrossingOver(Tour mother, Tour father)
{
int pos = N / 2;
City[] gens = new City[N];
for (int i = 0; i < pos; i++)
{
gens[i] = mother.Cities[i];
}
List<int> nonPos = new List<int>(); //Handles duplicate city positions
for (int i = pos; i < gens.Length; i++)
{
if (gens.Contains(father.Cities[i]))
nonPos.Add(i);
gens[i] = father.Cities[i];
}
List<City> noneGenes = new List<City>(); //Handles cities that doesnt exists in the child
foreach (City gene in map.Cities)
{
if (gens.Contains(gene)) continue;
noneGenes.Add(gene);
}
for (int i = 0; i < noneGenes.Count; i++)
{
int j = rnd.Next(nonPos.Count - 1);
gens[nonPos[j]] = noneGenes[i];
nonPos.RemoveAt(j);
}
Tour tur = new Tour(map) { Cities = gens };
return tur;
}
A crossover often called 'Double Point Ordered' can be very useful here. This type of crossover creates a child from a single parent. Two parents are selected and two random points along the chromosome are selected. The genes between the points are passed to the child. The remaining genes are transferred from the same parent, but in the order that they appear in the second parent. The result is that the child contains all of the values from a single parent but includes ordering, and therefore traits, from both parents.
I have a couple of examples of the TSP here which may help
http://johnnewcombe.net/blog/gaf-part-4/
http://johnnewcombe.net/blog/gaf-part-7/
In my experience using GA, Ordered Crossover (OX1) is one of the best crossover operators to solve TSP.
OX1:
A randomly selected portion of one parent is mapped to a portion
of the other parent. From the replaced portion on, the rest is filled
up by the remaining genes, where already present genes are omitted and
the order is preserved.
Other operators can influence the speed that GA reach best values. I usually use the operators bellow in Traveling Salesman Problem:
Crossover: Ordered Crossover (OX1)
Mutation: Reverse Sequence Mutation
Selection: Elite Selection
This is a sample code to solve TSP, using GeneticSharp, that found a good solution in a few seconds:
var numberOfCities = 20;
var selection = new EliteSelection();
var crossover = new OrderedCrossover();
var mutation = new ReverseSequenceMutation();
var fitness = new TspFitness(numberOfCities, 0, 1000, 0, 1000);
var chromosome = new TspChromosme(numberOfCities);
var population = new Population (100, 200, chromosome);
var ga = new GeneticAlgorithm(population, fitness, selection, crossover, mutation);
ga.Termination = new GenerationNumberTermination(100);
Console.WriteLine("GA running...");
ga.Start();
Console.WriteLine("Best solution found has {0} fitness.", ga.BestChromosome.Fitness);
You can see TspChromosome implementation at TspChromosome.cs and TspFitness at TspFitness.cs.

Aligning range column data with x axis labels in chart control

I'm trying to do a range column plot of a set of agents' tasks using the Chart control in C# .NET. I plot agent number across the x axis and task time along the y axis. My only problem is that the column data will not align properly with the agent numbers on the x axis. Does anyone know how to align the columns with their corresponding x axis labels?
Here is an image of my graph:
Here is my code:
chartSchedule.Titles.Add("Agent / Task Schedule");
chartSchedule.ChartAreas[0].AxisX.Title = "Agent";
chartSchedule.ChartAreas[0].AxisY.Title = "Time";
int index = 0;
foreach ( Agent a in _agents )
{
// Create a series for each agent and set up display details
Series agentSeries = chartSchedule.Series.Add("Agent " + a.Id);
agentSeries.ChartType = SeriesChartType.RangeColumn;
// Alternate colours of series lines
if ( index % 2 > 0 )
agentSeries.Color = Color.DodgerBlue;
else
agentSeries.Color = Color.Blue;
// Display start and end columns of every task
List<DataPoint> timeData = new List<DataPoint>();
foreach ( NodeTask t in a.AssignedTasks )
{
agentSeries.Points.AddXY(index + 1, t.StartTime, t.EndTime);
}
index++;
}
The reason for the seeming 'misalignment' is that you are adding a total of five series but each has only one (set of) Datapoint(s) per X-Value.
This existent DataPoint is then combined wih the the four non-existent DataPoints and the five of them are displayed side by side as one block centered at the X-Values/Labels. This looks only right for the middle Series, which actually has the middle point.
You could add a few the other Points to see the effect..:
agentSeries.Points.AddXY(1, 1, 4);
agentSeries.Points.AddXY(2, 1, 2);
agentSeries.Points.AddXY(4, 1, 3);
So the most natural solution is to not add Series with missing data.
Not sure if you are happy with this solution or if there is a better way to do it, but the result looks not so bad..
I have done away with adding all those series and instead add all data to one and same Series.
To create the Legends I hide the regular one by setting its color to transparent. (It needs to be there.) Then I add new Legend CustomItems and give them the colors and names as you did.
Here is the code I used, except for the actual data, which I have simulated:
chartSchedule.Series.Clear();
ChartArea CA = chartSchedule.ChartAreas[0];
chartSchedule.Titles.Add("Agent / Task Schedule");
chartSchedule.ChartAreas[0].AxisX.Title = "Agent";
chartSchedule.ChartAreas[0].AxisY.Title = "Time";
// our only Series
Series agentSeries = chartSchedule.Series.Add(" " );
agentSeries.ChartType = SeriesChartType.RangeColumn;
agentSeries.Color = Color.Transparent; // hide the default series entry!
agentSeries["PixelPointWidth"] = "20"; // <- your choice of width!
int index = 0;
foreach (Agent a in _agents)
{
// Alternate colours
Color color = index % 2 == 0 ? Color.DodgerBlue : Color.Blue;
// Display start and end columns of every task
List<DataPoint> timeData = new List<DataPoint>(); ///???
foreach (NodeTask t in a.AssignedTasks)
{
int p = agentSeries.Points.AddXY(index +1, t.StartTime, t.EndTime);
agentSeries.Points[p].Color = color;
}
chartSchedule.Legends[0].CustomItems.Add(color, "Agent " + index);
index++;
}

Get Series value from a mouse click

I use Microsoft.DataVisualization.Charting and want to get the value of the point when i click on it.
My problem: i want exactly that value i clicked, even if its only a value calculated by the Chart and between 2 points.
Example: 3 points: P(0;3), P(1;6), P(3;12)
When i click at x-Value 2 i want to get 9 as result if the line is linear.
Currently i do that:
HitTestResult[] hits = chart.HitTest(e.X, e.Y, false, ChartElementType.PlottingArea);
//DataInformation save the DateTime and Value for later use
DataInformation[] dinfo = new DataInformation[hits.Length];
foreach (ChartArea area in chart.ChartAreas)
{
area.CursorX.LineWidth = 0; //clear old lines
}
for (int i = 0; i < hits.Length; i++) //for all hits
{
if (hits[i].ChartElementType == ChartElementType.PlottingArea)
{
//val saves the x-value clicked in the ChartArea
double val = hits[i].ChartArea.AxisX.PixelPositionToValue(e.X);
DataPoint pt = chart.Series[hits[i].ChartArea.Name].Points.Last(elem => elem.XValue < val);
dinfo[i].caption = hits[i].ChartArea.Name;
dinfo[i].value = pt.YValues[0].ToString();
//hits[i].ChartArea.CursorX.Position = pt.XValue;
}
}
This show the right values for every existing data point but not that clicked point.
How can i get the exact value?
It seems, there is no way to get the exact value. I changed to OxyPlot. OxyPlot can show the data much faster and you can get the exact value for any point.

Categories