Follow QueSucede on Twitter
    hacker emblem

    Conway's Game of Life - Java applet and source code

    IconDetails...




    Conway's Game of Life - Java applet source code

    Conway's Game of Life is an example (and probably the best known one) of a cellular automaton. For a more in-depth overview of the game, check out the article at Wikipedia for Conway's Game of Life.

    Screen shot of Conway's Game of Life Java applet

    Conway's Game of Life Java applet

    SwingLife.java

    /*
    Copyright (c) 2010, Brett Alistair Kromkamp - brettkromkamp@gmail.com
    All rights reserved.
    
    Redistribution and use in source and binary forms, with or without modification,
    are permitted provided that the following conditions are met:
    
    Redistributions of source code must retain the above copyright notice, this list
    of conditions and the following disclaimer.
    
    Redistributions in binary form must reproduce the above copyright notice, this
    list of conditions and the following disclaimer in the documentation and/or
    other materials provided with the distribution.
    
    Neither the name of the copyright holder nor the names of the contributors
    may be used to endorse or promote products derived from this software without
    specific prior written permission.
    
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    */
    
    import java.awt.*;
    import javax.swing.*;
    import java.awt.event.*;
    import javax.swing.event.*;
    import java.awt.event.MouseListener;
    import java.util.Hashtable;
    
    public class SwingLife extends JApplet {
        LifePanel lifePanel;
        
        public void init() {
            Container contentPane = getContentPane();
            lifePanel = new LifePanel();
            contentPane.add(lifePanel);
            lifePanel.startAnimation();
        }
        
        public String getAppletInfo() {
            return "Title: Swing Game of Life Applet v1.0b, 20 Oct 2000. "
                + "Author: Brett Alistair Kromkamp. "
                + "Copyright (C) 2000 - Brett Alistair Kromkamp. "
                + "A simple Swing applet demonstrating Conway's Game of Life.";
        }
        
        public String[][] getParameterInfo() {
            String [][] info = {{"NONE", "NONE", "This applet takes no HTML-parameters"}};
            return info;
        }
    }
    
    class LifeGridComponent extends JComponent {
        final static int XSIZE = 362; // class constants
        final static int YSIZE = 162;
        private Dimension preferredSize = new Dimension(XSIZE, YSIZE);
        private Cursor cursor;
        private int row;
        private int col;
        private int size;
        private LifePanel lp;
        
        public LifeGridComponent(LifePanel newPanel, int newRow, int newCol, int newSize) {
            setPreferredSize(preferredSize);
            setMinimumSize(preferredSize);
            setCursor(cursor.getPredefinedCursor(cursor.CROSSHAIR_CURSOR));
            this.row = newRow;
            this.col = newCol;
            this.size = newSize;
            this.lp = newPanel;
        }
        
        public void paint(Graphics g) {
            int i, j;
            for (i = 0; i < row; i++) {
                for (j = 0; j < col; j++) {
                    if (lp.grid[i][j] != 0) {
                        switch(lp.cellShape) {
                            case 0: 
                                g.drawRoundRect((j * size) +1, 
                                    (i * size) +1, size, size, 2, 2);
                                g.drawRect((j * size) +1, 
                                    (i * size) +1, size -2, size -2); 
                                break;
                            case 1: 
                                g.drawRect((j * size) +1, 
                                    (i * size) +1, size -2, size -2); 
                                break;
                            case 2: // debug: should be drawPolyLine
                                g.drawLine((j * size) + (size / 2), 
                                    (i * size) +1, (j * size), 
                                    (i * size) + (size / 2) +1);
                                g.drawLine((j * size), (i * size) + (size / 2) +1, 
                                    (j * size) + size, (i * size) + (size / 2) +1);
                                g.drawLine((j * size) + size, 
                                    (i * size) + (size / 2) +1, 
                                    (j * size) + (size / 2), 
                                    (i * size) +1);
                                break;
                        }
                    }
                }
            }
            g.drawRect(0, 0, XSIZE -1, YSIZE -1); // debug
        }
        
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            // additional paint code
        }
    }
    
    class LifePanel extends JPanel implements ActionListener {
        
        class SliderListener implements ChangeListener {
            public void stateChanged(ChangeEvent e) {
                JSlider source = (JSlider)e.getSource();
                if (source.getValueIsAdjusting()) {
                    if (source == minSlider) {
                        min = (int)source.getValue();
                    } else if (source == maxSlider) {
                        max = (int)source.getValue();
                    } else if (source == hitSlider) {
                        hit = (int)source.getValue();
                    } else if (source == animSlider) {
                        int fps = (int)source.getValue();
                        if (fps == 0) {
                            if (!frozen) stopAnimation();
                        } else {
                            delay = 4000 / fps;
                            timer.setDelay(delay);
                            timer.setInitialDelay(4000 / INIT_FPS);
                            if (frozen) {
                                startAnimation();
                            }
                        }
                    }
                }
            }
        }
        
        static final int ROW = 20; // class constants
        static final int COL = 45;
        static final int SIZE = 8;
        static final int INIT_FPS = 10;
        
        public int[][] grid;
        public int cellShape = 0;
        public boolean colorCoded = false;
        
        private int[][] gridCopy;
        private int min = 2;
        private int max = 3;
        private int hit = 3;
        private int generationCount = 0;
        private String[] shapeStrings = {"3D-block", "Rectangle", "Triangle"};
        
        private Timer timer;
        private int delay = 4000 / INIT_FPS;
        private boolean frozen = false;
        
        private GridBagLayout gridbag = new GridBagLayout();
        private GridBagConstraints c = new GridBagConstraints();
        
        JLabel titleLabel; // components
        JLabel minSliderLabel;
        JLabel maxSliderLabel;
        JLabel hitSliderLabel;
        JLabel animSliderLabel;
        JLabel shapeLabel;
        JLabel countLabel;
        JLabel counter;
        JSlider minSlider;
        JSlider maxSlider;
        JSlider hitSlider;
        JSlider animSlider;
        JComboBox shapeCombo;
        LifeGridComponent gridComponent;
        JCheckBox colorCheckBox;
        
        LifePanel() { // constructor
            timer = new Timer(delay, this);
            timer.setInitialDelay(delay * 10);
            timer.setCoalesce(true); // ???
        
            grid = new int [ROW][COL]; // initialize data structures
            gridCopy = new int [ROW][COL];
        
            initGrid(grid);
            grid[8][(COL / 2) -1] = 1;
            grid[8][(COL / 2) +1] = 1;
            grid[9][(COL / 2) -1] = 1;
            grid[9][(COL / 2) +1] = 1;
            grid[10][(COL / 2) -1] = 1;
            grid[10][(COL / 2)] = 1;
            grid[10][(COL / 2) +1] = 1;
        
            setLayout(gridbag);
        
            // create and initialize components
            titleLabel = new JLabel("Java 2 (Swing) Game of Life Applet");
            c.gridx = 0;
            c.gridy = 0;
            c.gridwidth = 2;
            c.insets = new Insets (2, 0, 0, 0);
            c.anchor = GridBagConstraints.WEST;
            gridbag.setConstraints(titleLabel, c);
            add(titleLabel);
        
            gridComponent = new LifeGridComponent(this, ROW, COL, SIZE);
            c.gridx = 0;
            c.gridy = 1;
            gridbag.setConstraints(gridComponent, c);
            add(gridComponent);
        
            minSliderLabel = new JLabel("MIN-variable: ");
            c.gridx = 0;
            c.gridy = 2;
            c.gridwidth = 1;
            gridbag.setConstraints(minSliderLabel, c);
            add(minSliderLabel);
        
            minSlider = new JSlider(JSlider.HORIZONTAL, 0, 8, min);
            minSlider.addChangeListener(new SliderListener());
            minSlider.setToolTipText("Under-population threshold (deathrate)");
            minSlider.setMajorTickSpacing(2);
            minSlider.setMinorTickSpacing(1);
            minSlider.setPaintTicks(true);
            minSlider.setPaintLabels(true);
            minSlider.setSnapToTicks(true);
            c.gridx = 1;
            c.gridy = 2;
            gridbag.setConstraints(minSlider, c);
            add(minSlider);
        
            maxSliderLabel = new JLabel("MAX-variable: ");
            c.gridx = 0;
            c.gridy = 3;
            gridbag.setConstraints(maxSliderLabel, c);
            add(maxSliderLabel);
        
            maxSlider = new JSlider(JSlider.HORIZONTAL, 0, 8, max);
            maxSlider.addChangeListener(new SliderListener());
            maxSlider.setToolTipText("Over-population threshold (deathrate)");
            maxSlider.setMajorTickSpacing(2);
            maxSlider.setMinorTickSpacing(1);
            maxSlider.setPaintTicks(true);
            maxSlider.setPaintLabels(true);
            maxSlider.setSnapToTicks(true);
            c.gridx = 1;
            c.gridy = 3;
            gridbag.setConstraints(maxSlider, c);
            add(maxSlider);
        
            hitSliderLabel = new JLabel("HIT-variable: ");
            c.gridx = 0;
            c.gridy = 4;
            gridbag.setConstraints(hitSliderLabel, c);
            add(hitSliderLabel);
    
            hitSlider = new JSlider(JSlider.HORIZONTAL, 0, 8, hit);
            hitSlider.addChangeListener(new SliderListener());
            hitSlider.setToolTipText("Hit threshold (birthrate)");
            hitSlider.setMajorTickSpacing(2);
            hitSlider.setMinorTickSpacing(1);
            hitSlider.setPaintTicks(true);
            hitSlider.setPaintLabels(true);
            hitSlider.setSnapToTicks(true);
            c.gridx = 1;
            c.gridy = 4;
            gridbag.setConstraints(hitSlider, c);
            add(hitSlider);
        
            animSliderLabel = new JLabel("Animation speed: ");
            c.gridx = 0;
            c.gridy = 5;
            gridbag.setConstraints(animSliderLabel, c);
            add(animSliderLabel);
        
            animSlider = new JSlider(JSlider.HORIZONTAL, 0, 30, INIT_FPS);
            animSlider.addChangeListener(new SliderListener());
            Hashtable animLabelTable = new Hashtable();
            animLabelTable.put(new Integer(0), new JLabel("Stop"));
            animLabelTable.put(new Integer(10), new JLabel("Slow"));
            animLabelTable.put(new Integer(30), new JLabel("Fast"));
            animSlider.setLabelTable(animLabelTable);
            animSlider.setToolTipText("Calculation/animation speed");
            animSlider.setMajorTickSpacing(10);
            animSlider.setMinorTickSpacing(5);
            animSlider.setPaintTicks(true);
            animSlider.setPaintLabels(true);
            c.gridx = 1;
            c.gridy = 5;
            gridbag.setConstraints(animSlider, c);
            add(animSlider);
        
            shapeLabel = new JLabel("Cell shape: ");
            c.gridx = 0;
            c.gridy = 6;
            gridbag.setConstraints(shapeLabel, c);
            add(shapeLabel);
        
            shapeCombo = new JComboBox(shapeStrings);
            shapeCombo.setToolTipText("Cells' visual shape");
            c.gridx = 1;
            c.gridy = 6;
            gridbag.setConstraints(shapeCombo, c);
            add(shapeCombo);
        
            shapeCombo.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    JComboBox cb = (JComboBox)e.getSource();
                    cellShape = cb.getSelectedIndex();
                }
            });
        
            colorCheckBox = new JCheckBox("Color coded");
            colorCheckBox.setSelected(false);
            colorCheckBox.setToolTipText("Population density color coding (not implemented)");
            c.gridx = 1;
            c.gridy = 12;
            gridbag.setConstraints(colorCheckBox, c);
            add(colorCheckBox);
        
            colorCheckBox.addItemListener(new ItemListener() {
                public void itemStateChanged(ItemEvent e) {
                    JCheckBox cb = (JCheckBox)e.getSource();
                    colorCoded = !colorCoded;
                }
            });
    
            countLabel = new JLabel("Generation #: ");
            c.gridx = 0;
            c.gridy = 14;
            gridbag.setConstraints(countLabel, c);
            add(countLabel);
        
            counter = new JLabel("0");
            counter.setToolTipText("Current generation count");
            c.gridx = 1;
            c.gridy = 14;
            gridbag.setConstraints(counter, c);
            add(counter);
        }
        
        public void actionPerformed(ActionEvent e) {
            nextGeneration();
            gridComponent.repaint();
            generationCount++;
            counter.setText(Integer.toString(generationCount));
        }
        
        public void startAnimation() {
            timer.start();
            frozen = false;
        }
        
        public void stopAnimation() {
            timer.stop();
            frozen = true;
        }
        
        private int calc(int y, int x) {
            int m, n, total;
        
            total = (grid[y][x] != 0) ? -1 : 0;
            for (m = -1; m <= +1; m++) {
                for (n = -1; n <= +1; n++) {
                    if (grid[(ROW + (y + m)) % ROW][(COL + (x + n)) % COL] != 0) {
                        total++;
                    }
                }
            }
            return total;
        }
        
        private void duplicateGrid(int[][] source, int[][] dest) {
            int i, j;
            for (i = 0; i < ROW; i++) {
                for (j = 0; j < COL; j++) {
                    dest[i][j] = source[i][j];
                }
            }    
        }
        
        public void nextGeneration() {
            int i, j, neighbors;
            initGrid(gridCopy);
            for (i = 0; i < ROW; i++) {
                for (j = 0; j < COL; j++) {
                    neighbors = calc(i, j);
                
                    if (grid[i][j] != 0) {
                        if ((neighbors >= min) && (neighbors <= max)) {
                            gridCopy[i][j] = neighbors;
                        }
                    } else {
                        if (neighbors == hit) {
                            gridCopy[i][j] = hit;
                        }
                    }
                }
            }
            initGrid(grid);
            duplicateGrid(gridCopy, grid);
        }
        
        private void initGrid(int[][] matrix) {
            int i, j;
            for (i = 0; i < ROW; i++) {
                for (j = 0; j < COL; j++) {
                    matrix[i][j] = 0;
                }
            }
        }
        
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            // additional paint code
        }
    }
    

    Comments
    Game of Life Flex 4 implementation@10-05-27 16:50:57 by Admin user a.k.a. Brett Kromkamp

    Within the next couple of days I will start the Flex 4 implementation (and accompanying documentation) of the Game of Life - the problem space is small enough to allow me to only focus on Flex 4 while still including sufficient elements to make for both an interesting and valid learning experience. For example, the application includes:

    • a 'game' loop (timer)
    • animations (grid updates)
    • algorithm to calculate the next generation's state
    • components and controls with binding

    Flex 4 (2010) vs. Java (circa 2000)@10-05-27 00:33:20 by Admin user a.k.a. Brett Kromkamp

    An additional reason for implementing the Game of Life with Flex is to compare Flex development in 2010 with Java development from the year 2000 (when I originally implemented the Game of Life Java applet). It should be quite interesting to see how things have come on in these last 10 years (or perhaps they haven't - the more things change, the more they stay the same).


    Flex 4 implementation of Conway's Game of Life@10-05-27 22:53:14 by Admin user a.k.a. Brett Kromkamp

    I've started to implement Conway's Game of Life as a Flex 4 application - a learning excercise.


    Game of Life for Google Android@10-05-30 16:29:45 by Admin user a.k.a. Brett Kromkamp

    So much programming, so little time: at the moment, I want to focus on Android development and it really makes sense that I get to grip with Android as soon as possible, hence I will first be implementing the Game of Life for Google Android before I implement it with Flex 4.



    Click here to be able to create pages, upload images and file attachments, and link to other users and their pages.


    blog comments powered by Disqus