diff --git a/.gitignore b/.gitignore index 524f096..64e2fda 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* +/.idea/ diff --git a/Algorithm.java b/Algorithm.java index 8faf249..f70feea 100644 --- a/Algorithm.java +++ b/Algorithm.java @@ -7,6 +7,6 @@ public abstract class Algorithm { } - abstract public void shortestPath(Cities.cities start, Cities.cities end); + abstract public void shortestPath(City start, City end); } diff --git a/Board.java b/Board.java index bfd1e17..e5ffe60 100644 --- a/Board.java +++ b/Board.java @@ -1,99 +1,84 @@ import javax.swing.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Scanner; class Board { private int[][] board,tollBoard; private int size; + public Cities myCities; - private void createGraph() + private void createGraph(String configFileName) { + myCities.getCities(configFileName); + size = myCities.size; + board = new int[size][size]; + tollBoard = new int[size][size]; for (int x = 0; x < size; x++) for (int y = 0; y < size; y++) { board[x][y] = 0; tollBoard[x][y] = 0; } + + // now we read the connections from the config file + File configFile = null; + Scanner reader = null; - // All Possible Connections Between All The Train Station - setBoard(Cities.cities.Emden, Cities.cities.Groningen, 3,3); - setBoard(Cities.cities.Emden, Cities.cities.Lingen, 6,1); - setBoard(Cities.cities.Emden, Cities.cities.Emmen, 4,4); - setBoard(Cities.cities.Groningen, Cities.cities.Emmen, 3,3); - setBoard(Cities.cities.Groningen, Cities.cities.Waddeneilanden,6,4); - setBoard(Cities.cities.Groningen, Cities.cities.Leeuwarden,3,2); - setBoard(Cities.cities.Groningen, Cities.cities.Zwolle,6,4); - setBoard(Cities.cities.Waddeneilanden, Cities.cities.DenHelder,5,4); - setBoard(Cities.cities.Waddeneilanden, Cities.cities.Sneek,3,4); - setBoard(Cities.cities.Waddeneilanden, Cities.cities.Leeuwarden,2,3); - setBoard(Cities.cities.Leeuwarden, Cities.cities.Sneek,1,1); - setBoard(Cities.cities.Sneek, Cities.cities.DenHelder,4,3); - setBoard(Cities.cities.Sneek, Cities.cities.Lelystad,4,3); - setBoard(Cities.cities.DenHelder, Cities.cities.Haarlem,4,2); - setBoard(Cities.cities.Haarlem, Cities.cities.Amsterdam,1,1); - setBoard(Cities.cities.Haarlem, Cities.cities.DenHaag,2,2); - setBoard(Cities.cities.DenHaag, Cities.cities.Rotterdam,1,1); - setBoard(Cities.cities.DenHaag, Cities.cities.Middelburg,6,4); - setBoard(Cities.cities.Middelburg, Cities.cities.Antwerpen,5,3); - setBoard(Cities.cities.Antwerpen, Cities.cities.Rotterdam,5,4); - setBoard(Cities.cities.Antwerpen, Cities.cities.Turnhout,2,2); - setBoard(Cities.cities.Antwerpen, Cities.cities.Aarschot,2,1); - setBoard(Cities.cities.Aarschot, Cities.cities.Hasselt,2,1); - setBoard(Cities.cities.Aarschot, Cities.cities.Liege,5,1); - setBoard(Cities.cities.Liege, Cities.cities.Hasselt,2,1); - setBoard(Cities.cities.Liege, Cities.cities.Maastricht,2,1); - setBoard(Cities.cities.Maastricht, Cities.cities.Hasselt,2,2); - setBoard(Cities.cities.Maastricht, Cities.cities.Eindhoven,4,2); - setBoard(Cities.cities.Maastricht, Cities.cities.Roermond,2,1); - setBoard(Cities.cities.Roermond, Cities.cities.Eindhoven,3,3); - setBoard(Cities.cities.Roermond, Cities.cities.Duisburg,3,2); - setBoard(Cities.cities.Duisburg, Cities.cities.Nijmegen,4,2); - setBoard(Cities.cities.Duisburg, Cities.cities.Enschede,6,1); - setBoard(Cities.cities.Enschede, Cities.cities.Arnhem,5,2); - setBoard(Cities.cities.Enschede, Cities.cities.Zwolle,4,1); - setBoard(Cities.cities.Enschede, Cities.cities.Lingen,3,3); - setBoard(Cities.cities.Lingen, Cities.cities.Emmen,3,2); - setBoard(Cities.cities.Emmen, Cities.cities.Zwolle,4,3); - setBoard(Cities.cities.Zwolle, Cities.cities.Lelystad,2,4); - setBoard(Cities.cities.Zwolle, Cities.cities.Arnhem,4,2); - setBoard(Cities.cities.Lelystad, Cities.cities.Amsterdam,3,3); - setBoard(Cities.cities.Amsterdam, Cities.cities.Rotterdam,4,2); - setBoard(Cities.cities.Amsterdam, Cities.cities.Utrecht,1,1); - setBoard(Cities.cities.Utrecht, Cities.cities.Rotterdam,3,3); - setBoard(Cities.cities.Utrecht, Cities.cities.Arnhem,4,1); - setBoard(Cities.cities.Arnhem, Cities.cities.Nijmegen,1,3); - setBoard(Cities.cities.Nijmegen, Cities.cities.Eindhoven,3,3); - setBoard(Cities.cities.Eindhoven, Cities.cities.DenBosch,1,2); - setBoard(Cities.cities.DenBosch, Cities.cities.Breda,2,2); - setBoard(Cities.cities.Breda, Cities.cities.Rotterdam,2,4); - setBoard(Cities.cities.Breda, Cities.cities.Turnhout,2,2); - setBoard(Cities.cities.Turnhout, Cities.cities.Hasselt,3,3); - } + try { + configFile = new File(configFileName); + reader = new Scanner(configFile); + + } catch (FileNotFoundException e) { + System.out.println("File not found"); + System.exit(0); + } - public Board() { - this.size = Cities.cities.values().length; - board = new int[size][size]; - tollBoard = new int[size][size]; - this.createGraph(); + reader.nextLine(); // skip the first line + + while (reader.hasNext()) { + + String line = reader.nextLine(); + String[] tokens = line.split(","); + City from = myCities.lookup(tokens[0].trim()); + if (from == null) { + System.out.println("Error: _" + tokens[0] + "_ not found"); + System.exit(0); + } + City to = myCities.lookup(tokens[1].trim()); + if (to == null) { + System.out.println("Error: _" + tokens[1] + "_ not found"); + System.exit(0); + } + System.out.println("Adding connection: " + tokens[0] + " to " + tokens[1] + " cost: " + tokens[2] + " toll: " + tokens[3]); + + setBoard(from.index, to.index, Integer.parseInt(tokens[2]), Integer.parseInt(tokens[3])); + } } - + Board(String configfileName) { + super(); + myCities = new Cities(); + createGraph(configfileName); + } - public void setBoard(Cities.cities x, Cities.cities y, int value, int bridgeTool) { + public void setBoard(int x, int y, int value, int bridgeToll) { // Used to set the distance between 2 cities // First version of this program we will use just the distance. Once we have that working // We can switch the int to a class that contains the distance and the colours of the route and cost - board[x.ordinal()][y.ordinal()] = value; - board[y.ordinal()][x.ordinal()] = value; - tollBoard[x.ordinal()][y.ordinal()] = bridgeTool; - tollBoard[y.ordinal()][x.ordinal()] = bridgeTool; + board[x][y] = value; + board[y][x] = value; + tollBoard[x][y] = bridgeToll; + tollBoard[y][x] = bridgeToll; } - public int getBoard(Cities.cities x, Cities.cities y) { - return board[x.ordinal()][y.ordinal()]; + public int getBoard(City x, City y) { + return board[x.index][y.index]; } - public int getBridgeToll(Cities.cities x, Cities.cities y) { - return tollBoard[x.ordinal()][y.ordinal()]; + public int getBridgeToll(City x, City y) { + return tollBoard[x.index][y.index]; } public void printBoard() { diff --git a/BruteForce.java b/BruteForce.java index d948fa1..53cf103 100644 --- a/BruteForce.java +++ b/BruteForce.java @@ -10,6 +10,7 @@ public class BruteForce extends Algorithm { private List paths; + BruteForce(Board b) { super(b); paths = new ArrayList(); @@ -37,17 +38,17 @@ public void listPaths() for (CostPath cp :paths) { System.out.print("Cost: " + cp.cost); System.out.print(" Path: "); - for (Cities.cities c : cp.path) { - System.out.print(Cities.names[c.ordinal()]+"-"); + for (City c : cp.path) { + System.out.print(c.name +"-"); } System.out.println(); } } - public void shortestPath(Cities.cities start, Cities.cities end) { - - List visited = new ArrayList(); + public void shortestPath(City start, City end) { + + List visited = new ArrayList<>(); visited.add(start); long startTime = System.nanoTime(); shortestPath(start, end, 0, visited, 0); @@ -57,11 +58,11 @@ public void shortestPath(Cities.cities start, Cities.cities end) { return; } - public void shortestPath(Cities.cities start, Cities.cities end, int cost, List visited, int level) { + public void shortestPath(City start, City end, int cost, List visited, int level) { // System.out.println("BruteForce Shortest Path: level " + level); // Iterate over all cities - for (Cities.cities c : Cities.cities.values()) { + for (City c : b.myCities.cities) { // The cost to go from start to the next step (c) int nextStep = b.getBoard(start, c); @@ -71,13 +72,11 @@ public void shortestPath(Cities.cities start, Cities.cities end, int cost, List< // This is crucial step, otherwise the recursion never ends // DO nothing } - else if (nextStep > 0 && c == end) { + else if (nextStep > 0 && c == end) { // If the cost != 0 and c is the end, then we are there // We have reached the end // Return the cost and the path - CostPath cp = new CostPath(); - cp.cost = cost + nextStep; - cp.path = new ArrayList(visited); + CostPath cp = new CostPath(cost + nextStep,new ArrayList(visited)); cp.path.add(end); paths.add(cp); //System.out.println("Found path with cost " + cp.cost); @@ -87,19 +86,14 @@ else if (nextStep > 0) // There is a route between start and c // Recurse to find the all paths - ListnextVisited = new ArrayList(visited); + ListnextVisited = new ArrayList(visited); nextVisited.add(c); shortestPath(c, end, cost+nextStep, nextVisited, level + 1); } - } // we have tried all cities. Return return; - } - - - } \ No newline at end of file diff --git a/Cities.java b/Cities.java index 0d4e241..82fa6da 100644 --- a/Cities.java +++ b/Cities.java @@ -1,16 +1,66 @@ +import javax.crypto.Cipher; +import java.io.*; +import java.util.*; +import java.lang.System; + public class Cities { - - - static enum cities { Emden, Groningen, Lingen, Emmen, Waddeneilanden, Leeuwarden, Sneek, DenHelder, Zwolle, - Lelystad, Enschede, Haarlem, Amsterdam, Utrecht, Arnhem, Nijmegen, DenBosch, Maastricht, Rotterdam, DenHaag, - Eindhoven, Duisburg, Breda, Antwerpen, Liege, Turnhout, Roermond, Middelburg, Aarschot, Hasselt }; - static String[] names = { "Emden", "Groningen", "Lingen", "Emmen", "Waddeneilanden", "Leeuwarden", "Sneek", "Den Helder", "Zwolle", - "Lelystad", "Enschede", "Haarlem", "Amsterdam", "Utrecht", "Arnhem", "Nijmegen", "Den Bosch", "Maastricht", "Rotterdam", "Den Haag", - "Eindhoven", "Duisburg", "Breda", "Antwerpen", "Liège", "Turnhout", "Roermond", "Middelburg", "Aarschot", "Hasselt"}; - -} + // TODO: Next step: Remove the enum completely and change the rest of the classes to + // use the cityCount() function instead of cities.length + // All instances of Cities.cities need to be replaced with a for loop and an int + // Need to figure out how to acces Cities through BruteForce + + List names = new ArrayList<>(); + + public int size; + + public List cities = new ArrayList<>(); + + public int cityCount() { + return cities.size(); + } + + public City lookup(String string) // Finds the city that the string is related to + { + for (City c: cities) { + if (Objects.equals(string, c.name)) + return c; + } + return null; + } + public void getCities(String configFileName){ + // We read the cities from the config file here + System.out.println("Reading config file: " + configFileName); + File configFile = null; + Scanner reader = null; + + try { + configFile = new File(configFileName); + reader = new Scanner(configFile); + + } catch (FileNotFoundException e) { + System.out.println("File not found"); + System.exit(0); + } + + if (reader.hasNext()) { + // The first line has the list of cities + String line = reader.nextLine(); + String[] str = line.split(","); + for (String s : str) { + names.add(s.trim()); + } + size = names.size(); + System.out.println( "Cities: " ); + for (int i = 0; i < names.size(); i++) { + City c = new City (i,names.get(i).trim()); + cities.add(c); + System.out.print(c.name + ", "); + } + } + System.out.println("Reading connections"); - \ No newline at end of file + } +} \ No newline at end of file diff --git a/City.java b/City.java new file mode 100644 index 0000000..03b0bc0 --- /dev/null +++ b/City.java @@ -0,0 +1,8 @@ +public class City { + public int index; + public String name; + public City(int index, String name){ + this.index = index; + this.name = name; + } +} diff --git a/Colour.java b/Colour.java deleted file mode 100644 index ea714ac..0000000 --- a/Colour.java +++ /dev/null @@ -1,3 +0,0 @@ -public class Colour { - enum colour{ yellow, pink, white, red, green, blue, orange, gray, black} -} diff --git a/CostPath.java b/CostPath.java index f70ccda..b8d41a7 100644 --- a/CostPath.java +++ b/CostPath.java @@ -2,5 +2,9 @@ public class CostPath { public int cost; - public List path; + public List path; + public CostPath(int cost,List path){ + this.path = path; + this.cost = cost; + } } diff --git a/Dijkstra.java b/Dijkstra.java index 5466e2c..1720f92 100644 --- a/Dijkstra.java +++ b/Dijkstra.java @@ -1,5 +1,4 @@ import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class Dijkstra extends Algorithm{ @@ -7,65 +6,62 @@ public class Dijkstra extends Algorithm{ Dijkstra(Board b) { super(b); } + Cities myCities = b.myCities; - private List unvisited= new ArrayList(); - private CostPath[] paths = new CostPath[Cities.cities.values().length]; + private List unvisited= new ArrayList(); + private CostPath[] paths = new CostPath[myCities.names.size()]; - - public void shortestPath(Cities.cities start, Cities.cities end) { - + public void shortestPath(City start, City end) { // Add all cities to the unvisited list - unvisited.addAll(Arrays.asList(Cities.cities.values())); + unvisited = new ArrayList<>(myCities.cities); // Initialize all distances to infinity - for (Cities.cities x : Cities.cities.values()) + for (int i =0 ; i < myCities.cityCount(); i++) { - paths[x.ordinal()] = new CostPath(); - paths[x.ordinal()].cost = Integer.MAX_VALUE; - paths[x.ordinal()].path = new ArrayList(); - paths[x.ordinal()].path.add(start); - + paths[i] = new CostPath(Integer.MAX_VALUE, new ArrayList()); + paths[i].path.add(start); } - // Set the distance to the start city to 0 - paths[start.ordinal()].cost = 0; + paths[start.index].cost = 0; unvisited.remove(start); long startTime = System.nanoTime(); iterateShortestPath(start, end); long endTime = System.nanoTime(); - - System.out.println("Shortest Path: " + paths[end.ordinal()].cost + " : " + paths[end.ordinal()].path); + + List path = new ArrayList<>(); + for (City c: paths[end.index].path) { + path.add(c.name); + } + System.out.println("Shortest Path: " + paths[end.index].cost + " : " + path); System.out.println("This Path Will Cost : " + getTollCost(end) + " Bridge Toll Token(s)"); System.out.println("Dijkstra Shortest Path calculation time: " + (endTime-startTime) + "ns"); } - public void iterateShortestPath(Cities.cities start, Cities.cities end) + public void iterateShortestPath(City start, City end) { // Update the shortest path to all cities reachable from start - for (Cities.cities c : unvisited) + for (City c : unvisited) { int cost = b.getBoard(start, c); if (cost > 0) { - if (paths[c.ordinal()].cost > cost + paths[start.ordinal()].cost) + if (paths[c.index].cost > cost + paths[start.index].cost) { - paths[c.ordinal()].cost = cost + paths[start.ordinal()].cost; - paths[c.ordinal()].path = new ArrayList(paths[start.ordinal()].path); - paths[c.ordinal()].path.add(c); + paths[c.index].cost = cost + paths[start.index].cost; + paths[c.index].path = new ArrayList(paths[start.index].path); + paths[c.index].path.add(c); } } } - - // Find the next city to visit - Cities.cities next = unvisited.get(0); // Need to give it a possible value - for (Cities.cities c : unvisited) + City next = unvisited.get(0); // Need to give it a possible value + for (City c : unvisited) { - if (paths[c.ordinal()].cost < paths[next.ordinal()].cost) + if (paths[c.index].cost < paths[next.index].cost) { next = c; } @@ -78,10 +74,10 @@ public void iterateShortestPath(Cities.cities start, Cities.cities end) } iterateShortestPath(next, end); } - public int getTollCost( Cities.cities end){ + public int getTollCost( City end){ int cost = 0; - for(int i = 0; i <= (paths[end.ordinal()].path.size() - 2) ; i++){ - cost += b.getBoard(paths[end.ordinal()].path.get(i), paths[end.ordinal()].path.get(i+1)); + for(int i = 0; i < (paths[end.index].path.size() - 1) ; i++){ + cost += b.getBoard(paths[end.index].path.get(i), paths[end.index].path.get(i+1)); } return cost; } diff --git a/DijkstraToll.java b/DijkstraToll.java index 21a21f8..f1c6698 100644 --- a/DijkstraToll.java +++ b/DijkstraToll.java @@ -1,66 +1,65 @@ -import java.lang.reflect.GenericDeclaration; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.concurrent.Callable; public class DijkstraToll extends Algorithm{ DijkstraToll(Board b) { super(b); } - private List unvisited= new ArrayList(); - private CostPath[] tollPath = new CostPath[Cities.cities.values().length]; + Cities myCities = b.myCities; - public void shortestPath(Cities.cities start, Cities.cities end) { + private List unvisited= new ArrayList(); + private CostPath[] tollPath = new CostPath[myCities.cities.size()]; + + public void shortestPath(City start, City end) { // Add all cities to the unvisited list - unvisited.addAll(Arrays.asList(Cities.cities.values())); + unvisited = new ArrayList<>(myCities.cities); // Initialize all distances to infinity - for (Cities.cities x : Cities.cities.values()) + for (int i =0 ; i < myCities.cityCount(); i++) { - tollPath[x.ordinal()] = new CostPath(); - tollPath[x.ordinal()].cost = Integer.MAX_VALUE; - tollPath[x.ordinal()].path = new ArrayList(); - tollPath[x.ordinal()].path.add(start); + tollPath[i] = new CostPath(Integer.MAX_VALUE, new ArrayList()); + tollPath[i].path.add(start); } // Set the distance to the start city to 0 - tollPath[start.ordinal()].cost = 0; + tollPath[start.index].cost = 0; unvisited.remove(start); long startTime = System.nanoTime(); iterateShortestPath(start, end); long endTime = System.nanoTime(); - System.out.println("Smallest Toll Price: " + tollPath[end.ordinal()].cost + " : " + tollPath[end.ordinal()].path); + List path = new ArrayList<>(); + for (City c: tollPath[end.index].path) { + path.add(c.name); + } + System.out.println("Smallest Toll Price: " + tollPath[end.index].cost + " : " + path); System.out.println("This Path will cost : " + getTrainCost(end) + " train(s)"); System.out.println("Dijkstra Smallest Toll Price calculation time: " + (endTime-startTime) + "ns"); } - public void iterateShortestPath(Cities.cities start, Cities.cities end) { + public void iterateShortestPath(City start, City end) { // Update the shortest path to all cities reachable from start - for (Cities.cities c : unvisited) + for (City c : unvisited) { int cost = b.getBridgeToll(start, c); if (cost > 0) { - if (tollPath[c.ordinal()].cost > cost + tollPath[start.ordinal()].cost) + if (tollPath[c.index].cost > cost + tollPath[start.index].cost) { - tollPath[c.ordinal()].cost = cost + tollPath[start.ordinal()].cost; - tollPath[c.ordinal()].path = new ArrayList(tollPath[start.ordinal()].path); - tollPath[c.ordinal()].path.add(c); + tollPath[c.index].cost = cost + tollPath[start.index].cost; + tollPath[c.index].path = new ArrayList(tollPath[start.index].path); + tollPath[c.index].path.add(c); } } } - - // Find the next city to visit - Cities.cities next = unvisited.get(0); // Need to give it a possible value - for (Cities.cities c : unvisited) + City next = unvisited.get(0); // Need to give it a possible value + for (City c : unvisited) { - if (tollPath[c.ordinal()].cost < tollPath[next.ordinal()].cost) + if (tollPath[c.index].cost < tollPath[next.index].cost) { next = c; } @@ -74,11 +73,11 @@ public void iterateShortestPath(Cities.cities start, Cities.cities end) { iterateShortestPath(next, end); } - public int getTrainCost( Cities.cities end){ + public int getTrainCost( City end){ int cost = 0; //Get the bridge toll from the board add that value to int cost - for(int i = 0; i <= (tollPath[end.ordinal()].path.size() - 2) ; i++){ - cost += b.getBoard(tollPath[end.ordinal()].path.get(i), tollPath[end.ordinal()].path.get(i+1)); + for(int i = 0; i <= (tollPath[end.index].path.size() - 2) ; i++){ + cost += b.getBoard(tollPath[end.index].path.get(i), tollPath[end.index].path.get(i+1)); } return cost; } diff --git a/Main.java b/Main.java index a99c308..c957d5c 100644 --- a/Main.java +++ b/Main.java @@ -1,27 +1,33 @@ - - public class Main { static private Board b; public static void main(String[] args ){ + b = new Board("Netherlands.txt"); + + City start = b.myCities.lookup("Waddeneilanden"); + City end = b.myCities.lookup("Rotterdam"); + + System.out.println("Ticket to ride shortest path calculator"); System.out.println("--------------------------------------------------------------"); // Adds some space - b = new Board(); - BruteForce bf = new BruteForce(b); - bf.shortestPath(Cities.cities.Waddeneilanden, Cities.cities.Emmen); - bf.printShortestPath(); + //BruteForce bf = new BruteForce(b); + // bf.shortestPath("Waddeneilanden", "Emmen"); + //bf.printShortestPath(); System.out.println("--------------------------------------------------------------"); // Adds some space Dijkstra d = new Dijkstra(b); - d.shortestPath(Cities.cities.Waddeneilanden, Cities.cities.Emmen); + d.shortestPath(start, end); + System.out.println("--------------------------------------------------------------"); // Adds some space DijkstraToll dt = new DijkstraToll(b); - dt.shortestPath(Cities.cities.Waddeneilanden, Cities.cities.Emmen); + dt.shortestPath(start, end); + + } } \ No newline at end of file diff --git a/Netherlands.txt b/Netherlands.txt new file mode 100644 index 0000000..509f81e --- /dev/null +++ b/Netherlands.txt @@ -0,0 +1,55 @@ +Emden,Groningen,Lingen,Emmen,Waddeneilanden,Leeuwarden,Sneek,Den-Helder,Zwolle,Lelystad,Enschede,Haarlem,Amsterdam,Utrecht,Arnhem,Nijmegen,Den-Bosch,Maastricht,Rotterdam,Den-Haag,Eindhoven,Duisburg,Breda,Antwerpen,Liège,Turnhout,Roermond,Middelburg,Aarschot,Hasselt + Emden,Groningen,3,3 + Emden,Lingen,6,1 + Emden,Groningen,3,3 + Emden,Lingen,6,1 + Emden,Emmen,4,4 + Groningen,Emmen,3,3 + Groningen,Waddeneilanden,6,4 + Groningen,Leeuwarden,3,2 + Groningen,Zwolle,6,4 + Waddeneilanden,Den-Helder,5,4 + Waddeneilanden,Sneek,3,4 + Waddeneilanden,Leeuwarden,2,3 + Leeuwarden,Sneek,1,1 + Sneek,Den-Helder,4,3 + Sneek,Lelystad,4,3 + Den-Helder,Haarlem,4,2 + Haarlem,Amsterdam,1,1 + Haarlem,Den-Haag,2,2 + Den-Haag,Rotterdam,1,1 + Den-Haag,Middelburg,6,4 + Middelburg,Antwerpen,5,3 + Antwerpen,Rotterdam,5,4 + Antwerpen,Turnhout,2,2 + Antwerpen,Aarschot,2,1 + Aarschot,Hasselt,2,1 + Aarschot,Liège,5,1 + Liège,Hasselt,2,1 + Liège,Maastricht,2,1 + Maastricht,Hasselt,2,2 + Maastricht,Eindhoven,4,2 + Maastricht,Roermond,2,1 + Roermond,Eindhoven,3,3 + Roermond,Duisburg,3,2 + Duisburg,Nijmegen,4,2 + Duisburg,Enschede,6,1 + Enschede,Arnhem,5,2 + Enschede,Zwolle,4,1 + Enschede,Lingen,3,3 + Lingen,Emmen,3,2 + Emmen,Zwolle,4,3 + Zwolle,Lelystad,2,4 + Zwolle,Arnhem,4,2 + Lelystad,Amsterdam,3,3 + Amsterdam,Rotterdam,4,2 + Amsterdam,Utrecht,1,1 + Utrecht,Rotterdam,3,3 + Utrecht,Arnhem,4,1 + Arnhem,Nijmegen,1,3 + Nijmegen,Eindhoven,3,3 + Eindhoven,Den-Bosch,1,2 + Den-Bosch,Breda,2,2 + Breda,Rotterdam,2,4 + Breda,Turnhout,2,2 + Turnhout,Hasselt,3,3 \ No newline at end of file