From aa2b21e515a39ba34d9134fc724ab05bf455c9bf Mon Sep 17 00:00:00 2001 From: Ksan Date: Sun, 3 Aug 2025 14:51:00 +0200 Subject: [PATCH] saving work until i come back --- .../controller/MainController.java | 65 +++- .../ksan/travelpathoptimizer/graph/Graph.java | 322 ++++++++++-------- src/main/java/module-info.java | 56 ++- 3 files changed, 280 insertions(+), 163 deletions(-) diff --git a/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java b/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java index 321e12b..bd3cad9 100644 --- a/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java +++ b/src/main/java/dev/ksan/travelpathoptimizer/controller/MainController.java @@ -1,19 +1,24 @@ package dev.ksan.travelpathoptimizer.controller; import dev.ksan.travelpathoptimizer.app.TransportDataGenerator; +import dev.ksan.travelpathoptimizer.graph.Graph; import dev.ksan.travelpathoptimizer.model.City; import dev.ksan.travelpathoptimizer.model.Departure; import dev.ksan.travelpathoptimizer.model.Location; import dev.ksan.travelpathoptimizer.service.CityManager; import dev.ksan.travelpathoptimizer.util.JsonParser; import java.io.File; +import java.time.LocalTime; +import java.util.ArrayList; import java.util.List; import javafx.fxml.FXML; import javafx.scene.control.Button; +import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.RowConstraints; import javafx.scene.layout.StackPane; @@ -37,6 +42,7 @@ public class MainController { @FXML private Button startCityButton; @FXML private Button endCityButton; + private Graph graph; City[][] cities; private File selectedFile; @FXML private Button openFileButton; @@ -45,12 +51,51 @@ public class MainController { @FXML private TextField departureTextField; @FXML private VBox randomSideBar; @FXML private VBox mapSideBar; + @FXML private ChoiceBox categoryBox; + @FXML private Button startButton; + + // routeView + @FXML private HBox routeView; + @FXML private Text totalTicketPriceText; + @FXML private Button buyButton; + + @FXML + private void findTopPaths() { + graph.reset(); + if (this.startCity != null && this.endCity != null) { + List path = new ArrayList<>(); + + List departuress = new ArrayList<>(); + LocalTime currentTime = LocalTime.of(1, 0); + double totalCost = 0.0; + + graph.calculateTopPaths( + this.startCity, + this.endCity, + path, + totalCost, + currentTime, + departuress, + categoryBox.getValue().toString()); + System.out.println(graph.getTopPaths().size()); + // Output the top 5 paths + graph.printTopPaths(); + System.out.println(startCity.getName() + endCity.getName()); + } + } @FXML protected void onHelloButtonClick() { welcomeText.setText("Welcome to JavaFX Application!"); } + @FXML + private void showRouteView() { + boolean visible = routeView.isVisible(); + routeView.setVisible(!visible); + routeView.setManaged(!visible); + } + @FXML void showRandomSideBar() { boolean visible = randomSideBar.isVisible(); @@ -155,6 +200,10 @@ public class MainController { @FXML public void initialize() { + + categoryBox.getItems().addAll("time", "price", "hops"); + categoryBox.setValue("time"); + endCityText .textProperty() .addListener( @@ -164,6 +213,8 @@ public class MainController { endCityText.setStyle("-fx-text-fill: black;"); } else { City match = CityManager.getCityByName(newText.trim()); + + System.out.println("Selected end: " + match.getName()); if (match != null) { endCity = match; endCityText.setStyle("-fx-text-fill: green;"); @@ -184,6 +235,8 @@ public class MainController { startCityText.setStyle("-fx-text-fill: black;"); } else { City match = CityManager.getCityByName(newText.trim()); + + System.out.println("Selected start: " + match.getName()); if (match != null) { startCity = match; startCityText.setStyle("-fx-text-fill: green;"); @@ -217,17 +270,25 @@ public class MainController { private void getData() { CityManager.clear(); - List cities = JsonParser.parseCities(selectedFile.toString(), "stations"); + List cities = JsonParser.parseCities("transport_data.json", "stations"); List departures = JsonParser.getDeparturesList("transport_data.json", "departures"); + for (Departure dep : departures) { + for (City city : cities) { + if (dep.getTo().equals(city.getName())) { + dep.setToCity(city); + } + } + } cities = JsonParser.loadDepartures(cities, departures); City[][] map = JsonParser.loadMap("transport_data.json", "countryMap", cities); cities = JsonParser.loadDepartures(cities, departures); for (City city : cities) { - System.out.println(city); CityManager.addCity(city); } this.cities = map; + + this.graph = new Graph(map); } @FXML diff --git a/src/main/java/dev/ksan/travelpathoptimizer/graph/Graph.java b/src/main/java/dev/ksan/travelpathoptimizer/graph/Graph.java index 031963b..e2ce22b 100644 --- a/src/main/java/dev/ksan/travelpathoptimizer/graph/Graph.java +++ b/src/main/java/dev/ksan/travelpathoptimizer/graph/Graph.java @@ -5,10 +5,9 @@ import dev.ksan.travelpathoptimizer.model.Departure; import dev.ksan.travelpathoptimizer.model.Location; import dev.ksan.travelpathoptimizer.service.CityManager; import dev.ksan.travelpathoptimizer.util.JsonParser; +import java.time.Duration; import java.time.LocalTime; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; public class Graph { private City[][] matrix; @@ -16,7 +15,9 @@ public class Graph { private int pathIdCounter = 1; private static int nextid = 0; private static Set visitedRoutes = new HashSet<>(); - public static PriorityQueue topPaths = new PriorityQueue<>(5, Comparator.comparingDouble(PathResult::getCost).reversed()); + public static PriorityQueue topPaths = + new PriorityQueue<>(5, Comparator.comparingDouble(PathResult::getCost).reversed()); + public static void main(String[] args) { List cities = JsonParser.parseCities("transport_data.json", "stations"); List departures = JsonParser.getDeparturesList("transport_data.json", "departures"); @@ -32,19 +33,16 @@ public class Graph { Graph graph = new Graph(map); cities = JsonParser.loadDepartures(cities, departures); - System.out.println(cities.getFirst().getName() + " do " + cities.getLast().getName()); for (City city : cities) { CityManager.addCity(city); } + System.out.println(cities.getFirst().getName() + " do " + cities.getLast().getName()); Map result = graph.calculateShortestPath(cities.getFirst(), cities.getLast(), "time"); System.out.println( cities.get(1).getName() + " = " + result.get(cities.getLast().getLocation())); - - - /* graph.calculateTopPaths(cities.getFirst(), cities.getLast()); @@ -70,15 +68,23 @@ public class Graph { List path = new ArrayList<>(); List departuress = new ArrayList<>(); - LocalTime currentTime = LocalTime.of(1, 0); // Assume starting at 8:00 AM + LocalTime currentTime = LocalTime.of(1, 0); double totalCost = 0.0; calculateTopPaths( - cities.getFirst(), cities.getLast(), path, totalCost, currentTime, departuress); + cities.getFirst(), cities.getLast(), path, totalCost, currentTime, departuress, "time"); System.out.println(topPaths.size()); // Output the top 5 paths - printTopPaths(); } + printTopPaths(); + } + public void reset() { + topPaths.clear(); + pathIdCounter = 1; + nextid = 0; + allPaths.clear(); + visitedRoutes.clear(); + } public static void addToTopPaths(PathResult newPath) { String routeHash = generateRouteHash(newPath.getDeparturesUsed()); @@ -100,7 +106,6 @@ public class Graph { } } - private static String generateRouteHash(List departures) { StringBuilder sb = new StringBuilder(); for (Integer depId : departures) { @@ -108,20 +113,30 @@ public class Graph { } return sb.toString(); } + public static void calculateTopPaths( - City currentCity, - City endCity, - List path, - double totalCost, - LocalTime currentTime, - List departures) { + City currentCity, + City endCity, + List path, + double totalCost, + LocalTime currentTime, + List departures, + String type) { if (currentCity.getLocation().equals(endCity.getLocation())) { - addToTopPaths(new PathResult(nextid++, new ArrayList<>(path), new ArrayList<>(departures), totalCost, currentTime)); + addToTopPaths( + new PathResult( + nextid++, + new ArrayList<>(path), + new ArrayList<>(departures), + totalCost, + currentTime)); + return; } for (Departure dep : currentCity.getDestinations()) { + double cost = 0.0; if (dep.getDepartureTime().isBefore(currentTime)) continue; City nextCity; if (path.contains(dep.getDestinationCity())) { @@ -130,21 +145,41 @@ public class Graph { nextCity = dep.getDestinationCity(); } LocalTime arrivalTime = dep.getDepartureTime().plusMinutes(dep.getDuration()); + Duration duration = Duration.between(currentTime, arrivalTime); + duration = duration.abs(); - if (topPaths.size() >= 5 && totalCost + dep.getDuration() > topPaths.peek().getCost()) { + if (type.equals("time")) { + // cost += dep.getDuration(); + cost += duration.toMinutes(); + } else if (type.equals("price")) { + cost += dep.getPrice(); + } else if (type.equals("hops")) { + cost++; + } else { + return; + } + + if (topPaths.size() >= 5 && totalCost + cost > topPaths.peek().getCost()) { return; } path.add(nextCity); departures.add(dep.getIdCounter()); - calculateTopPaths( - nextCity, endCity, path, totalCost + dep.getDuration(), arrivalTime, departures); + calculateTopPaths(nextCity, endCity, path, totalCost + cost, arrivalTime, departures, type); departures.remove(departures.size() - 1); path.remove(path.size() - 1); + if (type.equals("hops")) { + + cost--; + } } } public static void printTopPaths() { + if (topPaths.isEmpty()) { + System.out.println("No Paths"); + return; + } System.out.println("Top 5 Paths:"); int rank = 5; while (!topPaths.isEmpty()) { @@ -158,138 +193,137 @@ public class Graph { } } -/* - private PriorityQueue topPaths = - new PriorityQueue<>(Comparator.comparingDouble(PathResult::getCost).reversed()); - private final AtomicInteger pathIdGenerator = new AtomicInteger(1); // Start from 1 - private final Set uniquePathKeys = new HashSet<>(); + /* + private PriorityQueue topPaths = + new PriorityQueue<>(Comparator.comparingDouble(PathResult::getCost).reversed()); + private final AtomicInteger pathIdGenerator = new AtomicInteger(1); // Start from 1 + private final Set uniquePathKeys = new HashSet<>(); - private int makeDeparturePathHash(Set departures) { - // A simple approach: sum of hashes or use Objects.hash(...) - return departures.stream() - .mapToInt( - dep -> - Objects.hash( - dep.getDepartureTime() - .toSecondOfDay(), // convert time to int seconds for consistency - dep.getDestinationCity() - .getLocation(), // assuming Location implements hashCode properly - Double.valueOf(dep.getDuration()).hashCode())) - .sorted() // sort to avoid order affecting hash - .reduce(1, (a, b) -> 31 * a + b); // combine with a hash combiner - } - - - - - private HashSet uniquePathHashes = new HashSet(); - public void calculateTopPaths(City start, City end) { - topPaths.clear(); - uniquePathKeys.clear(); - pathIdGenerator.set(1); - - for (Departure dep : start.getDestinations()) { - City next = dep.getDestinationCity(); - double duration = dep.getDuration(); - LocalTime depTime = dep.getDepartureTime(); - LocalTime arrivalTime = depTime.plusMinutes((long) duration); - - List initialPath = new ArrayList<>(); - Set usedDepartures = new HashSet<>(); - usedDepartures.add(dep); - - initialPath.add(start); // include start - initialPath.add(next); // move to first destination - List initialDepartures = new ArrayList<>(); - initialDepartures.add(dep.getIdCounter()); - Set visited = new HashSet<>(); - visited.add(start.getLocation()); - - findPaths( - next, - end, - initialPath, - usedDepartures, - initialDepartures, - duration, - arrivalTime, - visited); + private int makeDeparturePathHash(Set departures) { + return departures.stream() + .mapToInt( + dep -> + Objects.hash( + dep.getDepartureTime() + .toSecondOfDay(), + dep.getDestinationCity() + .getLocation(), + Double.valueOf(dep.getDuration()).hashCode())) + .sorted() + .reduce(1, (a, b) -> 31 * a + b); } - } - public void findPaths( - City current, - City end, - List pathSoFar, - Set usedDepartures, - List departuresSoFar, - double totalCost, - LocalTime currentTime, - Set visitedCities - ) { - if (visitedCities.contains(current.getLocation())) return; - visitedCities.add(current.getLocation()); - if (current.getLocation().equals(end.getLocation())) { - int pathHash = makeDeparturePathHash(usedDepartures); - if (uniquePathHashes.contains(pathHash)) { - visitedCities.remove(current.getLocation()); + + private HashSet uniquePathHashes = new HashSet(); + public void calculateTopPaths(City start, City end) { + topPaths.clear(); + uniquePathKeys.clear(); + pathIdGenerator.set(1); + + for (Departure dep : start.getDestinations()) { + City next = dep.getDestinationCity(); + double duration = dep.getDuration(); + LocalTime depTime = dep.getDepartureTime(); + LocalTime arrivalTime = depTime.plusMinutes((long) duration); + + List initialPath = new ArrayList<>(); + Set usedDepartures = new HashSet<>(); + usedDepartures.add(dep); + + initialPath.add(start); + initialPath.add(next); + List initialDepartures = new ArrayList<>(); + initialDepartures.add(dep.getIdCounter()); + Set visited = new HashSet<>(); + visited.add(start.getLocation()); + + findPaths( + next, + end, + initialPath, + usedDepartures, + initialDepartures, + duration, + arrivalTime, + visited); + } + } + + public void findPaths( + City current, + City end, + List pathSoFar, + Set usedDepartures, + List departuresSoFar, + double totalCost, + LocalTime currentTime, + Set visitedCities + ) { + if (visitedCities.contains(current.getLocation())) return; + + visitedCities.add(current.getLocation()); + + if (current.getLocation().equals(end.getLocation())) { + int pathHash = makeDeparturePathHash(usedDepartures); + if (uniquePathHashes.contains(pathHash)) { + visitedCities.remove(current.getLocation()); + return; + } + + uniquePathHashes.add(pathHash); + int pathId = pathIdGenerator.getAndIncrement(); + topPaths.add( + new PathResult( + pathId, + new ArrayList<>(pathSoFar), + new ArrayList<>(departuresSoFar), + totalCost, + currentTime)); + + if (topPaths.size() > 5) topPaths.poll(); + + visitedCities.remove(current.getLocation()); // backtrack return; } - uniquePathHashes.add(pathHash); - int pathId = pathIdGenerator.getAndIncrement(); - topPaths.add( - new PathResult( - pathId, - new ArrayList<>(pathSoFar), - new ArrayList<>(departuresSoFar), - totalCost, - currentTime)); + for (Departure dep : current.getDestinations()) { + if (usedDepartures.contains(dep)) continue; + if (dep.getDepartureTime().isBefore(currentTime)) continue; - if (topPaths.size() > 5) topPaths.poll(); + City next = dep.getDestinationCity(); + if (visitedCities.contains(next.getLocation())) continue; // skip if already visited + + LocalTime arrivalTime = dep.getDepartureTime().plusMinutes((long) dep.getDuration()); + + usedDepartures.add(dep); + departuresSoFar.add(dep.getIdCounter()); + pathSoFar.add(next); + if (!(topPaths.peek() == null)) { + if (totalCost + dep.getDuration() > topPaths.peek().getCost()) { + continue; + } + } + findPaths( + next, + end, + pathSoFar, + usedDepartures, + departuresSoFar, + totalCost + dep.getDuration(), + arrivalTime, + visitedCities); + + // backtrack + pathSoFar.remove(pathSoFar.size() - 1); + departuresSoFar.remove(departuresSoFar.size() - 1); + usedDepartures.remove(dep); + } visitedCities.remove(current.getLocation()); // backtrack - return; } - - for (Departure dep : current.getDestinations()) { - if (usedDepartures.contains(dep)) continue; - if (dep.getDepartureTime().isBefore(currentTime)) continue; - - City next = dep.getDestinationCity(); - if (visitedCities.contains(next.getLocation())) continue; // skip if already visited - - LocalTime arrivalTime = dep.getDepartureTime().plusMinutes((long) dep.getDuration()); - - usedDepartures.add(dep); - departuresSoFar.add(dep.getIdCounter()); - pathSoFar.add(next); - if (!(topPaths.peek() == null)) { - if (totalCost + dep.getDuration() > topPaths.peek().getCost()) { - continue; - } - } - findPaths( - next, - end, - pathSoFar, - usedDepartures, - departuresSoFar, - totalCost + dep.getDuration(), - arrivalTime, - visitedCities); - - // backtrack - pathSoFar.remove(pathSoFar.size() - 1); - departuresSoFar.remove(departuresSoFar.size() - 1); - usedDepartures.remove(dep); - } - - visitedCities.remove(current.getLocation()); // backtrack - } -*/ + */ public Map calculateShortestPath(City startCity, City endCity, String type) { int n = matrix.length; int m = matrix[0].length; @@ -338,8 +372,6 @@ public class Graph { return distances; } - - public List getAllPaths() { return allPaths; } @@ -359,4 +391,8 @@ public class Graph { public City getCity(Location loc) { return matrix[loc.getX()][loc.getY()]; } + + public static PriorityQueue getTopPaths() { + return topPaths; + } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index b579fe7..50b60c2 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,21 +1,41 @@ module dev.ksan.travelpathoptimizer { - requires javafx.controls; - requires javafx.fxml; - requires java.desktop; + requires javafx.controls; + requires javafx.fxml; + requires java.desktop; + requires javafx.graphics; + opens dev.ksan.travelpathoptimizer to + javafx.fxml; + + exports dev.ksan.travelpathoptimizer; + exports dev.ksan.travelpathoptimizer.app; + + opens dev.ksan.travelpathoptimizer.app to + javafx.fxml; + + exports dev.ksan.travelpathoptimizer.controller; + + opens dev.ksan.travelpathoptimizer.controller to + javafx.fxml; + + exports dev.ksan.travelpathoptimizer.model; + + opens dev.ksan.travelpathoptimizer.model to + javafx.fxml; + + exports dev.ksan.travelpathoptimizer.util; + + opens dev.ksan.travelpathoptimizer.util to + javafx.fxml; + + exports dev.ksan.travelpathoptimizer.service; + + opens dev.ksan.travelpathoptimizer.service to + javafx.fxml; + + exports dev.ksan.travelpathoptimizer.graph; + + opens dev.ksan.travelpathoptimizer.graph to + javafx.fxml; +} - opens dev.ksan.travelpathoptimizer to javafx.fxml; - exports dev.ksan.travelpathoptimizer; - exports dev.ksan.travelpathoptimizer.app; - opens dev.ksan.travelpathoptimizer.app to javafx.fxml; - exports dev.ksan.travelpathoptimizer.controller; - opens dev.ksan.travelpathoptimizer.controller to javafx.fxml; - exports dev.ksan.travelpathoptimizer.model; - opens dev.ksan.travelpathoptimizer.model to javafx.fxml; - exports dev.ksan.travelpathoptimizer.util; - opens dev.ksan.travelpathoptimizer.util to javafx.fxml; - exports dev.ksan.travelpathoptimizer.service; - opens dev.ksan.travelpathoptimizer.service to javafx.fxml; - exports dev.ksan.travelpathoptimizer.graph; - opens dev.ksan.travelpathoptimizer.graph to javafx.fxml; -} \ No newline at end of file