package org.ggp.base.apps.server.visualization; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.util.concurrent.LinkedBlockingQueue; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTabbedPane; import org.ggp.base.server.event.ServerCompletedMatchEvent; import org.ggp.base.server.event.ServerNewGameStateEvent; import org.ggp.base.server.event.ServerNewMatchEvent; import org.ggp.base.server.event.ServerTimeEvent; import org.ggp.base.util.game.Game; import org.ggp.base.util.game.GameRepository; import org.ggp.base.util.match.Match; import org.ggp.base.util.observer.Event; import org.ggp.base.util.observer.Observer; import org.ggp.base.util.statemachine.MachineState; import org.ggp.base.util.statemachine.StateMachine; import org.ggp.base.util.statemachine.cache.CachedStateMachine; import org.ggp.base.util.statemachine.implementation.prover.ProverStateMachine; import org.ggp.base.util.ui.GameStateRenderer; import org.ggp.base.util.ui.timer.JTimerBar; @SuppressWarnings("serial") public final class VisualizationPanel extends JPanel implements Observer { private final Game theGame; private final VisualizationPanel myThis; private JTabbedPane tabs = new JTabbedPane(); private final JTimerBar timerBar; private final RenderThread rt; public VisualizationPanel(Game theGame) { super(new GridBagLayout()); this.theGame = theGame; this.myThis = this; this.timerBar = new JTimerBar(); this.rt = new RenderThread(); this.rt.start(); this.add(tabs, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 5, 5)); this.add(timerBar, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 5, 5)); } private int stepCount = 1; @Override public void observe(Event event) { if (event instanceof ServerNewGameStateEvent) { MachineState s = ((ServerNewGameStateEvent)event).getState(); rt.submit(s, stepCount++); } else if (event instanceof ServerTimeEvent) { timerBar.time(((ServerTimeEvent) event).getTime(), 500); } else if (event instanceof ServerCompletedMatchEvent) { rt.finish(); timerBar.stop(); } else if (event instanceof ServerNewMatchEvent) { MachineState s = ((ServerNewMatchEvent) event).getInitialState(); rt.submit(s, stepCount); } } private class RenderThread extends Thread { private final LinkedBlockingQueue<VizJob> queue; public RenderThread() { this.queue = new LinkedBlockingQueue<>(); } private abstract class VizJob{ public abstract boolean stop(); public void render() {} }; private final class StopJob extends VizJob { @Override public boolean stop() { return true; } } private final class RenderJob extends VizJob { private MachineState state; private int stepNum; public RenderJob(MachineState state, int stepNum) { this.state = state; this.stepNum = stepNum; } @Override public boolean stop() { return false; } @Override public void render() { JPanel newPanel = null; try { String XML = Match.renderStateXML(state.getContents()); String XSL = theGame.getStylesheet(); if (XSL != null) { newPanel = new VizContainerPanel(XML, XSL, myThis); } } catch(Exception ex) { ex.printStackTrace(); } if(newPanel != null) { boolean atEnd = (tabs.getSelectedIndex() == tabs.getTabCount()-1); try { for(int i = tabs.getTabCount(); i < stepNum; i++) tabs.add(new Integer(i+1).toString(), new JPanel()); tabs.setComponentAt(stepNum-1, newPanel); tabs.setTitleAt(stepNum-1, new Integer(stepNum).toString()); if(atEnd) { tabs.setSelectedIndex(tabs.getTabCount()-1); } } catch(Exception ex) { System.err.println("Adding rendered visualization panel failed for: " + theGame.getKey()); } } } } public void submit(MachineState state, int stepNum) { queue.add(new RenderJob(state, stepNum)); } public void finish() { queue.add(new StopJob()); } @Override public void run() { boolean running = true; int interrupted = 0; while (running) { try { VizJob job = queue.take(); interrupted = 0; if (!job.stop()) { job.render(); } else { GameStateRenderer.shrinkCache(); running = false; } } catch (InterruptedException e) { interrupted += 1; if ((interrupted % 10) == 0) { System.err.println("Render thread interrupted "+interrupted+" times in a row"); } } } } } // Simple test that loads the nineBoardTicTacToe game and visualizes // a randomly-played match, to demonstrate that visualization works. public static void main(String args[]) { JFrame frame = new JFrame("Visualization Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Game theGame = GameRepository.getDefaultRepository().getGame("nineBoardTicTacToe"); VisualizationPanel theVisual = new VisualizationPanel(theGame); frame.setPreferredSize(new Dimension(1200, 900)); frame.getContentPane().add(theVisual); frame.pack(); frame.setVisible(true); StateMachine theMachine = new CachedStateMachine(new ProverStateMachine()); theMachine.initialize(theGame.getRules()); try { MachineState theCurrentState = theMachine.getInitialState(); do { theVisual.observe(new ServerNewGameStateEvent(theCurrentState)); theCurrentState = theMachine.getRandomNextState(theCurrentState); Thread.sleep(250); System.out.println("State: " + theCurrentState); } while(!theMachine.isTerminal(theCurrentState)); theVisual.observe(new ServerNewGameStateEvent(theCurrentState)); } catch (Exception e) { e.printStackTrace(); } } }