/* This file is part of Eternity II Editor. * * Eternity II Editor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Eternity II Editor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Eternity II Editor. If not, see <http://www.gnu.org/licenses/>. * * Eternity II Editor project is hosted on SourceForge: * http://sourceforge.net/projects/eternityii/ * and maintained by Yannick Kirschhoffer <alcibiade@alcibiade.org> */ package org.alcibiade.eternity.editor.application; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.alcibiade.eternity.editor.log.ConsoleLog; import org.alcibiade.eternity.editor.model.GridModel; import org.alcibiade.eternity.editor.model.QuadsFormatException; import org.alcibiade.eternity.editor.solver.ClusterManager; import org.alcibiade.eternity.editor.solver.EternitySolver; import org.alcibiade.eternity.editor.solver.SolverFactory; import org.alcibiade.eternity.editor.solver.UnknownSolverException; import org.alcibiade.eternity.editor.solver.path.PathFactory; import org.alcibiade.eternity.editor.solver.path.PathProvider; import org.alcibiade.eternity.editor.solver.path.UnknownPathException; import org.alcibiade.eternity.editor.solver.restarter.RestartingSolver; /** * ConsoleApp: A console application to launch solvers * * Syntax: * * ConsoleApp <SolverName> [<SovlerName> ...] * * Starts given solvers an reads grid data from standard input * * ConsoleApp <GridFile> <SolverName> [<SolverName> ...] * * Starts given solvers on the grid file. * * @author Yannick Kirschhoffer <alcibiade@alcibiade.org> * */ public class ConsoleApp { public static void main(String[] args) { if (args.length == 0) { showHelp(); return; } try { Random random = new Random(); /* * Load grid data from either the file pointed by args[0] or the * standard input. */ StringBuilder quadsBuffer = new StringBuilder(); int firstsolverarg = loadGridData(args, quadsBuffer); String quadsString = quadsBuffer.toString(); /* * Startup solvers */ Map<EternitySolver, GridModel> solvers = new HashMap<EternitySolver, GridModel>(); Pattern multipleSolverPattern = Pattern.compile("(\\d+)x(.*)"); Pattern restartSolverPattern = Pattern.compile("(.*)\\[(\\d+)\\]"); Pattern pathSolverPattern = Pattern.compile("(.*)\\|(.*)"); ClusterManager clusterManager = new ClusterManager(new ConsoleLog(), firstsolverarg == 0 ? null : "solution_" + args[0]); for (int argIndex = firstsolverarg; argIndex < args.length; argIndex++) { int instances = 1; int restartSecs = -1; String solverName = args[argIndex]; String pathName = PathFactory.LABEL_LINEAR; if (solverName.length() == 0) { continue; } else { Matcher intanceMatcher = multipleSolverPattern.matcher(solverName); if (intanceMatcher.find()) { instances = Integer.parseInt(intanceMatcher.group(1)); solverName = intanceMatcher.group(2); } Matcher restartMatcher = restartSolverPattern.matcher(solverName); if (restartMatcher.find()) { solverName = restartMatcher.group(1); restartSecs = Integer.parseInt(restartMatcher.group(2)); } Matcher pathMatcher = pathSolverPattern.matcher(solverName); if (pathMatcher.find()) { solverName = pathMatcher.group(1); pathName = pathMatcher.group(2); } } /* * We create a specific grid instance for each solver, and we * shuffle it to increase parallel processing performance. */ for (int instance = 0; instance < instances; instance++) { GridModel grid = new GridModel(); GridModel solution = new GridModel(); grid.fromQuadString(quadsString); if (!solvers.isEmpty()) { grid.shuffle(); } EternitySolver solver; PathProvider path = PathFactory.createPath(pathName); if (restartSecs > 0) { solver = new RestartingSolver(solverName, path, grid, solution, clusterManager, restartSecs * 1000 + random.nextInt(100)); } else { solver = SolverFactory.createSolver(solverName, grid, solution, clusterManager, path); } solver.start(); solvers.put(solver, solution); } } // Wait for solution boolean solved = false; while (!solved) { for (EternitySolver solver : solvers.keySet()) { if (!solver.isAlive()) { solved = true; GridModel solution = solvers.get(solver); System.out.println(solution.toQuadString()); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } // End all threads for (EternitySolver solver : solvers.keySet()) { solver.interrupt(); try { solver.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); } catch (QuadsFormatException e) { e.printStackTrace(); } catch (UnknownSolverException e) { System.out.println(e.getMessage()); } catch (UnknownPathException e) { System.out.println(e.getMessage()); } } private static void showHelp() { System.out.println("Syntax: <board file> <solver> [<solver>]..."); System.out.println(""); System.out.println("Solvers can be invoked:"); System.out.println(" - SolverName: Basic run"); System.out.println(" - NxSolverName: Run N instances of solver"); System.out.println(" - SolverName[T]: Run an instance and restart it every T seconds"); System.out .println(" - NxSolverName[T]: N instance of sovler restarting every T seconds"); System.out.println(""); System.out.println("Backtracking solvers can have specific path specified this way:"); System.out.println(" SolverType|PathName"); System.out.println(""); System.out .println("Available solver implementations: (* means contextual path can be specified)"); for (String label : SolverFactory.getAvailableSolvers()) { String note = ""; if (SolverFactory.isSolverPathSensitive(label)) { note = " *"; } System.out.println(" - " + label + note); } System.out.println(""); System.out.println("Available paths:"); for (String label : PathFactory.getAvailablePaths()) { System.out.println(" - " + label); } System.out.println(""); } private static int loadGridData(String[] args, StringBuilder quadsBuffer) throws FileNotFoundException, IOException { int firstsolverarg = 0; File gridfile = new File(args[0]); if (gridfile.exists()) { firstsolverarg++; LineNumberReader lnr = new LineNumberReader(new FileReader(gridfile)); String line = lnr.readLine(); while (line != null) { quadsBuffer.append(line); quadsBuffer.append('\n'); line = lnr.readLine(); } } else { LineNumberReader lnr = new LineNumberReader(new InputStreamReader(System.in)); String line = lnr.readLine(); while (line != null && line.length() > 0) { quadsBuffer.append(line); quadsBuffer.append('\n'); line = lnr.readLine(); } } return firstsolverarg; } }