package tsp; import java.applet.Applet; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.FontUIResource; import tsp.map.TSPMap; import tsp.solvers.GeneticSolver; import tsp.solvers.NearestNeigbourSolver; import tsp.solvers.Tour; import tsp.solvers.rnearest.NearestNeigbourRandomizedSolver; import tsp.util.BestList; import tsp.util.delay; public class TSPApplet extends Applet { private static final long serialVersionUID = 1L; public static final int MAX_POINTS = 2048; protected static final int PROCESSOR_COUNT = Runtime.getRuntime().availableProcessors(); TSPMap map = null; TSPApplet self = this; int pointsCount = 100; int iterationCount = 100; int popCount = 50; int crossFactor = 40; int muteFactor = 20; TourDisplay display; public JLabel lenLabel; JTextField inputPoints = new JTextField(); JTextField inputIterations = new JTextField(); JTextField outputDistance = new JTextField(); JTextField outputTime = new JTextField(); JTextField inputPop = new JTextField(); JTextField inputCross = new JTextField(); JTextField inputMute = new JTextField(); JSlider inputRnnDegree = new JSlider(JSlider.HORIZONTAL, 1, 100, 1); JLabel inputRnnDegreeLabel = null; final JButton gridButton = new JButton("Grid"); final JButton newButton = new JButton("Random"); final JButton nnButton = new JButton("Nearest Neighbor"); final JButton rnnButton = new JButton("Random Neighbor"); final JButton optButton = new JButton("2-opt"); final JButton gaButton = new JButton("Genetic"); final JButton gaModButton = new JButton("Genetic-M"); final JButton stopButton = new JButton("STOP"); Thread worker; boolean threadWorking = false; private Tour bestTour = null; private BestList bestValues = new BestList(); private JTextArea bestValuesLabel = new JTextArea(); private long bestTourStarted = 0; private String currentTour = ""; private boolean needToStop = false; public TSPApplet() { map = null; threadWorking = false; clearBestTour(""); } public void init() { try { UIManager.getDefaults().put("Label.font", new FontUIResource("Dialog", Font.PLAIN, 11)); } catch (Exception e) { System.out.println("Failed to set font to labels"); e.printStackTrace(); } // this.setBackground(Color.lightGray); display = new TourDisplay(map); display.setMinimumSize(new Dimension(400, 610)); display.setPreferredSize(new Dimension(400, 610)); lenLabel = new JLabel("Press 'Random' or 'Grid' button to start..."); inputRnnDegreeLabel = new JLabel(); } private void toggleOperationButtons(boolean enabled) { nnButton.setEnabled(enabled); rnnButton.setEnabled(enabled); optButton.setEnabled(enabled); gaButton.setEnabled(enabled); gaModButton.setEnabled(enabled); gridButton.setEnabled(enabled); newButton.setEnabled(enabled); } public void start() { this.setBackground(Color.WHITE); this.setLayout(new FlowLayout(1, 0, 0)); JPanel panel = new JPanel(); panel.setPreferredSize(new Dimension(420, 645)); panel.setBackground(getBackground()); panel.add(display); Border border = new TitledBorder("Map"); panel.setBorder(border); this.add(panel); JPanel rPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); rPanel.setPreferredSize(new Dimension(170, 645)); rPanel.setLocation(0, 0); panel = new JPanel(); panel.setBackground(getBackground()); rPanel.setBackground(getBackground()); panel.setBorder(new TitledBorder("Parameters")); panel.setPreferredSize(new Dimension(170, 180)); // panel.setBackground(getBackground()); panel.add(new JLabel(" # of cities:")); inputPoints.setPreferredSize(new Dimension(50, 17)); inputPoints.setText(Integer.toString(pointsCount)); panel.add(inputPoints); panel.add(new JLabel("# of iterations:")); inputIterations.setPreferredSize(new Dimension(50, 17)); inputIterations.setText(Integer.toString(iterationCount)); panel.add(inputIterations); panel.add(new JLabel("Population(GA):")); inputPop.setPreferredSize(new Dimension(30, 17)); inputPop.setText(Integer.toString(popCount)); panel.add(inputPop); panel.add(new JLabel("Cross Factor(GA):")); inputCross.setPreferredSize(new Dimension(30, 17)); inputCross.setText(Integer.toString(crossFactor)); panel.add(inputCross); panel.add(new JLabel("Mute Factor(GA):")); inputMute.setPreferredSize(new Dimension(30, 17)); inputMute.setText(Integer.toString(muteFactor)); panel.add(inputMute); inputRnnDegreeLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT); inputRnnDegreeLabel.setPreferredSize(new Dimension(150, 14)); panel.add(inputRnnDegreeLabel); inputRnnDegree.setPreferredSize(new Dimension(150, 20)); inputRnnDegree.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { inputRnnDegreeLabel.setText("Degree (NNR): " + inputRnnDegree.getValue()); } }); panel.add(inputRnnDegree); inputRnnDegree.setValue(100); inputRnnDegree.setBackground(getBackground()); rPanel.add(panel); panel = new JPanel(); panel.setBackground(getBackground()); panel.setBorder(new TitledBorder("Results")); // panel.setBackground(getBackground()); panel.setPreferredSize(new Dimension(170, 75)); panel.add(new JLabel("Distance:")); outputDistance.setPreferredSize(new Dimension(90, 17)); outputDistance.setText("0"); panel.add(outputDistance); panel.add(new JLabel("Time(ms):")); outputTime.setPreferredSize(new Dimension(80, 17)); outputTime.setText("0"); panel.add(outputTime); rPanel.add(panel); panel = new JPanel(); panel.setBackground(getBackground()); panel.setPreferredSize(new Dimension(170, 60)); panel.setBorder(new TitledBorder("Points")); panel.add(newButton); panel.add(gridButton); rPanel.add(panel); panel = new JPanel(); panel.setBackground(getBackground()); panel.setPreferredSize(new Dimension(170, 180)); panel.setBorder(new TitledBorder("Methods")); int bW = 150; int bH = 20; nnButton.setPreferredSize(new Dimension(bW, bH)); panel.add(nnButton); rnnButton.setPreferredSize(new Dimension(bW, bH)); panel.add(rnnButton); optButton.setPreferredSize(new Dimension(bW, bH)); panel.add(optButton); gaButton.setPreferredSize(new Dimension(bW, bH)); panel.add(gaButton); gaModButton.setPreferredSize(new Dimension(bW, bH)); panel.add(gaModButton); stopButton.setPreferredSize(new Dimension(bW, bH)); panel.add(stopButton); rPanel.add(panel); panel = new JPanel(); panel.setBackground(getBackground()); panel.setPreferredSize(new Dimension(170, 150)); panel.setBorder(new TitledBorder("Best lengths")); bestValuesLabel.setPreferredSize(new Dimension(150, 120)); panel.add(bestValuesLabel); rPanel.add(panel); this.add(rPanel); this.add(new JLabel("TSP Java Implementation - Ramūnas Dronga IT-4/1, Ramūnas Kraujutis.")); this.add(lenLabel); newButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { doNew(); } }); gridButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { doGrid(); } }); nnButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { doNearestNeighbour(); } }); rnnButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doRNearestNeighbour(); } }); optButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { do2Opt(); } }); gaButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { doGeneticAlgorithm(); } }); gaModButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doGeneticAlgorithmModified(); } }); stopButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { needToStop = true; stopButton.setEnabled(false); while (threadWorking) { delay.pause(10); } toggleOperationButtons(!threadWorking); stopButton.setEnabled(true); } }); } public void doNew() { if (threadWorking) { return; } else { bestValues.clear(); lenLabel.setText("Choose algorithm to solve map..."); pointsCount = Integer.parseInt(inputPoints.getText()); if (pointsCount > TSPApplet.MAX_POINTS) { pointsCount = TSPApplet.MAX_POINTS; inputPoints.setText(Integer.toString(pointsCount)); } map = new TSPMap(pointsCount, display.getWidth(), display.getHeight()); display.setMap(map); return; } } public void doGrid() { if (threadWorking) { return; } else { bestValues.clear(); lenLabel.setText("Choose method to solve map..."); pointsCount = Integer.parseInt(inputPoints.getText()); map = new TSPMap(); map.createGridCities(pointsCount, display.getWidth(), display.getHeight()); display.setMap(map); return; } } public void doRNearestNeighbour() { if (threadWorking || map == null) { return; } else { worker = new Thread() { public void run() { clearBestTour("NNR-" + getDegree()); toggleOperationButtons(!threadWorking); iterationCount = Integer.parseInt(inputIterations.getText()); ArrayList solvers = new ArrayList(); for (int i = 0; i < PROCESSOR_COUNT; i++) { solvers.add(new NearestNeigbourRandomizedSolver(self, map, (int) Math.round(Math.ceil(iterationCount / PROCESSOR_COUNT)))); solvers.get(solvers.size() - 1).start(); } long iter; boolean allFinished; do { iter = 0; allFinished = true; for (NearestNeigbourRandomizedSolver solver : solvers) { if (solver.isAlive()) { allFinished = false; } iter += solver.getIteration(); } lenLabel.setText("Calculating (" + iter + ")"); delay.pause(100); } while (!allFinished); lenLabel.setText("NNR finished. Best length: " + bestTour.getLength()); displayTourInfo(); threadWorking = false; toggleOperationButtons(!threadWorking); } }; threadWorking = true; toggleOperationButtons(!threadWorking); worker.start(); } } public void doNearestNeighbour() { if (threadWorking || map == null) { return; } else { worker = new Thread() { public void run() { clearBestTour("NN"); NearestNeigbourSolver tsp = new NearestNeigbourSolver(self, map); tsp.start(); while (tsp.isAlive()) { lenLabel.setText("Calculating " + tsp.getIteration()); delay.pause(10); } lenLabel.setText("NN done: " + bestTour.getLength()); displayTourInfo(); threadWorking = false; toggleOperationButtons(!threadWorking); } }; threadWorking = true; toggleOperationButtons(!threadWorking); worker.start(); } } public void do2Opt() { if (threadWorking || map == null) { return; } else { worker = new Thread() { public void run() { clearBestTour("2-opt"); iterationCount = Integer.parseInt(inputIterations.getText()); Tour tour = Tour.getRandomTour(map); for (int i = 1; !needStop() && i <= iterationCount; i++) { tour.randomize(); tour.do2Opt(); addTour(tour); lenLabel.setText("Calculating (" + i + ")"); delay.pause(10); } lenLabel.setText("2-opt done: " + bestTour.getLength()); displayTourInfo(); threadWorking = false; toggleOperationButtons(!threadWorking); } }; threadWorking = true; toggleOperationButtons(!threadWorking); worker.start(); } } public void doGeneticAlgorithm() { if (map == null) return; if (!threadWorking) { worker = new Thread() { public void run() { clearBestTour("GA"); iterationCount = Integer.parseInt(inputIterations.getText()); popCount = Integer.parseInt(inputPop.getText()); crossFactor = Integer.parseInt(inputCross.getText()); muteFactor = Integer.parseInt(inputMute.getText()); final GeneticSolver tsp = new GeneticSolver(map, popCount, crossFactor, muteFactor); for (int i = 1; !needStop() && i < iterationCount; i++) { tsp.doOneGeneration(); addTour(tsp.getSolution()); lenLabel.setText("Calculating (" + i + ")"); } lenLabel.setText("GA done: " + bestTour.getLength()); displayTourInfo(); threadWorking = false; toggleOperationButtons(!threadWorking); } }; threadWorking = true; toggleOperationButtons(!threadWorking); worker.start(); } } public void doGeneticAlgorithmModified() { if (map == null) return; if (!threadWorking) { worker = new Thread() { public void run() { clearBestTour("GA-M"); iterationCount = Integer.parseInt(inputIterations.getText()); popCount = Integer.parseInt(inputPop.getText()); crossFactor = Integer.parseInt(inputCross.getText()); muteFactor = Integer.parseInt(inputMute.getText()); final GeneticSolver tsp = new GeneticSolver(map, popCount, crossFactor, muteFactor); for (int i = 1; !needStop() && i < iterationCount; i++) { tsp.doOneGenerationModified(); addTour(tsp.getSolution()); lenLabel.setText("Calculating (" + i + ")"); } lenLabel.setText("GA-M done: " + bestTour.getLength()); displayTourInfo(); threadWorking = false; toggleOperationButtons(!threadWorking); } }; threadWorking = true; toggleOperationButtons(!threadWorking); worker.start(); } } public int getDegree() { return inputRnnDegree.getValue(); } private boolean betterTour(Tour tour) { return tour != null && (bestTour == null || bestTour.getLength() > tour.getLength()); } public void addTour(Tour tour) { if (betterTour(tour)) { synchronized (this) { if (betterTour(tour)) { bestTour = (Tour) tour.clone(); bestValues.put(currentTour, bestTour.getLength()); displayTour(bestTour); displayTourInfo(); } } } } public void displayTourInfo() { if (bestTour != null) { outputDistance.setText(String.format("%4.2f", bestTour.getLength())); outputTime.setText(String.format("%d", System.currentTimeMillis() - bestTourStarted)); StringBuilder text = new StringBuilder(); for (String method : bestValues.keySet()) { text.append(method).append(": "); text.append(String.format("%4.2f", bestValues.get(method))).append("\n"); } bestValuesLabel.setText(text.toString()); } } public Tour displayTour(Tour tour) { display.setTour(bestTour); return tour; } public TourDisplay getTourDisplay() { return display; } public void clearBestTour(String currentTour) { this.currentTour = currentTour; bestTour = null; bestTourStarted = System.currentTimeMillis(); needToStop = false; outputDistance.setText(""); outputTime.setText(""); } public boolean needStop() { return needToStop; } }