using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; // This is the code for your desktop app. // Press Ctrl+F5 (or go to Debug > Start Without Debugging) to run your app. namespace DesktopApp1 { public partial class Form1 : Form { //////////////////// Variables and Declarations //////////////////// ///////////////////////////////////////////////////////////////////////////// /*---------- Genetic Algorithm Variables ----------*/ private GeneticAlgorithm ga; public static double Tolerance = .0001; // used when testing to see how close a point is to a line (roundoff) public static double DistTolerance = 100; public static double BestPathTolerance = .1; // used to test if best line is close enough to good solution public int PopulationSize = 10; //Changable population size of each generation public double mutationRate = 0.05f; // % for how often to mutate public int NumOfElite = 2; // Number of kept elite of each generation public int NumOfGens = 100; // Number of generations before stopping public Random random; // used to generate "random" numbers /*---------- Default and Max/Min Variables ----------*/ private static int PolygonMaxSides = 50; private static int NumGensMax = 1000; private static int NumGensMin = 0; private static int NumGensDefault = 100; private static int PopulationSizeDefault = 10; private static int PopulationSizeMax = 100; private static int PopulationSizeMin = 1; private static double MutationRateDefault = 0.05f; private static double MutationRateMin = 0.0f; private static double MutationRateMax = 1.0f; private static int NumOfEliteDefault = 2; private static int NumOfEliteMax = 100; private static int NumOfEliteMin = 0; /*---------- Start and End Point Variables ----------*/ public static Point StartPoint; public static Point EndPoint; public static Line BestLine; // Point(StartPoint, EndPoint) == Straight line /*---------- Connectivity Variables ----------*/ private bool AddNewDestinations = true; public int[,] matrix; public List DestinationPoints; //list of all Vertices in polygons public List PolyLines; //list of all lines in polygons (used to check intersection) public List> PolyPtList; //list for each polygon which points are in it; public List> PolyLineList; //List for each polygon which lines are in it; public List> ValidPathLines; //List for each Point for where it can have lines to /*---------- Random Variables ----------*/ private Image DrawArea, BaseImg; private Graphics GScreen; Pen MyPen = new Pen(Color.Black, 3); /*--------------------- End Variables and Declarations ---------------------*/ /*--------------------------------------------------------------------------*/ public Form1() { InitializeComponent(); random = new Random(); DestinationPoints = new List(); PolyLines = new List(); ValidPathLines = new List>(); DrawArea = new Bitmap(Canvas.Width, Canvas.Height); BaseImg = DrawArea; GScreen = Graphics.FromImage(DrawArea); StartPoint = new Point(Canvas.Location.X + 10, Canvas.Location.Y + 10); EndPoint = new Point(Canvas.Width - 300, Canvas.Height - 100); BestLine = new Line(StartPoint, EndPoint); DistTolerance = (BestLine.Dist * .1); SetDefaultVarControls(); ResetScreen(); } ///////////////// Functions and Data Manipulation ////////////////// ///////////////////////////////////////////////////////////////////////////// /*---------- Connectivity of Points ----------*/ public void BuildMatix() { ValidPathLines = new List>(); //reinitialize to blank Line testLine = new Line(); //bool goodline = true; matrix = new int[DestinationPoints.Count, DestinationPoints.Count]; for (int i = 0; i < DestinationPoints.Count; i++) { ValidPathLines.Add(new List()); for (int j = 0; j < DestinationPoints.Count; j++) { if (i != j) { testLine = new Line(DestinationPoints[i], DestinationPoints[j]); if (!IsCross(testLine)) { matrix[i, j] = 1; ValidPathLines[i].Add(testLine); } } } } // Sort the ValidPathLines for (int i = 0; i < ValidPathLines.Count; i++) { ValidPathLines[i].Sort(delegate(Line A, Line B) { return A.Dist.CompareTo(B.Dist); }); } if (true) // Test outputs { /* Output for ValidPathLines */ //textBox1.AppendText(Environment.NewLine); //for (int i = 0; i < ValidPathLines.Count; i++) //{ // textBox1.AppendText(Environment.NewLine + DestinationPoints[i]); // for (int j = 0; j < ValidPathLines[i].Count; j++) // { // textBox1.AppendText(Environment.NewLine + ValidPathLines[i][j].Point1 // + ", " + ValidPathLines[i][j].Point2 + " -- " + ValidPathLines[i][j].Dist); // } // textBox1.AppendText(Environment.NewLine); //} /* Output for Matrix */ //textBox1.AppendText(Environment.NewLine + "Matrix" + Environment.NewLine); //for (int i = 0; i < DestinationPoints.Count; i++) //{ // for (int j = 0; j < DestinationPoints.Count; j++) // { // textBox1.AppendText(" " + matrix[i, j]); // } // textBox1.AppendText(Environment.NewLine); //} } } public bool IsCross(Line line) // checks if 'line' crosses any polygon lines, True if it crosses one { bool HasEndPt = false; bool HasStartPt = false; if (IsEndpoint(line, StartPoint) || IsEndpoint(line, EndPoint))//line contains startpoint or endpoint as a point { for (int i = 0; i < PolyLineList.Count; i++) { for (int j = 0; j < PolyLineList[i].Count; j++) { if (LineIntersect(line, PolyLineList[i][j])) { return true; // fail, it crosses a polygon line } } } return false; // doesn't cross another polygon, good path } for (int i = 0; i < PolyLineList.Count; i++)// these loops check to see if it's one of the polygon lines { for (int j = 0; j < PolyLineList[i].Count; j++) { if (IsEndpoint(PolyLineList[i][j], line.Point1) && IsEndpoint(PolyLineList[i][j], line.Point2)) { return false; //add because its one of the polygon lines } if (IsEndpoint(PolyLineList[i][j], line.Point1)) HasStartPt = true; if (IsEndpoint(PolyLineList[i][j], line.Point2)) HasEndPt = true; } if (HasStartPt && HasEndPt) // same polygon, not one of the lines, don't add { return true; } if (HasStartPt || HasEndPt) { break; } // leave this loops because its between 2 different polygons } for (int i = 0; i < PolyLineList.Count; i++) // these loops check to see if it crosses a polygon line { for (int j = 0; j < PolyLineList[i].Count; j++) { if (LineIntersect(line, PolyLineList[i][j])) { return true; // fail, it crosses a polygon line } } } return false; // doesn't cross another polygon, good path } /*---------- Variable Control ----------*/ private void VarSubmit() { NumOfGens = Convert.ToInt32(Math.Round(numericUpDown1.Value, 0)); PopulationSize = Convert.ToInt32(Math.Round(numericUpDown2.Value, 0)); NumOfElite = Convert.ToInt32(Math.Round(numericUpDown3.Value, 0)); mutationRate = (double)numericUpDown4.Value; textBox1.AppendText(Environment.NewLine + "Number of Gens: " + NumOfGens); textBox1.AppendText(Environment.NewLine + "Population Size: " + PopulationSize); textBox1.AppendText(Environment.NewLine + "Number of Elite: " + NumOfElite); textBox1.AppendText(Environment.NewLine + "Mutation Rate(%): " + mutationRate); } private void SetDefaultVarControls() { numericUpDown1.Minimum = NumGensMin; numericUpDown1.Maximum = NumGensMax; numericUpDown1.Value = NumGensDefault; NumOfGens = NumGensDefault; numericUpDown2.Minimum = PopulationSizeMin; numericUpDown2.Maximum = PopulationSizeMax; numericUpDown2.Value = PopulationSizeDefault; PopulationSize = PopulationSizeDefault; numericUpDown3.Minimum = NumOfEliteMin; numericUpDown3.Maximum = NumOfEliteMax; numericUpDown3.Value = NumOfEliteDefault; NumOfElite = NumOfEliteDefault; numericUpDown4.Minimum = (decimal)MutationRateMin; numericUpDown4.Maximum = (decimal)MutationRateMax; numericUpDown4.Value = (decimal)MutationRateDefault; mutationRate = MutationRateDefault; } /*---------- Form Controls ----------*/ private void ButtonStart_Click(object sender, EventArgs e) { BuildMatix(); //VarSubmit(); //ResetScreen(); if (false) { //////////////////////////////////////////////////////////////////////////// bool result = true; //TestLine1 Point Pt1 = new Point(30, 20); Point Pt2 = new Point(70, 200); //TestLine2 Point Pt3 = new Point(30, 40); Point Pt4 = new Point(60, 40); Line TestLine1 = new Line(Pt1, Pt2); Line TestLine2 = new Line(Pt3, Pt4); result = LineIntersect(TestLine1, TestLine2); //bool result2 = TestLine1 == TestLine2; //textBox1.AppendText(Environment.NewLine + "Result same: " + result2); textBox1.AppendText(Environment.NewLine + "Result Intersect:" + result + " " + TestLine1.Point1 + " " + TestLine1.Point2 + " " + TestLine2.Point1 + " " + TestLine2.Point2); ///////////////////////////////////////////////////////////////////////////////////////////// for (int i = 0; i < PolyLineList.Count; i++) { for (int j = 0; j < PolyLineList[i].Count; j++) { TestLine2 = PolyLineList[i][j]; result = LineIntersect(TestLine1, TestLine2); //bool result2 = testline1 == testline2; //textbox1.appendtext(environment.newline + "result same: " + result2); textBox1.AppendText(Environment.NewLine + "result intersect:" + result + " " + TestLine1.Point1 + " " + TestLine1.Point2 + " " + TestLine2.Point1 + " " + TestLine2.Point2); } //} } } if(false) { // tests for Crossover/mutation DNA test1 = new DNA(DestinationPoints.Count, BestLine, mutationRate, random, GetRandomGene, FitnessFunction, PointLocation, Splice, InitNewGenes: true); DNA test2 = new DNA(DestinationPoints.Count, BestLine, mutationRate, random, GetRandomGene, FitnessFunction, PointLocation, Splice, InitNewGenes: true); DNA Result = new DNA(DestinationPoints.Count, BestLine, mutationRate, random, GetRandomGene, FitnessFunction, PointLocation, Splice, InitNewGenes: false); //determine fitness double score = 0; for (int i = 0; i < test1.Genes.Length; i++) { // how to determine fitness here score += test1.Genes[i].Dist; } score /= BestLine.Dist; test1.Fitness = score; score = 0; for (int i = 0; i < test2.Genes.Length; i++) { // how to determine fitness here score += test2.Genes[i].Dist; } score /= BestLine.Dist; test2.Fitness = score; //Cross Result = test1.Crossover(test2); score = 0; for (int i = 0; i < Result.Genes.Length; i++) { // how to determine fitness here score += Result.Genes[i].Dist; } score /= BestLine.Dist; Result.Fitness = score; //output string[] lines = new string[4]; string file = ("WriteSolution.txt"); foreach (var gene in test1.Genes) { lines[0] += ("- " + gene.Point1 + " -"); } lines[0] += ("- " + EndPoint + Environment.NewLine + " Fitness: " + test1.Fitness + Environment.NewLine); for (int j = 0; j < DestinationPoints.Count; j++) { lines[0] += (" | " + test1.Visited[j]); } foreach (var gene in test2.Genes) { lines[1] += ("- " + gene.Point1 + " -"); } lines[1] += ("- " + EndPoint + Environment.NewLine + " Fitness: " + test2.Fitness + Environment.NewLine); for (int j = 0; j < DestinationPoints.Count; j++) { lines[1] += (" | " + test2.Visited[j]); } foreach (var gene in Result.Genes) { lines[2] += ("- " + gene.Point1 + " -"); } lines[2] += ("- " + EndPoint + Environment.NewLine + " Fitness: " + Result.Fitness + Environment.NewLine); for (int j = 0; j < DestinationPoints.Count; j++) { lines[2] += (" | " + Result.Visited[j]); } test1 = test1.Mutate(); foreach (var gene in test1.Genes) { lines[3] += ("- " + gene.Point1 + " -"); } lines[3] += ("- " + EndPoint + Environment.NewLine + " Fitness: " + test1.Fitness + Environment.NewLine); for (int j = 0; j < DestinationPoints.Count; j++) { lines[3] += (" | " + test1.Visited[j]); } File.WriteAllLines(file, lines); } ResetScreen(); ga = new GeneticAlgorithm(PopulationSize, DestinationPoints.Count, BestLine, random, GetRandomGene, FitnessFunction, PointLocation, Splice, NumOfElite, mutationRate); ga.CalculateFitness(); ga.Population.Sort(delegate (DNA A, DNA B) { return A.Fitness.CompareTo(B.Fitness); }); //OutputGeneration("BeforeUpdate.txt"); bool run = true; while (run) { run = GenUpdate(); } //textBox1.AppendText(Environment.NewLine // + "Hey! Go check out the 'WriteSolution.txt' file in the Debug folder!"); if (false) { //****************************// //// Create a new form. //Form form2 = new Form(); //// Set the text displayed in the caption. //form2.Text = "Display Generations"; //// Size the form to be 800 pixels in height and width. //form2.Size = new Size(800, 800); //// Display the form in the center of the screen. //form2.StartPosition = FormStartPosition.CenterScreen; //// Display the form as a modal dialog box. //form2.ShowDialog(); //ga = new GeneticsAlgorithm(PopulationSize, MaxPathLength, random, //GetRandomPoint, FitnessFunction, NumOfElite, mutationRate); //***************************// } } private void ButtonVarSubmit_Click(object sender, EventArgs e) { VarSubmit(); } private void ButtonVarReset_Click(object sender, EventArgs e) { SetDefaultVarControls(); // set to defaults VarSubmit(); // submit values } private void ButtonResetDisplay_Click(object sender, EventArgs e) { ResetScreen(); } private void UpdateTextBox1(object sender, EventArgs e) { Invalidate(); } private void Canvas_Paint(object sender, PaintEventArgs e) { //MessageBox.Show("Canvas_Paint Here I am"); Graphics g = e.Graphics; g.DrawImage(DrawArea, Canvas.Location.X, Canvas.Location.Y); } private void Canvas_MouseClick(object sender, MouseEventArgs e) { MessageBox.Show("Mouse: " + e.X.ToString() + " " + e.Y.ToString()); //Graphics g = Graphics.FromImage(DrawArea); //GScreen.DrawEllipse(MyPen, e.X, e.Y, 10, 10); } /*---------- Drawing ----------*/ private void DrawMap() { StreamReader sr = new StreamReader("Map1.txt"); string fileContents = sr.ReadToEnd(); sr.Close(); string[] AllPolygons = fileContents.Split('\n'); string[] Polygon = new string[PolygonMaxSides]; string[] APoint = new string[2]; Point FirstPt = new Point(0, 0); Point PrePt = new Point(0, 0); Point CurPt = new Point(0, 0); if (AddNewDestinations) { PolyPtList = new List>(); //list for each polygon which points are in it; PolyLineList = new List>(); //List for each polygon which lines are in it; } for (int i = 0; i < AllPolygons.Length; i++) { Polygon = AllPolygons[i].Split(';'); //textBox1.AppendText(Environment.NewLine + "Polygon #" + i + AllPolygons[i]); if (AddNewDestinations) { PolyPtList.Add(new List()); PolyLineList.Add(new List()); } for (int j = 0; j < Polygon.Length; j++) { APoint = Polygon[j].Split(','); if (APoint[0] == "") break; CurPt.X = int.Parse(APoint[0]); CurPt.Y = int.Parse(APoint[1]); //textBox1.AppendText(Environment.NewLine + "Point #" + j + " " + X + " , " + Y); if (j == 0) //first point { PrePt = CurPt; FirstPt = CurPt; //textBox1.AppendText(Environment.NewLine + "First Point " + j + " " + FirstX + " , " + FirstY); if (AddNewDestinations) { DestinationPoints.Add(CurPt); PolyPtList[i].Add(CurPt); } } else if (j == (Polygon.Length - 1)) //Last point { GScreen.DrawLine(MyPen, PrePt, CurPt); GScreen.DrawLine(MyPen, CurPt, FirstPt); if (AddNewDestinations) { PolyLines.Add(new Line(PrePt, CurPt)); PolyLines.Add(new Line(CurPt, FirstPt)); DestinationPoints.Add(CurPt); PolyPtList[i].Add(CurPt); PolyLineList[i].Add(new Line(PrePt, CurPt)); PolyLineList[i].Add(new Line(CurPt, FirstPt)); } } else //any point in between { GScreen.DrawLine(MyPen, PrePt, CurPt); if (AddNewDestinations) { PolyLines.Add(new Line(PrePt, CurPt)); DestinationPoints.Add(CurPt); PolyPtList[i].Add(CurPt); PolyLineList[i].Add(new Line(PrePt, CurPt)); } //textBox1.AppendText(Environment.NewLine + "Point " + j + " " + PreX + " , " + PreY); PrePt = CurPt; } } } //Add startpoint and endpoint so they can be selected randomly as well? if (AddNewDestinations) { DestinationPoints.Add(StartPoint); DestinationPoints.Add(EndPoint); } // draw startpoint, endpoint, and border GScreen.DrawEllipse(MyPen, StartPoint.X, StartPoint.Y, 8, 8); GScreen.DrawEllipse(MyPen, EndPoint.X, EndPoint.Y, 8, 8); //GScreen.DrawRectangle(MyPen, 0, 0, Canvas.Width, Canvas.Height); // Outputs if (AddNewDestinations) { //textBox1.AppendText(Environment.NewLine + "DestinationPoints"); //for (int i = 0; i < DestinationPoints.Count; i++) // out all points //{ // textBox1.AppendText(Environment.NewLine + "Point " + i + ": " // + DestinationPoints[i].X + " , " + DestinationPoints[i].Y); //} //textBox1.AppendText(Environment.NewLine + "PolyLines"); //for (int i = 0; i < PolyLines.Count; i++) // out all Lines //{ // textBox1.AppendText(Environment.NewLine + "Line " + i + ": " // + PolyLines[i].Point1 + " , " + PolyLines[i].Point2); //} //textBox1.AppendText(Environment.NewLine + "PolyPtList"); //for (int i = 0; i < PolyPtList.Count; i++) // out PolyPtList //{ // textBox1.AppendText(Environment.NewLine + "Polygon " + i + ": "); // for (int j = 0; j < PolyPtList[i].Count; j++) // { // textBox1.AppendText(Environment.NewLine + PolyPtList[i][j]); // } // textBox1.AppendText(Environment.NewLine); //} //textBox1.AppendText(Environment.NewLine + "PolyLineList" ); //for (int i = 0; i < PolyLineList.Count; i++) // out PolyLineList //{ // textBox1.AppendText(Environment.NewLine + "Polygon " + i + ": "); // for (int j = 0; j < PolyLineList[i].Count; j++) // { // textBox1.AppendText(Environment.NewLine + PolyLineList[i][j].Point1 // + ", " + PolyLineList[i][j].Point2); // } // textBox1.AppendText(Environment.NewLine); //} } AddNewDestinations = false; } private void ResetScreen() { BaseImg = new Bitmap(Canvas.Width, Canvas.Height); GScreen = Graphics.FromImage(BaseImg); DrawMap(); DrawArea = BaseImg; Canvas.Invalidate(); } private void AddPath(int index) { GScreen = Graphics.FromImage(DrawArea); if (index <= 0) { MyPen = new Pen(Color.Blue, 4); foreach (var gene in ga.BestGenes) { GScreen.DrawLine(MyPen, gene.Point1, gene.Point2); } } else { MyPen = new Pen(Color.Red, 4); foreach (var gene in ga.Population[index].Genes) {GScreen.DrawLine(MyPen, gene.Point1, gene.Point2); } } MyPen = new Pen(Color.Black, 3); Canvas.Invalidate(); } /*------------------------- End Functions and Data -------------------------*/ /*--------------------------------------------------------------------------*/ /////////////////////// Structs and Classes //////////////////////// ///////////////////////////////////////////////////////////////////////////// /*---------- Lines or Points ----------*/ public struct Line { public Point Point1 { get; set; } public Point Point2 { get; set; } public double Dist { get; } public Line(Point A, Point B) { Point1 = A; Point2 = B; Dist = Math.Sqrt(((A.X - B.X) * (A.X - B.X)) + ((A.Y - B.Y) * (A.Y - B.Y))); } public static bool operator ==(Line A, Line B) { return ((A.Point1 == B.Point1 && A.Point2 == B.Point2) || (A.Point2 == B.Point1 && A.Point1 == B.Point2)); } public static bool operator !=(Line A, Line B) { return !((A.Point1 == B.Point1 && A.Point2 == B.Point2) || (A.Point2 == B.Point1 && A.Point1 == B.Point2)); } public override bool Equals(object obj) { return base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); } } public bool LineIntersect(Line line1, Line line2) //return true if they intersect, false if they do not { double x1 = line1.Point1.X, y1 = line1.Point1.Y, //line one point 1 X,Y x2 = line1.Point2.X, y2 = line1.Point2.Y, //line one point 2 X,Y a1 = line2.Point1.X, b1 = line2.Point1.Y, //line two point 1 X,Y a2 = line2.Point2.X, b2 = line2.Point2.Y; //line two point 2 X,Y //check for denominator == 0 if (x2 - x1 == 0 && a2 - a1 != 0) //line1 vertical only { //textBox1.AppendText(Environment.NewLine + "Line1 Vertical Only"); if ((IsEndpoint(line1, line2.Point1) || IsEndpoint(line1, line2.Point2))) { // not same slope so can't be same line, check if shared endpoint return false; } if ((x1 - a1 <= 0 && x1 - a2 <= 0) || (x1 - a1 >= 0 && x1 - a2 >= 0)) { // if both points of other line are on the same side of vertical line return false; } // if points on opposite sides of line, then they cross somewhere along the full vertical line at X, but where? double Slope2 = (b2 - b1) / (a2 - a1); double YInt = Slope2 * (x1 - a1) + b1; //textBox1.AppendText(Environment.NewLine + "XInt: " + a1 + " YInt: " + YInt); // is intercept on non-vertical line and between the Y's of vertical line? return (IsOnLine(line2, a1, YInt) && ((y1 <= YInt && YInt <= y2) || (y1 >= YInt && YInt >= y2))); } if (x2 - x1 != 0 && a2 - a1 == 0) //line2 vertical only { //textBox1.AppendText(Environment.NewLine + "Line2 Vertical Only"); if ((IsEndpoint(line1, line2.Point1) || IsEndpoint(line1, line2.Point2))) { // not same slope so can't be same line, check if shared endpoint return false; } if ((a1 - x1 <= 0 && a1 - x2 <= 0) || (a1 - x1 >= 0 && a1 - x2 >= 0)) { // if both points of other line are on the same side of vertical line return false; } // if points on opposite sides of line, then they cross somewhere along the full vertical line at X, but where? double Slope1 = (y2 - y1) / (x2 - x1); double YInt = Slope1 * (a1 - x1) + y1; //textBox1.AppendText(Environment.NewLine + "XInt: " + a1 + " YInt: " + YInt); // is intercept on non-vertical line and between the Y's of vertical line? return (IsOnLine(line1, a1, YInt) && ((b1 <= YInt && YInt <= b2) || (b1 >= YInt && YInt >= b2))); } if (x2 - x1 == 0 && a2 - a1 == 0) // both vertical/parallel { //textBox1.AppendText(Environment.NewLine + "Lines Both Vertical"); // if X's are same (same vertical line), and one // lines Y lies within the other they cross if (x1 != a1) { return false; // can't overlap, segments not in same line } //segments on same line, check if Y's overlap at all return (((b1 < y1 && y1 < b2) || (b1 < y2 && y2 < b2)) || ((y1 < b1 && b1 < y2) || (y1 < b2 && b2 < y2))); } double m1 = (y2 - y1) / (x2 - x1), m2 = (b2 - b1) / (a2 - a1); // calculate slopes if (m1 == m2) { //textBox1.AppendText(Environment.NewLine + "Is Same Slope?"); //same slope return IsOnLine(line1, line2.Point1.X, line2.Point1.Y) || IsOnLine(line1, line2.Point2.X, line2.Point2.Y) || line1 == line2; // check if either point of one line are on the other line } if ((IsEndpoint(line1, line2.Point1) || IsEndpoint(line1, line2.Point2))) { // not same slope so can't be same line, check if shared endpoint //textBox1.AppendText(Environment.NewLine + "Is an Endpoint?"); return false; } else//if not same slope, they have to cross somewhere... { double X = ((m2 * a1) - (m1 * x1) + y1 - b1) / (m2 - m1); double Y = m1 * (X - x1) + y1; //textBox1.AppendText(Environment.NewLine + "intercepts at X: " //+ X + " Y: " + Y); return (IsOnLine(line1, X, Y) && IsOnLine(line2, X, Y)); } } public bool IsOnLine(Line line, double X, double Y) //returns true if (X,Y) is on line { bool DoDef = true; double def = 0; // X between lines X's not including endpoints bool XBetween = (line.Point1.X < X && X < line.Point2.X) || (line.Point1.X > X && X > line.Point2.X); if (line.Point1.X == line.Point2.X) { if (Math.Abs(line.Point1.X - X) < Tolerance) { XBetween = true; // vertical line, intercepts on line DoDef = false; } } //textBox1.AppendText(Environment.NewLine + "XBetween: " + XBetween); // Y between lines Y's not including endpoints bool YBetween = (line.Point1.Y < Y && Y < line.Point2.Y) || (line.Point1.Y > Y && Y > line.Point2.Y); if (line.Point1.Y == line.Point2.Y) { if (Math.Abs(line.Point1.Y - Y) < Tolerance) { YBetween = true; // horisontal line, intercepts on line DoDef = false; } } //textBox1.AppendText(Environment.NewLine + "YBetween: " + YBetween); if (DoDef) { def = Math.Abs(((line.Point2.X - line.Point1.X) * (Y - line.Point1.Y) - ((X - line.Point1.X) * (line.Point2.Y - line.Point1.Y)))); //textBox1.AppendText(Environment.NewLine + "def: " + def); } return (XBetween && YBetween) && (def < Tolerance); } public bool IsEndpoint(Line line, Point point) // returns true if point is one of the endpoints { return (point == line.Point1 || point == line.Point2); } public int PointLocation(Point A) { int retVal = -1; for(int i = 0; i < DestinationPoints.Count; i++) { if(A == DestinationPoints[i]) { retVal = i; break; } } return retVal; } public List Splice(int offset, int count, Line[] list) { return list.Skip(offset).Take(count).ToList(); } /*---------- Genetic Algorithm ----------*/ public class GeneticAlgorithm { public List Population { get; private set; } public int Generation { get; private set; } public double BestFitness { get; private set; } public Line[] BestGenes { get; private set; } public int BestGenesTime { get; private set; } public static Line IdealLine { get; private set; } public static double MutationRate; private List NewPopulation; private Random random; private double FitnessSum; public static int NumOfElite; private static int NumVertex; private static Func GetRandomGene; private static Func FitnessFunction; private static Func PointLocation; private static Func> Splice; public GeneticAlgorithm(int PopulationSize, int numVertex,Line idealLine, Random random, Func getRandomGene, Func fitnessFunction, Func pointLocation, Func> splice, int numOfElite, double mutationRate = 0.01f) { Generation = 1; BestGenes = new Line[numVertex]; BestFitness = 1000; BestGenesTime = -1; IdealLine = idealLine; NumOfElite = numOfElite; MutationRate = mutationRate; NumVertex = numVertex; GetRandomGene = getRandomGene; FitnessFunction = fitnessFunction; PointLocation = pointLocation; Splice = splice; Population = new List(PopulationSize); NewPopulation = new List(PopulationSize); this.random = random; for (int i = 0; i < PopulationSize; i++) { Population.Add(new DNA(numVertex, IdealLine, MutationRate, random, getRandomGene, fitnessFunction, pointLocation, splice, InitNewGenes: true)); } } public void NewGeneration(int NumNew = 0, bool CrossNewMembers = false) { int finalCount = Population.Count + NumNew; if (finalCount <= 0) { return; } if (Population.Count > 0) { CalculateFitness(); Population.Sort(CompareGenes); } NewPopulation.Clear(); for (int i = 0; i < finalCount; i++) { if (i < NumOfElite && i < Population.Count) { NewPopulation.Add(Population[i]); } else if (i < Population.Count - 2 || CrossNewMembers) { DNA parent1 = ChooseParent(); DNA parent2 = ChooseParent(); DNA child = parent1.Crossover(parent2); child.Mutate(); NewPopulation.Add(child); } else { DNA NewDNA = new DNA(NumVertex, IdealLine, MutationRate, random, GetRandomGene, FitnessFunction, PointLocation, Splice, true); NewPopulation.Add(NewDNA); } } List Temp = Population; Population = NewPopulation; NewPopulation = Temp; Generation++; } public int CompareGenes(DNA A, DNA B) { return A.Fitness.CompareTo(B.Fitness); //if (A.Fitness > B.Fitness) return -1; // A is more fit //else if (A.Fitness < B.Fitness) return 1; // B is more fit //else return 0; //Tie } public void CalculateFitness() { FitnessSum = 0; DNA best = Population[0]; best.CalculateFitness(0); for (int i = 0; i < Population.Count; i++) { FitnessSum += Population[i].CalculateFitness(i); if (Population[i].Fitness < best.Fitness) { best = Population[i]; } } if (BestFitness > best.Fitness) { BestFitness = best.Fitness; List temp = new List(); for (int i = 0; i < best.Genes.Length; i++) { temp.Add(best.Genes[i]); } BestGenesTime = 0; this.BestGenes = new Line[temp.Count]; temp.ToArray().CopyTo(this.BestGenes, 0); } else { BestGenesTime++; } } private DNA ChooseParent() { double randomNumber = random.NextDouble() * FitnessSum; for (int i = 0; i < Population.Count; i++) { if (randomNumber < Population[i].Fitness) { return Population[i]; } randomNumber -= Population[i].Fitness; } return null; } } /*---------- Individual DNA ----------*/ public class DNA { public Line[] Genes { get; set; } public int[] Visited { get; set; } public double Fitness { get; set; } private Random random; private static Line IdealLine; private static double MutationRate; private static int NumVertex; private static Func GetRandomGene; private static Func FitnessFunction; private static Func PointLocation; private static Func> Splice; public DNA(int numVertex, Line idealLine, double mutationRate, Random random, Func getRandomGene, Func fitnessFunction, Func pointLocation, Func> splice, bool InitNewGenes = true) { this.random = random; IdealLine = idealLine; MutationRate = mutationRate; GetRandomGene = getRandomGene; FitnessFunction = fitnessFunction; PointLocation = pointLocation; Splice = splice; NumVertex = numVertex; Visited = new int[numVertex]; for (int i = 0; i < numVertex; i++) { Visited[i] = -1; } if (InitNewGenes) Genes = GeneratePath(); } public Line[] GeneratePath() { List OutLines = new List(); Line TempLine = new Line(); Line BadLine = new Line(new Point(-1, -1), new Point(-1, -1)); int Vis = 0; TempLine = GetRandomGene(0, Visited, IdealLine); if(TempLine != BadLine) { Visited[PointLocation(TempLine.Point1)] = Vis++; OutLines.Add(TempLine); } { Visited[PointLocation(TempLine.Point2)] = Vis++; while (Visited[Visited.Length - 1] < 0 && TempLine != BadLine) { TempLine = GetRandomGene(1, Visited, OutLines.Last()); if (TempLine != BadLine) { Visited[PointLocation(TempLine.Point2)] = Vis++; OutLines.Add(TempLine); } } } if(OutLines.Last().Point2 == EndPoint) return OutLines.ToArray(); else { for (int i = 0; i < Visited.Length; i++) { Visited[i] = -1; } return GeneratePath(); } } public DNA Mutate() { DNA mutated = new DNA(Visited.Length, IdealLine, MutationRate, random, GetRandomGene, FitnessFunction, PointLocation, Splice, InitNewGenes: false); mutated = this; int MutatePt = random.Next(Genes.Length - 1) + 1; List OutLines = new List(); Line TempLine = new Line(new Point(0, 0), new Point(0, 0)); Line BadLine = new Line(new Point(-1, -1), new Point(-1, -1)); int Vis = 0; if (random.NextDouble() > MutationRate) { return mutated; } // Don't mutate else { for (int i = 0; i < mutated.Visited.Length; i++) { mutated.Visited[i] = -1; } for (int i = 0; i < MutatePt; i++) { OutLines.Add(Genes[i]); mutated.Visited[PointLocation(Genes[i].Point1)] = Vis++; } mutated.Visited[PointLocation(OutLines.Last().Point2)] = Vis++; while (mutated.Visited[Visited.Length - 1] < 0 && TempLine != BadLine) { TempLine = GetRandomGene(1, mutated.Visited, OutLines.Last()); if (TempLine != BadLine) { mutated.Visited[PointLocation(TempLine.Point2)] = Vis++; OutLines.Add(TempLine); } } mutated.Genes = OutLines.ToArray(); double score = 0; for (int i = 0; i < mutated.Genes.Length; i++) { score += mutated.Genes[i].Dist; } score /= BestLine.Dist; mutated.Fitness = score; return mutated; } } public double CalculateFitness(int index) { Fitness = FitnessFunction(index); return Fitness; } public DNA Crossover(DNA otherParent) { DNA child = new DNA(Visited.Length, IdealLine, MutationRate, random, GetRandomGene, FitnessFunction, PointLocation, Splice, InitNewGenes: false); int numShared = 0; List newList = new List(); for (int i = 0; i < NumVertex - 1; i++) // find overlaps (1 in child Visited if yes) { if (this.Visited[i] > 0 && otherParent.Visited[i] > 0) { child.Visited[i] = 1; numShared++; } } if (numShared == 0) // no overlap return first parent { if (this.Fitness > otherParent.Fitness) child = otherParent; else child = this; return child; } else if (numShared == 1) //overlap at only one point { int Vis = 0; bool found = false; for(int i = 0; i < this.Genes.Length; i++) { newList.Add(this.Genes[i]); child.Visited[PointLocation(newList.Last().Point1)] = Vis++; if (child.Visited[PointLocation(this.Genes[i].Point2)] > 0) { break; } } for (int i = 0; i < otherParent.Genes.Length; i++) { if(!found) if (otherParent.Genes[i].Point1 == newList.Last().Point2) { found = true; } if(found) { newList.Add(otherParent.Genes[i]); child.Visited[PointLocation(newList.Last().Point1)] = Vis++; } } child.Visited[PointLocation(newList.Last().Point2)] = Vis++; child.Genes = newList.ToArray(); } else { int Vis = 0; bool test = true; int index1 = 0, index2 = otherParent.Genes.Length - 1, Order = 1; int[] SharedThis = new int[NumVertex]; int[] SharedOther = new int[NumVertex]; // find order in which they appear in each of the Original lists for (int i = 0; i < this.Genes.Length; i++) { int temp = PointLocation(this.Genes[i].Point1); if (child.Visited[temp] > 0) { SharedThis[temp] = Order++; } } Order = 1; for (int i = 0; i < otherParent.Genes.Length; i++) { int temp = PointLocation(otherParent.Genes[i].Point1); if (child.Visited[temp] > 0) { SharedOther[temp] = Order++; } } bool check = true; while(check) { for(int i = 0; i < NumVertex; i++) { if(child.Visited[i] > 0) { SharedThis[i]--; SharedOther[i]--; if(SharedThis[i] == 0 || SharedOther[i] == 0) { child.Visited[i] = -1; numShared--; if(numShared == 1) { break; } } } } int sum = 0; for(int i = 0; i < NumVertex; i++) { sum += child.Visited[i] + 1; } if(sum == 2 || sum == 1) { for (int i = 0; i < NumVertex; i++) { if(child.Visited[i] > 0) { index1 = i; check = false; } } } } for(int i = 0; i < this.Genes.Length; i++) { newList.Add(this.Genes[i]); child.Visited[PointLocation(newList.Last().Point2)] = Vis++; if(PointLocation(newList.Last().Point2) == index1) { break; } } check = false; for(int i = 0; i < otherParent.Genes.Length; i++) { if (check) { newList.Add(otherParent.Genes[i]); child.Visited[PointLocation(newList.Last().Point2)] = Vis++; } if (!check) { if (index1 //PointLocation(newList.Last().Point2) == PointLocation(otherParent.Genes[i].Point2)) { check = true; } } } //child.Visited[PointLocation(newList.Last().Point2)] = Vis++; child.Genes = newList.ToArray(); if (false) { //for (int i = 1; i < numShared; i++) // for (int j = 0; j < NumVertex; j++) // if (child.Visited[j] > 0 && (SharedThis[j] != SharedOther[j])) // { // if (SharedThis[j] > SharedOther[j]) // { // for (int k = 0; k < NumVertex; k++) // { // if (SharedThis[k] > 0) SharedThis[k]--; // if (SharedThis[k] == 0) // { // child.Visited[k] = 0; // SharedOther[k] = 0; // } // } // } // else if (SharedThis[j] < SharedOther[j]) // { // for (int k = 0; k < NumVertex; k++) // { // if (SharedOther[k] > 0) SharedOther[k]--; // if (SharedOther[k] == 0) // { // child.Visited[k] = 0; // SharedThis[k] = 0; // } // } // } // } newList.Add(this.Genes[0]); if (child.Visited[PointLocation(newList.Last().Point2)] > 0) test = false; child.Visited[PointLocation(newList.Last().Point1)] = Vis++; child.Visited[PointLocation(newList.Last().Point2)] = Vis++; index1++; index2++; while (newList.Last().Point2 != EndPoint) { if (test) { if (index1 < this.Genes.Length) { newList.Add(this.Genes[index1]); child.Visited[PointLocation(newList.Last().Point1)] = Vis++; if (newList.Last().Point2 == EndPoint) { break; } if (child.Visited[PointLocation(newList.Last().Point2)] > 0) { while (PointLocation(otherParent.Genes[index2].Point1) != PointLocation(newList.Last().Point2) && index2 < otherParent.Genes.Length - 1) { if (index2 == otherParent.Genes.Length - 1) { test = !test; break; } index2++; } //if (index2 < otherParent.Genes.Length) test = !test; } index1++; } else { break; } } if (!test) { if (index2 < otherParent.Genes.Length) { newList.Add(otherParent.Genes[index2]); child.Visited[PointLocation(newList.Last().Point1)] = Vis++; if (newList.Last().Point2 == EndPoint) { break; } if (child.Visited[PointLocation(newList.Last().Point2)] > 0) { while (PointLocation(this.Genes[index1].Point1) != PointLocation(newList.Last().Point2) && index1 < this.Genes.Length - 1) { if (index1 == this.Genes.Length - 1) { test = !test; break; } index1++; } //if (index1 < this.Genes.Length) test = !test; } index2++; } else { break; } } } child.Visited[PointLocation(newList.Last().Point2)] = Vis++; child.Genes = newList.ToArray(); } } //for(int i = 0; i < NumVertex - 2; i++) //{ // if (child.Visited[i] == 0) // child.Visited[i] = -1; //} double score = 0; for (int i = 0; i < child.Genes.Length; i++) { score += child.Genes[i].Dist; } score /= BestLine.Dist; child.Fitness = score; return child; } } /*---------- Individual DNA/Genetic Algorithm Utilities----------*/ private double FitnessFunction(int index) { double score = 0; DNA dna = ga.Population[index]; for (int i = 0; i < dna.Genes.Length; i++) { // how to determine fitness here score += dna.Genes[i].Dist; } score /= BestLine.Dist; return score; } private Line GetRandomGene(int index, int[] visited, Line A) { int Index2 = 0; if (index == 0) // startpoint in first position { // if first line in list, add a line starting from startpoint Index2 = ValidPathLines.Count - 2; int r = random.Next(ValidPathLines[Index2].Count); return ValidPathLines[Index2][r]; } else if (index == 1) //not first position { if (IsEndpoint(A, EndPoint)) //check if it already found the endpoint { return new Line(new Point(-1, -1), new Point(-1, -1)); } // find index for what the endpoint of the last line was Index2 = PointLocation(A.Point2); // check to see if any connect to Endpoint (finish path) for (int i = 0; i < ValidPathLines[Index2].Count; i++) { if (ValidPathLines[Index2][i].Point2 == EndPoint) return ValidPathLines[Index2][i]; } // Find a Line to a point that hasn't been visited for (int i = 0; i < 25; i++) { int r = random.Next(ValidPathLines[Index2].Count); if (ValidPathLines[Index2][r] != A && visited[PointLocation(ValidPathLines[Index2][r].Point2)] < 0) {//get a random Line from the possible solutions return ValidPathLines[Index2][r]; } } return new Line(new Point(-1, -1), new Point(-1, -1)); } else { return new Line(new Point(-1, -1), new Point(-1, -1)); } } public bool GenUpdate() { ga.NewGeneration(); ga.CalculateFitness(); ga.Population.Sort(delegate (DNA A, DNA B) { return A.Fitness.CompareTo(B.Fitness); }); OutputGeneration("WriteSolution.txt"); //int max = byte.MaxValue + 1; // 256 //int r = random.Next(max); //int g = random.Next(max); //int b = random.Next(max); //MyPen.Color = Color.FromArgb(r, g, b); if (ga.Generation == NumOfGens || ga.BestGenesTime >= 15 || ga.BestFitness - 1.0 < BestPathTolerance) { //stop condition, straight path or reached Generation setting ResetScreen(); DrawArea = BaseImg; AddPath(-1); //Canvas.Invalidate(); string OutPut = ""; OutPut += (Environment.NewLine + "Good Solution Found in Generation: " + ga.Generation + Environment.NewLine + "Best Path: "); foreach (var gene in ga.BestGenes) { OutPut += ("- " + gene.Point1 + " -"); } OutPut += ("- " + EndPoint + Environment.NewLine + " Fitness: " + ga.BestFitness + Environment.NewLine); textBox1.AppendText(OutPut + Environment.NewLine); return false; } if (ga.Generation % 10 == 0) { string message = ("You are at Gen: " + ga.Generation + Environment.NewLine + "Continue?"); string title = "Continue?"; MessageBoxButtons buttons = MessageBoxButtons.YesNo; DialogResult result = MessageBox.Show(message, title, buttons); if (result == DialogResult.Yes) { ResetScreen(); DrawArea = BaseImg; Canvas.Invalidate(); for (int i = 0; i < NumOfElite; i++) { AddPath(i); } textBox1.AppendText(Environment.NewLine + "Elite of Generation " + ga.Generation + ": "); for (int i = 0; i < NumOfElite; i++) { Out1DNA(i); } //Task.Delay(5000); // Wait 5 seconds return true; } else { DrawArea = BaseImg; AddPath(-1); Canvas.Invalidate(); string OutPut = ""; OutPut += ("Generation: " + ga.Generation + Environment.NewLine + "Best Path: "); foreach (var gene in ga.BestGenes) { OutPut += ("- " + gene.Point1 + " -"); } OutPut += ("- " + EndPoint + Environment.NewLine + " Fitness: " + ga.BestFitness + Environment.NewLine); textBox1.AppendText(OutPut + Environment.NewLine); return false; } } //DrawArea = BaseImg; //AddPath(-1); //Canvas.Invalidate(); //Task.Delay(5000); // Wait 5 seconds return true; // hasn't hit stopping conditions } public void OutputGeneration(string path) { string[] lines = new string[ga.Population.Count]; string file = (path); for (int i = 0; i < ga.Population.Count; i++) { foreach (var gene in ga.Population[i].Genes) { lines[i] += ("- " + gene.Point1 + " -"); } lines[i] += ("- " + EndPoint + Environment.NewLine + " Fitness: " + ga.Population[i].Fitness + Environment.NewLine); //for(int j = 0; j < DestinationPoints.Count; j++) //{ // lines[i] += (" | " + ga.Population[i].Visited[j]); //} } File.WriteAllLines(file, lines); } public void Out1DNA(int index) { string OutPut = ""; foreach (var gene in ga.Population[index].Genes) { OutPut += ("- " + gene.Point1 + " -"); } OutPut += ("- " + EndPoint + Environment.NewLine + " Fitness: " + ga.Population[index].Fitness + Environment.NewLine); textBox1.AppendText(OutPut + Environment.NewLine); } /*------------------------- End Structs and Classes -------------------------*/ /*---------------------------------------------------------------------------*/ } }