diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..19b9e6e914d5104afd0e03c031105f1c9708fac7
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,22 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "java",
+            "name": "Current File",
+            "request": "launch",
+            "mainClass": "${file}"
+        },
+        {
+            "type": "java",
+            "name": "cvrp_ls",
+            "request": "launch",
+            "mainClass": "cvrp_ls",
+            "projectName": "optialgos_92db6011",
+            "args": ["C:\\Users\\Jule Behrens\\Documents\\JonahAlgos\\optialgos\\src\\instances\\Loggi-n401-k23.vrp", "taboo_search", "3600", "50"]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 4869a8f9f5e76094828e08696aef53fb9c35dfb3..c78d38a05364e8295cc25f361b9a72d06c6b4ec9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
 # Optimierungsalgorithmen für schwere Systeme WiSe 24/25
 ### Author: Jonah Schierding
 ## Problem Set 1 Aufgabe 4
+## Problem Set 2 Aufgabe 1
 
 
 
@@ -10,4 +11,11 @@ Im src-Ordner liegen die Quelldateien und kompilierten Dateien. Aufruf erfolgt d
 (TYPE : CVRP,
 EDGE_WEIGHT_TYPE : EXPLICIT,
 EDGE_WEIGHT_FORMAT : LOWER_ROW)
-zeigen muss.
\ No newline at end of file
+zeigen muss. 
+
+Das Programm gibt die Kosten für die Initial ermittelte Lösung mit einem Greedy-Approach und die mit basic local search ohne Zeitlimit optimierte Lösung und anschließend die Lösungen selbst im .sol Format aus.
+
+Alternativ kann ein Algorithmus spezifiziert werden mit "java cvrp_ls *absolutePath* *algorithm*". Dabei sind die Optionen für *algorithm* "basic_local_serach", "taboo_search" und "ant_colony".
+Für "taboo_search" und "ant_colony" muss zudem eine maximale Laufzeit für die Optimierung in vollen Sekunden angegeben werden ("java cvrp_ls *absolutePath* *algorithm* *maxTimeInSeconds*"). Für "basic_local_serach" ist das optional. 
+
+"taboo_search" kann optional ein weiterer Parameter mitgegeben werden: Die Anzahl der Taboos. Der Standardwert liegt bei *Anzahl der Knoten/3* abgerundet. Ein möglicher Aufruf wäre: "java cvrp_ls *absolutePath* taboo_search *maxTimeInSeconds* *numberOfTaboos*".
\ No newline at end of file
diff --git a/bin/Algorithms.class b/bin/Algorithms.class
index 8a02c8326f7828299319a119a016f9ad4dec6227..b6ae11b222029b818ae7893b44664a06c8973154 100644
Binary files a/bin/Algorithms.class and b/bin/Algorithms.class differ
diff --git a/bin/Instance.class b/bin/Instance.class
index 8845f4076d27d4ca304ae85e83cfcec3ebd35e7e..bafb11695094f5ed66efed0095c51e5103634fca 100644
Binary files a/bin/Instance.class and b/bin/Instance.class differ
diff --git a/bin/Solution.class b/bin/Solution.class
index d942cbee86cf073aaa077abb18516db858c50190..2cf9957a6dd60cf27a03de8ecc06e93c2f4377c3 100644
Binary files a/bin/Solution.class and b/bin/Solution.class differ
diff --git a/bin/cvrp_ls.class b/bin/cvrp_ls.class
index 4daff393d4b3346ed63729baf9cae5c3fe3ce26c..b881a47e3112097351bffd1753ab79d4097b2e25 100644
Binary files a/bin/cvrp_ls.class and b/bin/cvrp_ls.class differ
diff --git a/src/Algorithms.class b/src/Algorithms.class
index 9e57c751a76bf2586fe1ab06ef7e25d2b8d58588..fb297dc91aaf997c811fb976fbe8cf4dc18fc6f2 100644
Binary files a/src/Algorithms.class and b/src/Algorithms.class differ
diff --git a/src/Algorithms.java b/src/Algorithms.java
index 4e77456f90d47e2aa98e670b0f39d756b47c40ea..590b7d1d279c00ea4516a0b8b404490ee34f01a2 100644
--- a/src/Algorithms.java
+++ b/src/Algorithms.java
@@ -4,27 +4,142 @@ import java.util.Arrays;
 public class Algorithms {
 
     String algorithmType;
-
-    public Algorithms(String algorithmType){
+    int maxRuntimeInSeconds = -1;
+    int numberOfTaboos = -1;
+    public Algorithms(){}
+    public Algorithms(String algorithmType, String[] optionalParams) throws Exception{
         this.algorithmType = algorithmType;
-        if(algorithmType == null || algorithmType.equals("")){
-            this.algorithmType = "primitive_local_search";
+        try {
+            this.maxRuntimeInSeconds = Integer.valueOf(optionalParams[0]);
+        } catch (Exception e) {
+            
+        }
+        //checks if algorithm specific parameters are feasible and assigns them
+        switch (algorithmType) {
+            case "none":
+                this.algorithmType = "basic_local_search";
+                break;
+            case "ant_colony":
+                if(maxRuntimeInSeconds <= 0)
+                    throw new Exception();
+                break;
+            case "taboo_search":
+                try {
+                    this.numberOfTaboos = Integer.valueOf(optionalParams[1]);
+                } catch (Exception e) {
+                    
+                }
+                if(maxRuntimeInSeconds <= 0)
+                    throw new Exception();
+                if(this.numberOfTaboos <= 0)
+                    System.out.println("Using standard value for number of Taboos."
+                );
+                break;
+            default:
+                break;
         }
     }
 
     public Solution generateSolution(Instance instance){
-        return generateInitialSolutionGreedy(instance); 
+            switch (this.algorithmType) {
+                case "greedy":
+                    return generateInitialSolutionGreedy(instance);
+                case "ant_colony":
+                    return generateInitialSolutionAnt(instance);
+                default:
+                    return null;
+            }
     }
 
     public Solution generateSolution(Solution solution){
         switch (this.algorithmType) {
-            case "primitive_local_search":
+            case "basic_local_search":
                 return localSearch(solution);
+            case "greedy":
+                return generateInitialSolutionGreedy(solution.instance);
+            case "taboo_search":
+                if(this.numberOfTaboos <= 0)
+                    numberOfTaboos = solution.instance.getDimension()/3;
+                return tabooSearch(solution);
+            case "ant_colony":
+                return generateInitialSolutionAnt(solution.instance);
             default:
                 return null;
         }
     }
 
+    private Solution tabooSearch(Solution solution) {
+        int[] state = {2,0,0,0};
+        int newBestCount = 0;
+
+        Solution currentSolution = solution;
+        Solution bestSolutionSeen = solution;
+        
+        long start = System.currentTimeMillis();
+        long end = start + this.maxRuntimeInSeconds * 1000;
+        Solution possibleBetterNeighbor = getOtherNeighbor(state, bestSolutionSeen);
+        Solution currentBestNeighbor = possibleBetterNeighbor;
+
+        int currentBestNeighborNodeChanged = 2;
+        
+        ArrayList<Integer> taboos = new ArrayList<>();
+    
+        
+        while(maxRuntimeInSeconds > 0 && System.currentTimeMillis()<end){
+            if(possibleBetterNeighbor.getCost() < currentBestNeighbor.getCost()){
+                currentBestNeighbor = possibleBetterNeighbor;
+                currentBestNeighborNodeChanged = state[0];
+            }
+            //permutate state to get another neighbor which is not taboo
+            state[0]++;
+            while(taboos.contains(state[0])){
+                state[0]++;
+            }
+            if(state[0] > bestSolutionSeen.instance.getDimension()){
+                //System.out.println("reset extracted node");
+                state[0] = 2;
+                state[2]++;
+                if(state[2] > bestSolutionSeen.tours[state[1]].length-1){
+                    //System.out.println("reset position");
+                    state[2] = 0;
+                    state[1]++;
+                    if(state[1] > bestSolutionSeen.tours.length-1){
+                        //System.out.println("reset tours");
+                        state[1] = 0;
+                        state[3]++;
+                        //choose minimal after all neighbors are seen, update taboos, best solution see so far and current solution
+                        if(state[3] == 2){
+                            currentSolution = currentBestNeighbor;
+                            //System.out.println("Made a step, taboos: "+taboos.size());
+                            if(currentSolution.getCost() < bestSolutionSeen.getCost()){
+                                bestSolutionSeen = currentSolution;
+                                
+                                System.out.println("Found new best solution "+(++newBestCount) + ": All "+taboos.size()+" taboos dropped");
+                                taboos.clear();
+                            } else {
+                                taboos.add(currentBestNeighborNodeChanged);
+                                if(taboos.size() > numberOfTaboos){
+                                    taboos.remove(0);
+                                }
+                            }
+                            int[] help = {2,0,0,0};
+                            state = help;
+                            currentBestNeighbor = getOtherNeighbor(state, currentSolution);
+                        }
+                    }
+                }
+            }
+            possibleBetterNeighbor = getOtherNeighbor(state, currentSolution);
+        }
+        System.out.println("terminated by time limit");
+        return bestSolutionSeen;
+    }
+
+    private Solution generateInitialSolutionAnt(Instance instance) {
+        // TODO Auto-generated method stub
+        throw new UnsupportedOperationException("Unimplemented method 'generateInitialSolutionAnt'");
+    }
+
     //primitive greedy Approach
     private Solution generateInitialSolutionGreedy(Instance instance){
         Solution solution = new Solution(instance);
@@ -95,9 +210,17 @@ public class Algorithms {
         int[] state = {2,0,0,0};
         Solution possibleBetterSolution = getOtherNeighbor(state, currentBestSolution);
         int newBestCount = 0;
+        long start = System.currentTimeMillis();
+        long end = start + this.maxRuntimeInSeconds * 1000;
         //tries all neighbors as possibleBetterSolution, if better solution found, change currentBestSolution and search from there 
         //terminates when it cannot find a better solution in its neighbarhood
         while (possibleBetterSolution != null) {
+           
+            //stop if max execution time has exceeded
+            if(maxRuntimeInSeconds > 0 && System.currentTimeMillis()>end){
+                System.out.println("terminated by time limit");
+                break;
+            }
             if(possibleBetterSolution.getCost() < currentBestSolution.getCost()){
                 currentBestSolution = possibleBetterSolution;
                 int[] help = {2,0,0,0};
@@ -105,7 +228,6 @@ public class Algorithms {
                 possibleBetterSolution = getOtherNeighbor(state, currentBestSolution);
                 System.out.println("found new best solution "+(++newBestCount));
             }else{
-
                 //permutate state to get another neighbor
                 state[0]++;
                 if(state[0] > currentBestSolution.instance.getDimension()){
@@ -120,7 +242,9 @@ public class Algorithms {
                             //System.out.println("reset tours");
                             state[1] = 0;
                             state[3]++;
+                            //stop if all neighbors are worse/local optimum is reached
                             if(state[3] == 2){
+                                System.out.println("terminated by local optimum");
                                 break;
                             }
                         }
@@ -128,6 +252,7 @@ public class Algorithms {
                 } 
                 possibleBetterSolution = getOtherNeighbor(state, currentBestSolution);
             }
+            
         }
        
         
diff --git a/src/Instance.class b/src/Instance.class
index a7624a488152f0e44889372417d520df2ea64979..e4611d38b6f7a38653ccd053cf29daf15aa610b4 100644
Binary files a/src/Instance.class and b/src/Instance.class differ
diff --git a/src/Instance.java b/src/Instance.java
index 784d9405faf1d4c51176d0d0c269ebba1b9f2b9c..4a1557cf0d4eb7b0b42f693cc91e5066eb734495 100644
--- a/src/Instance.java
+++ b/src/Instance.java
@@ -13,8 +13,9 @@ public class Instance {
     private Node depot;
     private int optimalCost;
 
-    public  Instance(String fileLocation){
-        //import instance of cvrp from file at fileLocation, lower Marix of distances
+    public  Instance(){}
+    public  Instance(String fileLocation) throws Exception{
+        //import instance of cvrp from file at fileLocation and optimal solutioncost from sol file in same folder, lower Marix of distances
         try {
             File file = new File(fileLocation);
             Scanner scanner = new Scanner(file);
@@ -67,20 +68,19 @@ public class Instance {
               }
               scanner.close();
         } catch (FileNotFoundException e) {
-            System.out.println("An error occurred at import.");
-            e.printStackTrace();
+            System.err.println("An error occurred at import, please check if your .vrp file fulfills the readme-conditions and the path is correct.");
+            throw new Exception();
         }
         try {
             File file = new File(fileLocation.replace(".vrp", ".sol"));
             Scanner scanner = new Scanner(file);
             while (scanner.hasNextLine()) {
                 String data = scanner.nextLine();
-                if(data.contains("COST")) this.optimalCost = Integer.valueOf(data.replace("COST", "").trim());
+                if(data.contains("Cost")) this.optimalCost = Integer.valueOf(data.replace("Cost", "").trim());
               }
               scanner.close();
         } catch (FileNotFoundException e) {
-            System.out.println("An error occurred at import.");
-            e.printStackTrace();
+            System.out.println("No .sol file found for optimal solution.");
         }
     }
 
diff --git a/src/Solution.java b/src/Solution.java
index cdf582310c74dd3493ad3c0239cd444378e71fc0..9d53c54abdc73c92a53b168bf52e69ed906b9d1e 100644
--- a/src/Solution.java
+++ b/src/Solution.java
@@ -23,7 +23,6 @@ public class Solution {
                     }
                     lastNode = node;
                 } catch (Exception e) {
-                    // TODO: handle exception
                    
                 }
                 
@@ -55,4 +54,21 @@ public class Solution {
         copy.tours = toursCopy;
         return copy;
     }
+
+    public String toSol(){
+        String rep = "";
+        for (int i = 0; i < tours.length; i++) {
+           rep += "Route #"+(i+1)+":"; 
+           for (Node node : tours[i]) {
+            try {
+                rep += " "+node.number;
+            } catch (Exception e) {
+                
+            }              
+           } 
+           rep += "\n";
+        }
+        rep += "Cost " + this.getCost();
+        return rep;
+    }
 }
\ No newline at end of file
diff --git a/src/cvrp_ls.class b/src/cvrp_ls.class
index aac62dc9d75652f282e88028984eb0c6e8dacd02..98308e98a6a7e534721ee16364b6e19e9714c6e5 100644
Binary files a/src/cvrp_ls.class and b/src/cvrp_ls.class differ
diff --git a/src/cvrp_ls.java b/src/cvrp_ls.java
index 0a12a27182cd6b2227bdf349f84ec5bc9c2069c9..481c19969c4bc5722126298caad645b160679fda 100644
--- a/src/cvrp_ls.java
+++ b/src/cvrp_ls.java
@@ -3,33 +3,54 @@
 public class cvrp_ls {
     public static void main(String[] args) throws Exception {
         String fileLocation = "";
-        String algorithm ="";
-        int maxRuntimeInSeconds = -1;
+        String algorithm ="none";
+        String[] optionalParams = new String[args.length-2];
+        System.arraycopy(args, 2, optionalParams, 0, args.length-2);
+
         try {
             fileLocation = args[0];
             algorithm = args[1];
-            maxRuntimeInSeconds = Integer.valueOf(args[2]);
-
         } catch (Exception e) {
-            
+            System.out.println("Something seems to be wrong with the given parameters");
+           
         }
 
 
             
   
         //create new instance from file
-        Instance instance = new Instance(fileLocation);
+        Instance instance = new Instance();
+        try {
+            instance = new Instance(fileLocation);
+        } catch (Exception e) {
+            System.err.println("Import of instance failed critically");
+            e.printStackTrace();
+            System.exit(1);
+        }
+        
         //create initial Greedy Solution
-        Algorithms greedy = new Algorithms("greedy");
+        Algorithms greedy = new Algorithms("greedy", optionalParams);
         Solution greedySolution = greedy.generateSolution(instance);
         System.out.println("Greedy done");
 
         //optimize with chosen algorithm
-        Algorithms chosen = new Algorithms(algorithm);
+        Algorithms chosen = new Algorithms();
+        try {
+            chosen = new Algorithms(algorithm, optionalParams);
+        } catch (Exception e) {
+            System.err.println("Not all parameters for this algorithm seem to be right");
+        }
+        
         Solution optimizedSolution = chosen.generateSolution(greedySolution);
 
         //print results
-        System.out.println("Instance name"+" & "+"Cost initial Solution"+" & "+"Cost optimized solution"+" & " + "Cost optimal solution");
-        System.out.println(instance.getName()+" & "+greedySolution.getCost()+" & "+optimizedSolution.getCost()+" & "+instance.getOptimalCost());
+        System.out.println(("\nAfter initial greedy Approach, optimized with "+chosen.algorithmType+" with a maximum runtime of approximately "+chosen.maxRuntimeInSeconds+" seconds!").replace("-1", "unlimited"));
+        System.out.println("Instance name"+" & "+"Cost initial Solution"+" & "+"Cost optimized solution ("+chosen.algorithmType+")"+" & " + "Cost optimal solution (100%)"+ "   \\\\");
+        if(instance.getOptimalCost() > 0)
+            System.out.println(instance.getName()+" & "+greedySolution.getCost()+" ("+(float)greedySolution.getCost()/(float)instance.getOptimalCost()*100+"%) & "+optimizedSolution.getCost()+" ("+(float)optimizedSolution.getCost()/(float)instance.getOptimalCost()*100+"%) & "+instance.getOptimalCost() + "   \\\\");
+        else
+            System.out.println(instance.getName()+" & "+greedySolution.getCost()+" & "+optimizedSolution.getCost()+" & "+ "   \\\\");
+        System.out.println("\n Initial solution:\n"+greedySolution.toSol());
+        System.out.println("\n Optimized solution:\n"+optimizedSolution.toSol());
     }
 }