import javax.swing.*; import javax.swing.table.*; import javax.swing.event.*; import java.net.*; import java.awt.*; import java.util.*; import java.io.*; /** Graphical display and management of Darwin Game tournaments. @sa Darwin for single matches and debugging. <pre> java Tournament <i>mapfile</i> Creature0 Creature1 ... </pre> If mapfile begins with "mz_" it is assumed to have one creature on it. If mapfile begins with "ns_" it is assumed to have two creatures on it. <p> The Tournament class runs each trial with a fresh instance of the classes, intentionally reloading them from disk. This prevents classes from sharing any information with themselves between runs using static variables. */ public class Tournament extends JFrame { /** Description of a creature */ private static class Description implements Comparable { public String className; public String authorName; public Icon icon; public int bestTime = Integer.MAX_VALUE; public int worstTime = 0; public int meanTime = Integer.MAX_VALUE; public ArrayList<Integer> times = new ArrayList<Integer>(); public Description(String cname) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { className = cname; Class c = Simulator.loadClass(className); assert c != null; Creature instance = (Creature)c.newInstance(); authorName = instance.getAuthorName(); icon = new ImageIcon(Simulator.getImage(c, Direction.SOUTH)); } public int compareTo(Object obj) { Description that = (Description)obj; return this.meanTime - that.meanTime; } /** Run one trial and update this description with the result. */ public void runTrial(String mapName) { try { System.out.println(className + " on " + mapName + ": "); Class[] creatures = {Simulator.loadClass(className)}; Simulator simulator = new Simulator(mapName, creatures); // Launch the simulation simulator.setDelayTime(10000); // Run until completion. Simulator.Result result = simulator.getResult(); while (result == null) { try { // Don't sleep too long; if we don't notice // that the game has ended we might let it run // for too long past the end, which affects // maze running timings. Thread.sleep(25); } catch (InterruptedException e) {} result = simulator.getResult(); } simulator.stop(); addTime(result.timeSteps); if (result.species != creatures[0]) { // Lose! } System.out.println(className + " completed " + mapName + " in " + result.timeSteps); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** Update times to reflect this change. */ public void addTime(int time) { bestTime = Math.min(bestTime, time); worstTime = Math.max(worstTime, time); times.add(time); meanTime = 0; for (int t : times) { meanTime += t; } meanTime /= times.size(); } } private Description[] creatureArray; private Description[] sortedCreatureArray; private String mapFilename; private ResultData resultData; private JProgressBar progressBar; private int currentTrial; private int numTrials; private static final int NUM_REPEATS = 8; private Tournament(String mapfile, String[] creatureClassNames) { super("Darwin Game Tournament - " + mapfile); System.setSecurityManager(new MaximumSecurityManager(new String[]{"readFileDescriptor"})); creatureArray = new Description[creatureClassNames.length]; for (int i = 0; i < creatureArray.length; ++i) { try { creatureArray[i] = new Description(creatureClassNames[i]); } catch (Exception e) { System.err.println("Error while loading " + creatureClassNames[i] + ":\n" + e); e.printStackTrace(); System.exit(-1); } } sortedCreatureArray = (Description[])creatureArray.clone(); mapFilename = mapfile; // Must happen before GUI is created. currentTrial = 0; numTrials = NUM_REPEATS * creatureArray.length; makeGUI(); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); runAllTrials(mapfile); } private void runAllTrials(String mapfile) { for (int i = 0; i < NUM_REPEATS; ++i) { for (Description d : creatureArray) { d.runTrial(mapfile); synchronized (Tournament.this) { // Update the data Arrays.sort(sortedCreatureArray); resultData.fireTableChanged(new TableModelEvent(resultData)); } ++currentTrial; progressBar.setValue(currentTrial); } } progressBar.setVisible(false); System.out.println("Trials complete"); } private void makeGUI() { resultData = new ResultData(); JTable resultTable = new JTable(resultData); resultTable.setRowHeight(70); resultTable.setShowHorizontalLines(true); resultTable.getColumnModel().getColumn(RANK_COL).setPreferredWidth(40); resultTable.getColumnModel().getColumn(AUTHOR_COL).setPreferredWidth(120); resultTable.getColumnModel().getColumn(IMAGE_COL).setPreferredWidth(45); DefaultTableCellRenderer timeRenderer = new DefaultTableCellRenderer(); timeRenderer.setHorizontalAlignment(SwingConstants.RIGHT); timeRenderer.setFont(new Font("Monospaced", Font.PLAIN, 14)); resultTable.getColumnModel().getColumn(BEST_COL).setCellRenderer(timeRenderer); resultTable.getColumnModel().getColumn(WORST_COL).setCellRenderer(timeRenderer); resultTable.getColumnModel().getColumn(MEAN_COL).setCellRenderer(timeRenderer); progressBar = new JProgressBar(currentTrial, numTrials); JPanel pane = new JPanel(); pane.setLayout(new BorderLayout()); pane.add(new JScrollPane(resultTable), BorderLayout.CENTER); pane.add(new JScrollPane(progressBar), BorderLayout.PAGE_END); getContentPane().add(pane); } // For ResultData static final private int RANK_COL = 0; static final private int IMAGE_COL = 1; static final private int CREATURE_COL = 2; static final private int AUTHOR_COL = 3; static final private int BEST_COL = 4; static final private int WORST_COL = 5; static final private int MEAN_COL = 6; static private final String[] columnNames = {"Rank", "Image", "Creature", "Author", "Best Time", "Worst Time", "Mean Time"}; private class ResultData extends AbstractTableModel { public String getColumnName(int col) { return columnNames[col]; } public int getColumnCount() { return columnNames.length; } public Class getColumnClass(int col) { if (col == IMAGE_COL) { return Icon.class; } else { return String.class; } } public int getRowCount() { return creatureArray.length; } public Object getValueAt(int row, int col) { Description d = sortedCreatureArray[row]; switch (col) { case RANK_COL: return "" + (row + 1); case CREATURE_COL: return d.className; case IMAGE_COL: return d.icon; case AUTHOR_COL: return d.authorName; case BEST_COL: if (d.bestTime == Integer.MAX_VALUE) { return ""; } else { return "" + d.bestTime; } case WORST_COL: if (d.bestTime == Integer.MAX_VALUE) { return ""; } else { return "" + d.worstTime; } case MEAN_COL: if (d.bestTime == Integer.MAX_VALUE) { return ""; } else { return "" + d.meanTime; } } return ""; } } public static void main(String[] arg) { String mapfile = arg[0]; String[] creatures = new String[arg.length - 1]; for (int c = 1; c < arg.length; ++c) { creatures[c - 1] = arg[c]; } new Tournament(mapfile, creatures); } }