diff --git a/include/flight_planner.h b/include/flight_planner.h index 958a0b6..18f6d5c 100644 --- a/include/flight_planner.h +++ b/include/flight_planner.h @@ -30,10 +30,11 @@ class IdCity { bool operator!=(const IdCity& other) const { return !(*this == other); } bool operator<(const IdCity& other) const { return id_ < other.id_; } + /// Returns the id of the city int id() const { return id_; } private: - int id_; + int id_; // Id of the city }; @@ -46,6 +47,8 @@ class StateAircraft { id_city_(id_city), battery_level_hours_(battery_level_hours) {} StateAircraft() : id_city_(IdCity()), battery_level_hours_(-1.0) {} + /// Calculates the change in battery flying time from this state to another state + /// DeltaBattery = BatteryOther - BatteryThis double CalcBatteryDelta(const StateAircraft& other) const { assert(id_city_ == other.id_city_); return other.battery_level_hours_ - battery_level_hours_; @@ -63,12 +66,15 @@ class StateAircraft { } } + /// Returns the id of the city IdCity id_city() const { return id_city_; } + + /// Returns the remaining battery flight time in hours double battery_level_hours() const { return battery_level_hours_; } private: - IdCity id_city_; - double battery_level_hours_; + IdCity id_city_; // Id of the city + double battery_level_hours_; // Remaining battery flight time in hours }; @@ -105,6 +111,9 @@ class FlightPlannerBase : public GraphDirected { } static constexpr double MinBatteryHours() { return 0.0; } + + /// Returns the maximum battery flytime in hours. The maximum flytime is the + /// maximum flight distance 320km divided by the speed of the aircraft. static constexpr double MaxBatteryHours() { return 320.0 / SpeedKmPerHr(); } static constexpr double MaxFlightTimeHours() { return MaxBatteryHours() - MinBatteryHours(); } @@ -137,7 +146,7 @@ class FlightPlannerBase : public GraphDirected { double CalcChargeRateKmPerHr(const IdCity& id_city) const { return GetAirport(id_city).rate; } static constexpr double RadEarth() { return 6356.752; } // Radius of Earth in km - static constexpr double SpeedKmPerHr() { return 105.0; } // Speed of aircraft in km/hr + static constexpr double SpeedKmPerHr() { return 105.0; } // Speed of aircraft in km/hr from the problem statement const std::array airports_; // List of airports copied from the non-constant global in airports.h }; @@ -153,6 +162,7 @@ class FlightPlannerExact : public FlightPlannerBase { } private: + // Adds edges for this graph construction approach void AddEdgesFlightsExact(); }; @@ -170,10 +180,14 @@ class FlightPlannerGrid : public FlightPlannerBase { AddEdgesCharge(); } + /// Returns the number of battery levels in the grid int n_levels() const { return n_levels_; } + + /// Returns the vector of evenly spaced battery levels const vector& v_battery_levels() const { return v_battery_levels_; } private: + // Creates a vector of evenly spaced battery levels between the minimum and maximum battery levels static vector CreateGridBatteryLevels(int n_levels) { const double delta_battery = (MaxBatteryHours() - MinBatteryHours()) / (n_levels - 1); @@ -186,14 +200,17 @@ class FlightPlannerGrid : public FlightPlannerBase { return v_battery_levels; } + // Adds vertices linearly spaced between the minimum and maximum battery levels void AddVerticesGrid(); + // Finds the battery level in the grid that is closest to the given battery level int FindBatteryLevel(const double battery_level) const; + // Adds approximate edges for this graph construction approach void AddEdgesDrivingGrid(); - const int n_levels_; - const vector v_battery_levels_; + const int n_levels_; // Number of evenly spaced battery levels in the grid + const vector v_battery_levels_; // Vector of evenly spaced battery levels }; }; // namespace std diff --git a/include/graph_directed.h b/include/graph_directed.h index dc36308..c4620ec 100644 --- a/include/graph_directed.h +++ b/include/graph_directed.h @@ -103,7 +103,10 @@ class GraphDirected { return make_pair(path, dist[dst]); // Return the shortest path and its cost } + /// Returns the set of vertices in the graph const set& vertices() const { return vertices_; } + + /// Returns the adjacency list of the graph const map>& adjacency_list() const { return adjacency_list_; } private: diff --git a/src/flight_planner.cpp b/src/flight_planner.cpp index fbbae50..da069b3 100644 --- a/src/flight_planner.cpp +++ b/src/flight_planner.cpp @@ -6,13 +6,9 @@ namespace std { // FlightPlannerBase pair, double> FlightPlannerBase::SolvePath(const char* char_src, const char* char_dst) const { - IdCity id_src = GetIdCity(char_src); - IdCity id_dst = GetIdCity(char_dst); - - // Calculate shortest distance from vertex src to every other vertex - StateAircraft src{id_src, MaxBatteryHours()}; // Source city with fully battery - StateAircraft dst{id_dst, MinBatteryHours()}; // Destination city - return CalcMinCostPathDijkstra(src, dst); + StateAircraft src{GetIdCity(char_src), MaxBatteryHours()}; // Source city with fully battery + StateAircraft dst{GetIdCity(char_dst), MinBatteryHours()}; // Destination city + return CalcMinCostPathDijkstra(src, dst); // Solve for the minimum cost path } void FlightPlannerBase::AddEdgesCharge() { @@ -39,7 +35,7 @@ void FlightPlannerBase::AddEdgesCharge(const IdCity& id_city, const set& v_path) const { - IdCity id_src = v_path[0].id_city(); - IdCity id_dst = v_path[v_path.size() - 1].id_city(); + IdCity id_src = v_path[0].id_city(); // Id of the first city + IdCity id_dst = v_path[v_path.size() - 1].id_city(); // Id of the last city cout << endl; PrintCity(id_src); cout << ", " << endl; // Print first city - PrintChargingCitiesAndTimes(v_path, id_dst); // Print out each city the plane charges at + PrintChargingCitiesAndTimes(v_path, id_dst); // Print out each intermediate city and charging time PrintCity(id_dst); // Print last city cout << endl; } void FlightPlannerBase::PrintChargingCitiesAndTimes(const vector& v_path, const IdCity& id_dst) const { - auto fn_find_last_index_of_city = [this](vector v_path, int i) { - int j = i; - while (j < v_path.size() && v_path[i].id_city() == v_path[j].id_city()) { - j++; + // Finds the index where charging stops + auto fn_find_i_stop = [this](const vector& v_path, int i_start) { + int i_stop = i_start; + while (v_path[i_start].id_city() == v_path[i_stop].id_city()) { // Is same city + i_stop++; + if (i_stop == v_path.size()) { break; } // Reached the end of the path } - return j - 1; + + return i_stop - 1; // The last index before the city changes }; // Prints out the change time - auto fn_print_charge_time = [this](vector v_path, int i, int j) { - PrintCity(v_path[i]); - double charge_time_hours = CalcChargeTime(v_path[i], v_path[j]); + auto fn_print_charge_time = [this](vector v_path, int i_arrive, int i_depart) { + PrintCity(v_path[i_arrive]); + double charge_time_hours = CalcChargeTime(v_path[i_arrive], v_path[i_depart]); cout << ", " << setprecision(16) << charge_time_hours << "," << endl; }; // Runs the lambda on each city the plane charges at - int ind_arrive = 1; - int ind_depart = -1; + int i_charge_start = 1; + int i_charge_stop = -1; - while (v_path[ind_arrive].id_city() != id_dst) { - ind_depart = fn_find_last_index_of_city(v_path, ind_arrive); - fn_print_charge_time(v_path, ind_arrive, ind_depart); - ind_arrive = ind_depart + 1; + while (v_path[i_charge_start].id_city() != id_dst) { + i_charge_stop = fn_find_i_stop(v_path, i_charge_start); + fn_print_charge_time(v_path, i_charge_start, i_charge_stop); + i_charge_start = i_charge_stop + 1; // Start at the next city } } @@ -105,23 +104,25 @@ pair FlightPlannerBase::GetLatLonRadians(const IdCity& id_city) } double FlightPlannerBase::CalcDistKm(const IdCity& src, const IdCity& dst) const { - double lat1r, lon1r, lat2r, lon2r, u, v; + const auto lat1r_lon1r = GetLatLonRadians(src); + const double lat1r = lat1r_lon1r.first; + const double lon1r = lat1r_lon1r.second; - // Get the latitude and longitude of the source and destination cities in radians - tie(lat1r, lon1r) = GetLatLonRadians(src); - tie(lat2r, lon2r) = GetLatLonRadians(dst); + const auto lat2r_lon2r = GetLatLonRadians(dst); + const double lat2r = lat2r_lon2r.first; + const double lon2r = lat2r_lon2r.second; // Copilot auto-generated code - u = sin((lat2r - lat1r) / 2); - v = sin((lon2r - lon1r) / 2); + const double u = sin((lat2r - lat1r) / 2); + const double v = sin((lon2r - lon1r) / 2); return 2.0 * RadEarth() * asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v)); } double FlightPlannerBase::CalcChargeTime(const StateAircraft& state_lo, const StateAircraft& state_hi) const { const IdCity& id = state_lo.id_city(); - assert(id == state_hi.id_city()); // Charging needs to happen at the same city + assert(id == state_hi.id_city()); // Aircraft can't change cities while charging - const double flight_time_per_charge_hr = CalcChargeRateKmPerHr(id) / SpeedKmPerHr(); + const double flight_time_per_charge_hr = CalcChargeRateKmPerHr(id) / SpeedKmPerHr(); // Called R in the readme return state_lo.CalcBatteryDelta(state_hi) / flight_time_per_charge_hr; } @@ -138,13 +139,16 @@ void FlightPlannerExact::AddEdgesFlightsExact() { double flight_time = CalcFlightTimeHours(src, dst); if (flight_time < MaxFlightTimeHours()) { - // Flight that departs with full charge - double max_battery = MaxBatteryHours(); - AddDirectedEdge(StateAircraft{src, max_battery}, StateAircraft{dst, max_battery - flight_time}, flight_time); - - // Flight that arrives with zero charge - double min_battery = MinBatteryHours(); - AddDirectedEdge(StateAircraft{src, flight_time + min_battery}, StateAircraft{dst, min_battery}, flight_time); + { + // Flight that departs with full charge + double max_battery = MaxBatteryHours(); + AddDirectedEdge(StateAircraft{src, max_battery}, StateAircraft{dst, max_battery - flight_time}, flight_time); + } + { + // Flight that arrives with zero charge + double min_battery = MinBatteryHours(); + AddDirectedEdge(StateAircraft{src, flight_time + min_battery}, StateAircraft{dst, min_battery}, flight_time); + } } } } @@ -161,6 +165,7 @@ void FlightPlannerGrid::AddVerticesGrid() { } } +// Finds the nearest battery level without going over int FlightPlannerGrid::FindBatteryLevel(const double battery_level) const { const vector& v_bat = v_battery_levels(); @@ -177,14 +182,14 @@ void FlightPlannerGrid::AddEdgesDrivingGrid() { for (auto vertex_i : vertices()) { for (int j = 0; j < NumAirports(); j++) { if (vertex_i.id_city() == IdCity{j}) { continue; } - double flight_time_ij = CalcFlightTimeHours(vertex_i.id_city(), IdCity{j}); - double battery_i = vertex_i.battery_level_hours(); - double battery_post = battery_i - flight_time_ij; - double battery_min = MinBatteryHours(); + const double flight_time_ij = CalcFlightTimeHours(vertex_i.id_city(), IdCity{j}); + const double battery_i = vertex_i.battery_level_hours(); + const double battery_post = battery_i - flight_time_ij; + const double battery_min = MinBatteryHours(); if (battery_post < battery_min) { continue; } - int ind_lo = FindBatteryLevel(battery_post); - double battery_level_lo = v_battery_levels()[ind_lo]; + const int ind_lo = FindBatteryLevel(battery_post); + const double battery_level_lo = v_battery_levels()[ind_lo]; StateAircraft vertex_j_lo{IdCity{j}, battery_level_lo}; AddDirectedEdge(vertex_i, vertex_j_lo, flight_time_ij); } diff --git a/src/main.cpp b/src/main.cpp index 28e68be..291bc2d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,8 +11,8 @@ int main(int argc, char** argv) { return 1; } - FlightPlannerExact(airports).SolvePathAndPrint(argv[1], argv[2]); - // FlightPlannerGrid(airports, 4).SolvePathAndPrint(argv[1], argv[2]); + FlightPlannerExact(airports).SolvePathAndPrint(argv[1], argv[2]); // Run the exact planner + // FlightPlannerGrid(airports, 4).SolvePathAndPrint(argv[1], argv[2]); // Run the grid planner return 0; };