Conway's Game of Life/Java/Swing: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added the Java swing based implementation to cut down on clutter on the main page. (as discussed on the main talk page))
 
m (Hardcoded value changed to its variable counterpart.)
Line 200: Line 200:
public void addPoint(MouseEvent me) {
public void addPoint(MouseEvent me) {
int x = me.getPoint().x/10-1;
int x = me.getPoint().x/BLOCK_SIZE-1;
int y = me.getPoint().y/10-1;
int y = me.getPoint().y/BLOCK_SIZE-1;
if ((x >= 0) && (x < d_gameBoardSize.width) && (y >= 0) && (y < d_gameBoardSize.height)) {
if ((x >= 0) && (x < d_gameBoardSize.width) && (y >= 0) && (y < d_gameBoardSize.height)) {
addPoint(x,y);
addPoint(x,y);

Revision as of 20:19, 26 October 2015

Works with: Java version 6+

This swing based implementation will take user input, and can be interacted with using the mouse.

<lang java> import java.awt.*; import java.awt.event.*; import java.net.URI; import java.util.ArrayList; import java.util.ConcurrentModificationException; import javax.swing.*;

/**

* Conway's game of life is a cellular automaton devised by the
* mathematician John Conway.
*/

public class ConwaysGameOfLife extends JFrame implements ActionListener {

   private static final Dimension DEFAULT_WINDOW_SIZE = new Dimension(800, 600);
   private static final Dimension MINIMUM_WINDOW_SIZE = new Dimension(400, 400);
   private static final int BLOCK_SIZE = 10;
   private JMenuBar mb_menu;
   private JMenu m_file, m_game, m_help;
   private JMenuItem mi_file_options, mi_file_exit;
   private JMenuItem mi_game_autofill, mi_game_play, mi_game_stop, mi_game_reset;
   private JMenuItem mi_help_about, mi_help_source;
   private int i_movesPerSecond = 3;
   private GameBoard gb_gameBoard;
   private Thread game;
   
   public static void main(String[] args) {
       // Setup the swing specifics
       JFrame game = new ConwaysGameOfLife();
       game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       game.setTitle("Conway's Game of Life");
       game.setIconImage(new ImageIcon(ConwaysGameOfLife.class.getResource("/images/logo.png")).getImage());
       game.setSize(DEFAULT_WINDOW_SIZE);
       game.setMinimumSize(MINIMUM_WINDOW_SIZE);
       game.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width - game.getWidth())/2, 
               (Toolkit.getDefaultToolkit().getScreenSize().height - game.getHeight())/2);
       game.setVisible(true);
   }
   
   public ConwaysGameOfLife() {
       // Setup menu
       mb_menu = new JMenuBar();
       setJMenuBar(mb_menu);
       m_file = new JMenu("File");
       mb_menu.add(m_file);
       m_game = new JMenu("Game");
       mb_menu.add(m_game);
       m_help = new JMenu("Help");
       mb_menu.add(m_help);
       mi_file_options = new JMenuItem("Options");
       mi_file_options.addActionListener(this);
       mi_file_exit = new JMenuItem("Exit");
       mi_file_exit.addActionListener(this);
       m_file.add(mi_file_options);
       m_file.add(new JSeparator());
       m_file.add(mi_file_exit);
       mi_game_autofill = new JMenuItem("Autofill");
       mi_game_autofill.addActionListener(this);
       mi_game_play = new JMenuItem("Play");
       mi_game_play.addActionListener(this);
       mi_game_stop = new JMenuItem("Stop");
       mi_game_stop.setEnabled(false);
       mi_game_stop.addActionListener(this);
       mi_game_reset = new JMenuItem("Reset");
       mi_game_reset.addActionListener(this);
       m_game.add(mi_game_autofill);
       m_game.add(new JSeparator());
       m_game.add(mi_game_play);
       m_game.add(mi_game_stop);
       m_game.add(mi_game_reset);
       mi_help_about = new JMenuItem("About");
       mi_help_about.addActionListener(this);
       mi_help_source = new JMenuItem("Source");
       mi_help_source.addActionListener(this);
       m_help.add(mi_help_about);
       m_help.add(mi_help_source);
       // Setup game board
       gb_gameBoard = new GameBoard();
       add(gb_gameBoard);
   }
   
   public void setGameBeingPlayed(boolean isBeingPlayed) {
       if (isBeingPlayed) {
           mi_game_play.setEnabled(false);
           mi_game_stop.setEnabled(true);
           game = new Thread(gb_gameBoard);
           game.start();
       } else {
           mi_game_play.setEnabled(true);
           mi_game_stop.setEnabled(false);
           game.interrupt();
       }
   }
       
   @Override
   public void actionPerformed(ActionEvent ae) {
       if (ae.getSource().equals(mi_file_exit)) {
           // Exit the game
           System.exit(0);
       } else if (ae.getSource().equals(mi_file_options)) {
           // Put up an options panel to change the number of moves per second
           final JFrame f_options = new JFrame();
           f_options.setTitle("Options");
           f_options.setSize(300,60);
           f_options.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width - f_options.getWidth())/2, 
               (Toolkit.getDefaultToolkit().getScreenSize().height - f_options.getHeight())/2);
           f_options.setResizable(false);
           JPanel p_options = new JPanel();
           p_options.setOpaque(false);
           f_options.add(p_options);
           p_options.add(new JLabel("Number of moves per second:"));
           Integer[] secondOptions = {1,2,3,4,5,10,15,20};
           final JComboBox cb_seconds = new JComboBox(secondOptions);
           p_options.add(cb_seconds);
           cb_seconds.setSelectedItem(i_movesPerSecond);
           cb_seconds.addActionListener(new ActionListener(){
               @Override
               public void actionPerformed(ActionEvent ae) {
                   i_movesPerSecond = (Integer)cb_seconds.getSelectedItem();
                   f_options.dispose();
               }
           });
           f_options.setVisible(true);
       } else if (ae.getSource().equals(mi_game_autofill)) {
           final JFrame f_autoFill = new JFrame();
           f_autoFill.setTitle("Autofill");
           f_autoFill.setSize(360, 60);
           f_autoFill.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width - f_autoFill.getWidth())/2, 
               (Toolkit.getDefaultToolkit().getScreenSize().height - f_autoFill.getHeight())/2);
           f_autoFill.setResizable(false);
           JPanel p_autoFill = new JPanel();
           p_autoFill.setOpaque(false);
           f_autoFill.add(p_autoFill);
           p_autoFill.add(new JLabel("What percentage should be filled? "));
           Object[] percentageOptions = {"Select",5,10,15,20,25,30,40,50,60,70,80,90,95};
           final JComboBox cb_percent = new JComboBox(percentageOptions);
           p_autoFill.add(cb_percent);
           cb_percent.addActionListener(new ActionListener() {
               @Override
               public void actionPerformed(ActionEvent e) {
                   if (cb_percent.getSelectedIndex() > 0) {
                       gb_gameBoard.resetBoard();
                       gb_gameBoard.randomlyFillBoard((Integer)cb_percent.getSelectedItem());
                       f_autoFill.dispose();
                   }
               }
           });
           f_autoFill.setVisible(true);
       } else if (ae.getSource().equals(mi_game_reset)) {
           gb_gameBoard.resetBoard();
           gb_gameBoard.repaint();
       } else if (ae.getSource().equals(mi_game_play)) {
           setGameBeingPlayed(true);
       } else if (ae.getSource().equals(mi_game_stop)) {
           setGameBeingPlayed(false);
       } else if (ae.getSource().equals(mi_help_source)) {
           Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
           try {
               desktop.browse(new URI("https://github.com/Burke9077/Conway-s-Game-of-Life"));
           } catch (Exception ex) {
               JOptionPane.showMessageDialog(null, "Source is available on GitHub at:\nhttps://github.com/Burke9077/Conway-s-Game-of-Life", "Source", JOptionPane.INFORMATION_MESSAGE);
           }
       } else if (ae.getSource().equals(mi_help_about)) {
           JOptionPane.showMessageDialog(null, "Conway's game of life was a cellular animation devised by the mathematician John Conway.\nThis Java, swing based implementation was created by Matthew Burke.\n\nhttp://burke9077.com\nBurke9077@gmail.com\n@burke9077\n\nCreative Commons Attribution 4.0 International");
       }
   }
   
   private class GameBoard extends JPanel implements ComponentListener, MouseListener, MouseMotionListener, Runnable {
       private Dimension d_gameBoardSize = null;
       private ArrayList<Point> point = new ArrayList<Point>(0);
       
       public GameBoard() {
           // Add resizing listener
           addComponentListener(this);
           addMouseListener(this);
           addMouseMotionListener(this);
       }
       
       private void updateArraySize() {
           ArrayList<Point> removeList = new ArrayList<Point>(0);
           for (Point current : point) {
               if ((current.x > d_gameBoardSize.width-1) || (current.y > d_gameBoardSize.height-1)) {
                   removeList.add(current);
               }
           }
           point.removeAll(removeList);
           repaint();
       }
       
       public void addPoint(int x, int y) {
           if (!point.contains(new Point(x,y))) {
               point.add(new Point(x,y));
           } 
           repaint();
       }
       
       public void addPoint(MouseEvent me) {
           int x = me.getPoint().x/BLOCK_SIZE-1;
           int y = me.getPoint().y/BLOCK_SIZE-1;
           if ((x >= 0) && (x < d_gameBoardSize.width) && (y >= 0) && (y < d_gameBoardSize.height)) {
               addPoint(x,y);
           }
       }
       
       public void removePoint(int x, int y) {
           point.remove(new Point(x,y));
       }
       
       public void resetBoard() {
           point.clear();
       }
       
       public void randomlyFillBoard(int percent) {
           for (int i=0; i<d_gameBoardSize.width; i++) {
               for (int j=0; j<d_gameBoardSize.height; j++) {
                   if (Math.random()*100 < percent) {
                       addPoint(i,j);
                   }
               }
           }
       }
       
       @Override
       public void paintComponent(Graphics g) {
           super.paintComponent(g);
           try {
               for (Point newPoint : point) {
                   // Draw new point
                   g.setColor(Color.blue);
                   g.fillRect(BLOCK_SIZE + (BLOCK_SIZE*newPoint.x), BLOCK_SIZE + (BLOCK_SIZE*newPoint.y), BLOCK_SIZE, BLOCK_SIZE);
               }
           } catch (ConcurrentModificationException cme) {}
           // Setup grid
           g.setColor(Color.BLACK);
           for (int i=0; i<=d_gameBoardSize.width; i++) {
               g.drawLine(((i*BLOCK_SIZE)+BLOCK_SIZE), BLOCK_SIZE, (i*BLOCK_SIZE)+BLOCK_SIZE, BLOCK_SIZE + (BLOCK_SIZE*d_gameBoardSize.height));
           }
           for (int i=0; i<=d_gameBoardSize.height; i++) {
               g.drawLine(BLOCK_SIZE, ((i*BLOCK_SIZE)+BLOCK_SIZE), BLOCK_SIZE*(d_gameBoardSize.width+1), ((i*BLOCK_SIZE)+BLOCK_SIZE));
           }
       }
       @Override
       public void componentResized(ComponentEvent e) {
           // Setup the game board size with proper boundries
           d_gameBoardSize = new Dimension(getWidth()/BLOCK_SIZE-2, getHeight()/BLOCK_SIZE-2);
           updateArraySize();
       }
       @Override
       public void componentMoved(ComponentEvent e) {}
       @Override
       public void componentShown(ComponentEvent e) {}
       @Override
       public void componentHidden(ComponentEvent e) {}
       @Override
       public void mouseClicked(MouseEvent e) {}
       @Override
       public void mousePressed(MouseEvent e) {}
       @Override
       public void mouseReleased(MouseEvent e) {
           // Mouse was released (user clicked)
           addPoint(e);
       }
       @Override
       public void mouseEntered(MouseEvent e) {}
       @Override
       public void mouseExited(MouseEvent e) {}
       @Override
       public void mouseDragged(MouseEvent e) {
           // Mouse is being dragged, user wants multiple selections
           addPoint(e);
       }
       @Override
       public void mouseMoved(MouseEvent e) {}
       @Override
       public void run() {
           boolean[][] gameBoard = new boolean[d_gameBoardSize.width+2][d_gameBoardSize.height+2];
           for (Point current : point) {
               gameBoard[current.x+1][current.y+1] = true;
           }
           ArrayList<Point> survivingCells = new ArrayList<Point>(0);
           // Iterate through the array, follow game of life rules
           for (int i=1; i<gameBoard.length-1; i++) {
               for (int j=1; j<gameBoard[0].length-1; j++) {
                   int surrounding = 0;
                   if (gameBoard[i-1][j-1]) { surrounding++; }
                   if (gameBoard[i-1][j])   { surrounding++; }
                   if (gameBoard[i-1][j+1]) { surrounding++; }
                   if (gameBoard[i][j-1])   { surrounding++; }
                   if (gameBoard[i][j+1])   { surrounding++; }
                   if (gameBoard[i+1][j-1]) { surrounding++; }
                   if (gameBoard[i+1][j])   { surrounding++; }
                   if (gameBoard[i+1][j+1]) { surrounding++; }
                   if (gameBoard[i][j]) {
                       // Cell is alive, Can the cell live? (2-3)
                       if ((surrounding == 2) || (surrounding == 3)) {
                           survivingCells.add(new Point(i-1,j-1));
                       } 
                   } else {
                       // Cell is dead, will the cell be given birth? (3)
                       if (surrounding == 3) {
                           survivingCells.add(new Point(i-1,j-1));
                       }
                   }
               }
           }
           resetBoard();
           point.addAll(survivingCells);
           repaint();
           try {
               Thread.sleep(1000/i_movesPerSecond);
               run();
           } catch (InterruptedException ex) {}
       }
   }

} </lang>