package inport.ConversionUtils; import inport.Main; import inport.TaskCase; import java.io.*; import java.util.ArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; import java.util.stream.Collectors; public class Solver { public enum SolverName { Undefined(""), Chuffed ("Chuffed"), OrTools ("OrTools"); public final String text; public static final String legalValues = "\"Chuffed\", \"OrTools\""; SolverName(String text) { this.text = text; } public static Solver.SolverName fromString(String text) { for (Solver.SolverName t : Solver.SolverName.values()) { if (t.text.equalsIgnoreCase(text)) { return t; } } return Undefined; } } private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private Boolean isDestroyed; private TaskCase task; private final String constraintName; private final BiConsumer converterToMiniZincFormat; private final BiConsumer interpreter; private String tempDir = "temp_data"; private int timeLimitS; private String flatZincSolver = ""; private SolverName solverName = SolverName.Undefined; private String solverResults = ""; public void setSolverResults(String solverResults) { this.solverResults = solverResults; } public void setTask(TaskCase task) { this.task = task; } public void setTempDir(String tempDir) { this.tempDir = tempDir; } public void setTimeLimitS(int timeLimitS) { this.timeLimitS = timeLimitS; } public void setFlatZincSolver(String flatZincSolver) { this.flatZincSolver = flatZincSolver; } public void setSolverName(String solverName) { this.solverName = SolverName.fromString(solverName); } public String getFlatZincSolver() { return flatZincSolver; } public BiConsumer getConverterToMiniZincFormat() { return converterToMiniZincFormat; } public String getConstraintName() { return constraintName; } public Solver(String constraintName, BiConsumer converterToMiniZincFormat, BiConsumer interpreter) { this.constraintName = constraintName; this.converterToMiniZincFormat = converterToMiniZincFormat; this.interpreter = interpreter; } public String solve() { File directory = new File(tempDir); if (!directory.exists()) { directory.mkdir(); } File candidate = new File(tempDir + "/1/"); final int limitOfDirectories = 1000000; int i = 1; while (i < limitOfDirectories) { candidate = new File(tempDir + "/" + i + "/"); if (candidate.mkdir()) { break; } i++; } if (i == limitOfDirectories) { return "Impossible to create files from " + tempDir + "/1/ to " + tempDir + "/" + (limitOfDirectories - 1) + "/ ."; } tempDir = tempDir + "/" + i + "/"; String minizincData = tempDir + "minizinc_data.dzn"; if (solverResults.isEmpty()) { solverResults = tempDir + "solver_results.txt"; } String constraints = tempDir + "constraints.mzn"; String flatZincConstraints = tempDir + "model.fzn"; new File(solverResults).delete(); try { try (FileWriter res = new FileWriter(constraints)) { BufferedReader reader = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream("/constraints/" + constraintName))); String line; while ((line = reader.readLine()) != null) { res.write(line + "\n"); } } converterToMiniZincFormat.accept(task, minizincData); ProcessBuilder pb; boolean isResultsInOutput; Process solverProcess; switch (solverName) { case Chuffed: { pb = new ProcessBuilder("minizinc", "--solver", "Chuffed", constraints, minizincData, "-o", solverResults); isResultsInOutput = false; solverProcess = pb.start(); break; } case OrTools: { isResultsInOutput = true; { ProcessBuilder lPB = new ProcessBuilder("mzn2fzn", "-o", flatZincConstraints, constraints, minizincData); Process process = lPB.start(); process.waitFor(); BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String output = br.lines().collect(Collectors.joining("\n")); if (output.trim().equals("=====UNSATISFIABLE=====")) { task.setSolution(new ArrayList<>()); task.setSolution_result(-1); return ""; } } pb = new ProcessBuilder("external_tools/or-tools_flatzinc_Ubuntu-18.04-64bit_v7.2.6977/bin/fzn-or-tools", flatZincConstraints); solverProcess = pb.start(); break; } case Undefined: { if (flatZincSolver.isEmpty()) { return "FlatZinc solver not defined!"; } isResultsInOutput = true; { ProcessBuilder lPB = new ProcessBuilder("mzn2fzn", "-o", flatZincConstraints, constraints, minizincData); Process process = lPB.start(); process.waitFor(); BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String output = br.lines().collect(Collectors.joining("\n")); if (output.trim().equals("=====UNSATISFIABLE=====")) { task.setSolution(new ArrayList<>()); task.setSolution_result(-1); return ""; } } solverProcess = new ProcessBuilder(flatZincSolver, flatZincConstraints).start(); break; } default: return "Undefined solver."; } lock.lock(); isDestroyed = false; lock.unlock(); final Process process = solverProcess; final BufferedReader br = new BufferedReader(new InputStreamReader(solverProcess.getInputStream())); Thread killer = new Thread(() -> { lock.lock(); long start = System.currentTimeMillis(); try { while (process.isAlive()) { long currentTimeMillis = System.currentTimeMillis(); if (currentTimeMillis - start > timeLimitS * 1000) { process.destroyForcibly(); isDestroyed = true; break; } condition.await(timeLimitS * 1000 - (currentTimeMillis - start), TimeUnit.MILLISECONDS); } } catch (InterruptedException ex) { isDestroyed = true; process.destroyForcibly(); } finally { lock.unlock(); } }); killer.start(); String output = br.lines().collect(Collectors.joining("\n")); int exitCode = solverProcess.waitFor(); lock.lock(); try { condition.signal(); } finally { lock.unlock(); } killer.join(); assert exitCode == 0; lock.lock(); try { if (isDestroyed) { return "Time limit exceeded."; } }finally { lock.unlock(); } if (isResultsInOutput) { try (FileWriter res = new FileWriter(solverResults)) { for (byte b : output.getBytes("UTF8")) { res.write(b); } } interpreter.accept(task, solverResults); } else { // String errors = br.lines().collect(Collectors.joining("\n")); // // System.out.println("output : " + output); // System.out.println("errors : " + errors); if (output.trim().equals("=====UNSATISFIABLE=====")) { task.setSolution(new ArrayList<>()); task.setSolution_result(-1); } else { interpreter.accept(task, solverResults); } } } catch (UncheckedIOException | IOException | InterruptedException | ParserException ex) { return ex.getMessage(); } finally { // removeDirectory(candidate); } return ""; } private static void removeDirectory(File dir) { if (dir.isDirectory()) { File[] files = dir.listFiles(); if (files != null && files.length > 0) { for (File aFile : files) { removeDirectory(aFile); } } } dir.delete(); } }