saving work until i come back

This commit is contained in:
Ksan 2025-08-03 14:51:00 +02:00
parent 167dea9451
commit aa2b21e515
3 changed files with 280 additions and 163 deletions

View File

@ -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<City> path = new ArrayList<>();
List<Integer> 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<City> cities = JsonParser.parseCities(selectedFile.toString(), "stations");
List<City> cities = JsonParser.parseCities("transport_data.json", "stations");
List<Departure> 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

View File

@ -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<String> visitedRoutes = new HashSet<>();
public static PriorityQueue<PathResult> topPaths = new PriorityQueue<>(5, Comparator.comparingDouble(PathResult::getCost).reversed());
public static PriorityQueue<PathResult> topPaths =
new PriorityQueue<>(5, Comparator.comparingDouble(PathResult::getCost).reversed());
public static void main(String[] args) {
List<City> cities = JsonParser.parseCities("transport_data.json", "stations");
List<Departure> 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<Location, Double> 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<City> path = new ArrayList<>();
List<Integer> 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<Integer> 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<City> path,
double totalCost,
LocalTime currentTime,
List<Integer> departures) {
City currentCity,
City endCity,
List<City> path,
double totalCost,
LocalTime currentTime,
List<Integer> 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<PathResult> topPaths =
new PriorityQueue<>(Comparator.comparingDouble(PathResult::getCost).reversed());
private final AtomicInteger pathIdGenerator = new AtomicInteger(1); // Start from 1
private final Set<String> uniquePathKeys = new HashSet<>();
/*
private PriorityQueue<PathResult> topPaths =
new PriorityQueue<>(Comparator.comparingDouble(PathResult::getCost).reversed());
private final AtomicInteger pathIdGenerator = new AtomicInteger(1); // Start from 1
private final Set<String> uniquePathKeys = new HashSet<>();
private int makeDeparturePathHash(Set<Departure> 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<Integer> uniquePathHashes = new HashSet<Integer>();
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<City> initialPath = new ArrayList<>();
Set<Departure> usedDepartures = new HashSet<>();
usedDepartures.add(dep);
initialPath.add(start); // include start
initialPath.add(next); // move to first destination
List<Integer> initialDepartures = new ArrayList<>();
initialDepartures.add(dep.getIdCounter());
Set<Location> visited = new HashSet<>();
visited.add(start.getLocation());
findPaths(
next,
end,
initialPath,
usedDepartures,
initialDepartures,
duration,
arrivalTime,
visited);
private int makeDeparturePathHash(Set<Departure> 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<City> pathSoFar,
Set<Departure> usedDepartures,
List<Integer> departuresSoFar,
double totalCost,
LocalTime currentTime,
Set<Location> 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<Integer> uniquePathHashes = new HashSet<Integer>();
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<City> initialPath = new ArrayList<>();
Set<Departure> usedDepartures = new HashSet<>();
usedDepartures.add(dep);
initialPath.add(start);
initialPath.add(next);
List<Integer> initialDepartures = new ArrayList<>();
initialDepartures.add(dep.getIdCounter());
Set<Location> visited = new HashSet<>();
visited.add(start.getLocation());
findPaths(
next,
end,
initialPath,
usedDepartures,
initialDepartures,
duration,
arrivalTime,
visited);
}
}
public void findPaths(
City current,
City end,
List<City> pathSoFar,
Set<Departure> usedDepartures,
List<Integer> departuresSoFar,
double totalCost,
LocalTime currentTime,
Set<Location> 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<Location, Double> 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<PathResult> getAllPaths() {
return allPaths;
}
@ -359,4 +391,8 @@ public class Graph {
public City getCity(Location loc) {
return matrix[loc.getX()][loc.getY()];
}
public static PriorityQueue<PathResult> getTopPaths() {
return topPaths;
}
}

View File

@ -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;
}