content: package aima.gui.swing.applications.search.games;
  
  import java.awt.BorderLayout;
  import java.awt.Color;
  import java.awt.Font;
  import java.awt.GridLayout;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  
  import javax.swing.BorderFactory;
  import javax.swing.Box;
  import javax.swing.JButton;
  import javax.swing.JComboBox;
  import javax.swing.JFrame;
  import javax.swing.JLabel;
  import javax.swing.JPanel;
  import javax.swing.JToolBar;
  
  import aima.core.environment.tictactoe.TicTacToeGame;
  import aima.core.environment.tictactoe.TicTacToeState;
  import aima.core.search.adversarial.AdversarialSearch;
  import aima.core.search.adversarial.AlphaBetaSearch;
  import aima.core.search.adversarial.IterativeDeepeningAlphaBetaSearch;
  import aima.core.search.adversarial.MinimaxSearch;
  import aima.core.search.framework.Metrics;
  import aima.core.util.datastructure.XYLocation;
  
  /**
   * Simple graphical Tic-tac-toe game application. It demonstrates the Minimax
   * algorithm for move selection as well as alpha-beta pruning.
   * 
   * @author Ruediger Lunde
   */
  public class TicTacToeApp {
  
  	/** Used for integration into the universal demo application. */
  	public JFrame constructApplicationFrame() {
  		JFrame frame = new JFrame();
  		JPanel panel = new TicTacToePanel();
  		frame.add(panel, BorderLayout.CENTER);
  		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  		return frame;
  	}
  
  	/** Application starter. */
  	public static void main(String[] args) {
  		JFrame frame = new TicTacToeApp().constructApplicationFrame();
  		frame.setSize(400, 400);
  		frame.setVisible(true);
  	}
  
  	/** Simple panel to control the game. */
  	private static class TicTacToePanel extends JPanel implements
  			ActionListener {
  		private static final long serialVersionUID = 1L;
  		JComboBox<String> strategyCombo;
  		JButton clearButton;
  		JButton proposeButton;
  		JButton[] squares;
  		JLabel statusBar;
  
  		TicTacToeGame game;
  		TicTacToeState currState;
  		Metrics searchMetrics;
  
  		/** Standard constructor. */
  		TicTacToePanel() {
  			this.setLayout(new BorderLayout());
  			JToolBar tbar = new JToolBar();
  			tbar.setFloatable(false);
  			strategyCombo = new JComboBox<String>(new String[] { "Minimax",
  					"Alpha-Beta", "Iterative Deepening Alpha-Beta",
  					"Iterative Deepening Alpha-Beta (log)" });
  			strategyCombo.setSelectedIndex(1);
  			tbar.add(strategyCombo);
  			tbar.add(Box.createHorizontalGlue());
  			clearButton = new JButton("Clear");
  			clearButton.addActionListener(this);
  			tbar.add(clearButton);
  			proposeButton = new JButton("Propose Move");
  			proposeButton.addActionListener(this);
  			tbar.add(proposeButton);
  
  			add(tbar, BorderLayout.NORTH);
  			JPanel spanel = new JPanel();
  			spanel.setLayout(new GridLayout(3, 3));
  			add(spanel, BorderLayout.CENTER);
  			squares = new JButton[9];
  			Font f = new java.awt.Font(Font.SANS_SERIF, Font.PLAIN, 32);
  			for (int i = 0; i < 9; i++) {
  				JButton square = new JButton("");
  				square.setFont(f);
  				square.setBackground(Color.WHITE);
  				square.addActionListener(this);
  				squares[i] = square;
  				spanel.add(square);
  			}
  			statusBar = new JLabel(" ");
  			statusBar.setBorder(BorderFactory.createEtchedBorder());
  			add(statusBar, BorderLayout.SOUTH);
  
  			game = new TicTacToeGame();
  			actionPerformed(null);
  		}
  
  		/** Handles all button events and updates the view. */
  		@Override
  		public void actionPerformed(ActionEvent ae) {
  			searchMetrics = null;
  			if (ae == null || ae.getSource() == clearButton)
  				currState = game.getInitialState();
  			else if (!game.isTerminal(currState)) {
  				if (ae.getSource() == proposeButton)
  					proposeMove();
  				else {
  					for (int i = 0; i < 9; i++)
  						if (ae.getSource() == squares[i])
  							currState = game.getResult(currState,
  									new XYLocation(i % 3, i / 3));
  				}
  			}
  			for (int i = 0; i < 9; i++) {
  				String val = currState.getValue(i % 3, i / 3);
  				if (val == TicTacToeState.EMPTY)
  					val = "";
  				squares[i].setText(val);
  			}
  			updateStatus();
  		}
  
  		/** Uses adversarial search for selecting the next action. */
  		private void proposeMove() {
  			AdversarialSearch<TicTacToeState, XYLocation> search;
  			XYLocation action;
  			switch (strategyCombo.getSelectedIndex()) {
  			case 0:
  				search = MinimaxSearch.createFor(game);
  				break;
  			case 1:
  				search = AlphaBetaSearch.createFor(game);
  				break;
  			case 2:
  				search = IterativeDeepeningAlphaBetaSearch.createFor(game, 0.0,
  						1.0, 1000);
  				break;
  			default:
  				search = IterativeDeepeningAlphaBetaSearch.createFor(game, 0.0,
  						1.0, 1000);
  				((IterativeDeepeningAlphaBetaSearch<?, ?, ?>) search)
  						.setLogEnabled(true);
  			}
  			action = search.makeDecision(currState);
  			searchMetrics = search.getMetrics();
  			currState = game.getResult(currState, action);
  		}
  
  		/** Updates the status bar. */
  		private void updateStatus() {
  			String statusText;
  			if (game.isTerminal(currState))
  				if (game.getUtility(currState, TicTacToeState.X) == 1)
  					statusText = "X has won :-)";
  				else if (game.getUtility(currState, TicTacToeState.O) == 1)
  					statusText = "O has won :-)";
  				else
  					statusText = "No winner...";
  			else
  				statusText = "Next move: " + game.getPlayer(currState);
  			if (searchMetrics != null)
  				statusText += "    " + searchMetrics;
  			statusBar.setText(statusText);
  		}
  	}
  }
  