summaryrefslogtreecommitdiffstats
path: root/src/CS340.TSP/TSPSolver.cs
blob: 24e28c2b6557b910a589ea02aa34b67ce3e31bcc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Extensions;
using Graph;
using Graph.IO;

namespace TSP
{

    using Map = Graph<Vertex<Edge<double>, double>, Edge<double>, double>;
    using Road = Edge<double>;

    public static class TSPSolver
    {
        public static Tour MST(string file, string coords, int init)
        {
            Map map = GraphFile.Read(new FileReader(file)).MST(0);
            return new Tour(map.Vertices, Coordinate.Parse(coords));
        }

        public static Tour Approximate(string file, string coords, int init)
        {
            Map map = GraphFile.Read(new FileReader(file)).MST(0);
            Tour mstTour = new Tour(map.Vertices, Coordinate.Parse(coords));

            Tour approximateTour = PreOrder(mstTour[init], mstTour, new Tour());

            Debug.WriteLine("\nApproximate Best Tour");
            Debug.WriteLine(approximateTour);

            return approximateTour;

            Tour PreOrder(City city, Tour mst, Tour visited)
            {
                // set parent of current city to previous city
                if (visited.Cities.Count > 0)
                    city.Parent = visited.Cities.Last().Id;

                // add current City to visited
                visited.Cities.Add(city);

                // loop through each child, and recursivly traverse 
                foreach (Road road in city.Edges)
                    if (mst[road.V].Parent == city.Id)
                        PreOrder(mst[road.V], mst, visited);

                // on the last node, set init node's parent to the last node
                if (visited.Cities.Count == mst.Cities.Count)
                    visited.Cities.First().Parent = visited.Cities.Last().Id;

                return visited;
            }
        }


        public static Tour Solve(string file, string coords, int init)
        {
            Map map = GraphFile.Read(new FileReader(file));
            Tour bestTour = new Tour(map.Vertices, Coordinate.Parse(coords));

            double BestWeight = double.MaxValue;
            List<int> BestTour = new();

            BruteForce(map, init, map.Vertices.Select(city => city.Id).ToList(), new List<int>(), 0);


            int parent = BestTour.Last();

            foreach (int city in BestTour.Skip(1))
            {
                bestTour[city].Parent = parent;
                parent = city;
            }

            Debug.WriteLine("\nTrue Best Tour");
            Debug.WriteLine(bestTour);

            return bestTour;

            void BruteForce(Map map, int city, List<int> unvisitedInput, List<int> visitedInput, double weight)
            {
                // create local copy of Tour variables
                List<int> unvisited = new(unvisitedInput);
                List<int> visited = new(visitedInput);

                // remove current City from tour
                int removed = unvisited.RemoveAll(c => c == city);

                //? verify that a single city was removed
                Debug.Assert(removed == 1,
                             $"Failed to remove a city from the tour. \nTour: {unvisited}\nCity: {city}");

                // set parent of current city to previous city
                if (visited.Count > 0)
                    weight += map[city][visited.Last()].Weight;

                // add current City to visited
                visited.Add(city);

                if (weight > BestWeight)
                    return;

                // loop through each city. Each level of recursion has a one less city in unvisited.Cities
                // so each loop is (n - 1)! down to n == 0 as the base case
                foreach (int neighbor in unvisited)
                    BruteForce(map, neighbor, unvisited, visited, weight);

                // if we traversed all cities in the graph
                if (unvisited.Count == 0)
                {
                    // add the return home
                    weight += map[visited.First()][visited.Last()].Weight;

                    // set as best if lower weight than previous best 
                    if (weight < BestWeight)
                    {
                        BestTour = visited;
                        BestWeight = weight;
                        Debug.WriteLine("*** Found new best tour ***");
                        Debug.WriteLine(String.Join(" -> ", visited));
                        Debug.WriteLine(weight);
                    }
                }
            }
        }
    }
}