The Game of Life

Will be posted in the Code Snippet

Page 1 of 1

2 Replies - 3961 Views - Last Post: 13 May 2010 - 10:26 AM Rate Topic: -----

#1 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8066
  • View blog
  • Posts: 31,309
  • Joined: 06-March 08

The Game of Life

Posted 08 October 2009 - 09:16 PM

*
POPULAR

Hello

I have received a "personal mail" asking me for help about the Game of Life

I usually do not answer to personal mails because, if I did, I would not have time to post in the forum, but this time the problem was really interesting and I also think that my solution was interesting

Wikipedia defines the Game of Life that way

The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells,
each of which is in one of two possible states, live or dead.
Every cell interacts with its eight neighbors, which are the cells that are directly horizontally,
vertically, or diagonally adjacent. At each step in time, the following transitions occur:

1.Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
2.Any live cell with more than three live neighbours dies, as if by overcrowding.
3.Any live cell with two or three live neighbours lives on to the next generation.
4.Any dead cell with exactly three live neighbours becomes a live cell.
The initial pattern constitutes the seed of the system. The first generation is created by applying the
above rules simultaneously to every cell in the seed—births and deaths happen simultaneously,
and the discrete moment at which this happens is sometimes called a tick
(in other words, each generation is a pure function of the one before).
The rules continue to be applied repeatedly to create further generations.


This is the author original code (I have removed and added some comments in the code)
import java.awt.*;
import javax.swing.*;


public class GameOfLife extends JFrame {

	// size in pixel of every label
	static final int size = 15;
	public static final int NB_ROWS = 30, NB_COLUMNS = 50;
	private static final int DEAD = 0, ALIVE = 1;

	// the cells labels
	private JLabel[][] label;
	// weneed 2 states one which is the actual one and one for the next run
	private int[][] state, newState;

	GameOfLife() {
		super("GameOfLife");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		// create the labels
		label = new JLabel[NB_ROWS][NB_COLUMNS];
		state = new int[NB_ROWS][NB_COLUMNS];
		newState = new int[NB_ROWS][NB_COLUMNS];
		for(int i = 0; i < NB_ROWS; i++) {
			for(int j = 0; j < NB_COLUMNS; j++) {
				state[i][j] = DEAD;
				newState[i][j] = DEAD;
				label[i][j] = new JLabel();
				label[i][j].setOpaque(true);
				label[i][j].setBackground(Color.LIGHT_GRAY);
				add(label[i][j]);
			}
		}
		// setup a few cells on to start

		pack();
	}

	// play the game
	public void run() {
		while(true) {
			// this is where it is too complicated
			for(int i = 0; i < NB_ROWS; i++) {
				for(int j = 0; j < NB_COLUMNS; j++) {
					int nbDead = 0;
					int nbAlive = 0;
					// test if there is a cell left of me
					if(i > 0) {
						if(state[i-1][j] == ALIVE)
							nbAlive++;
						else
							nbDead++;
					}
					// test if there is a cell right of me
					if(i < NB_ROWS -1) {
						if(state[i+1][j] == ALIVE)
							nbAlive++;
						else
							nbDead++;
					}
					// test if a cell over me...
					// test if a cell below me...
					// test if a cell up left...
					// test if a cell up right...
					// test if a cell down left..
					if(i > 0 && j < 0) {
						
					}
					// test if a cell down right...

					// now perform test if the cell should stay alive or not
					// newState
					if(state[i][j] == 1) {				// if alive
						if(nbAlive < 2)				// 1.Any live cell with fewer than two live neighbours dies
							newState[i][j] = DEAD;
						if(nbAlive > 3)				// 2.Any live cell with more than three live neighbours dies
							newState[i][j] = DEAD;
					}
					else {
						if(nbAlive == 3)			// 4.Any dead cell with exactly three live neighbours becomes a live cell
							newState[i][j] = ALIVE;
					}

				}
			}
			// update the status
			for(int i = 0; i < NB_ROWS; i++) {
				for(int j = 0; j < NB_COLUMNS; j++) {
					state[i][j] = newState[i][j];
					if(state[i][j] == DEAD) {
						label[i][j].setBackground(Color.LIGHT_GRAY);
						label[i][j].repaint();
					}
					if(state[i][j] == ALIVE) {
						label[i][j].setBackground(Color.BLUE);
						label[i][j].repaint();
					}
				}
			}

			try {
				Thread.sleep(2000);
			}
			catch(Exception e) {
				System.out.println("Error in sleep");
			}


			public static void main(String[] arg) {
				GameOfLife gameOfLife =	new GameOfLife();
				gameOfLife.setVisible(true);
				gameOfLife.run();
			}
		}



The problems he/she was encountering:
- logic was very complicated when testing the around cells where you are on the first/last row or column
- how to start randomly the life cell when starting the game ?

I fixed these 2 problems in 3 interesting ways (I think)
- first I added 2 additionnal columns and rows, not showed by the GUI, at the left, right, top bottom so no need to check if at a border
- second I delegate to a class that extends JLabel the role of checking if a cell should stay alive or not with the addNeighbour() method
- third I add a MouseListener to the cells so you can select, on the fly, which cells you want alive

For the fun of it, I added:
- buttons to stop/clear the process
- a Slider to select the speed at which each generation are produced
- a label displaying the generation number

I used a Swing timer to control the speed at which each generation is created

To do: a way to figure out if the system is frozen
To do: a way to figure out if the system oscillated to infinity

I have tried to respect original author variables names but parameterized the size of the grid in the main() method

Here is the code: enjoy.... I will post it in the Code Snippet
import java.awt.*;
import java.awt.event.*;
import java.util.Hashtable;

import javax.swing.*;


/*
The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, 
each of which is in one of two possible states, live or dead. 
Every cell interacts with its eight neighbors, which are the cells that are directly horizontally, 
vertically, or diagonally adjacent. At each step in time, the following transitions occur:

1.Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
2.Any live cell with more than three live neighbours dies, as if by overcrowding.
3.Any live cell with two or three live neighbours lives on to the next generation.
4.Any dead cell with exactly three live neighbours becomes a live cell.
The initial pattern constitutes the seed of the system. The first generation is created by applying the 
above rules simultaneously to every cell in the seed—births and deaths happen simultaneously, 
and the discrete moment at which this happens is sometimes called a tick 
(in other words, each generation is a pure function of the one before). 
The rules continue to be applied repeatedly to create further generations.
 */

public class GameOfLife2 extends JFrame implements ActionListener {
	static final Color[] color = {Color.LIGHT_GRAY, Color.BLUE};
	// size in pixel of every label
	static final int size = 15;
	static final Dimension dim = new Dimension(size, size);

	// the cells labels
	private LifeLabel[][] label;
	// timer that fires the next feneration
	private Timer timer;
	// generation counter
	private int generation = 0;
	private JLabel generationLabel = new JLabel("Generation: 0");
	// the 3 buttons
	private JButton bClear = new JButton("Clear"), 
					bPause = new JButton("Pause"), 
					bGo = new JButton("Go");
	// the slider for the speed
	JSlider slider = new JSlider(0, 5000);	// 0 to 5000 milliseconds (5 seconds)
	// state of the game (running or pause)
	private boolean gameRunning = false;
	// if the mouse is down or not
	private boolean mouseDown = false;

	GameOfLife2(int nbRow, int nbCol) {
		super("GameOfLife");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		// create the labels (2 more on each size) these wont be shown
		// but will be used in calculating the cells alive around
		label = new LifeLabel[nbRow+2][nbCol+2];
		for(int r = 0; r < nbRow+2; r++) {
			for(int c = 0; c < nbCol+2; c++) {
				label[r][c] = new LifeLabel();
			}
		}

		// panel in the center with the labels
		JPanel panel = new JPanel(new GridLayout(nbRow, nbCol, 1, 1));
		panel.setBackground(Color.BLACK);
		panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

		// add each label (not the one on the border) to the panel and add to each of them its neighbours
		for(int r = 1; r < nbRow+1; r++) {
			for(int c = 1; c < nbCol+1; c++) {
				panel.add(label[r][c]);
				label[r][c].addNeighbour(label[r-1][c]); 	// North
				label[r][c].addNeighbour(label[r+1][c]); 	// South
				label[r][c].addNeighbour(label[r][c-1]); 	// West
				label[r][c].addNeighbour(label[r][c+1]); 	// East
				label[r][c].addNeighbour(label[r-1][c-1]); 	// North West
				label[r][c].addNeighbour(label[r-1][c+1]); 	// North East
				label[r][c].addNeighbour(label[r+1][c-1]); 	// South West
				label[r][c].addNeighbour(label[r+1][c+1]); 	// South East
			}
		}

		// now the panel can be added
		add(panel, BorderLayout.CENTER);
		
		// the bottom panel with the buttons the generation label and the slider
		// this panel is formed grid panels 
		panel = new JPanel(new GridLayout(1,3));
		// another panel for the 3 buttons
		JPanel buttonPanel = new JPanel(new GridLayout(1,3));
		bClear.addActionListener(this);
		buttonPanel.add(bClear);
		bPause.addActionListener(this);
		bPause.setEnabled(false);			// game is pause the pause button is disabled
		buttonPanel.add(bPause);
		bGo.addActionListener(this);
		buttonPanel.add(bGo);
		// add the 3 buttons to the panel
		panel.add(buttonPanel);
		// the generation label
		generationLabel.setHorizontalAlignment(SwingConstants.CENTER);
		panel.add(generationLabel);
		// the slider
		slider.setMajorTickSpacing(1000);
		slider.setMinorTickSpacing(250);
		slider.setPaintTicks(true);
		// the labels for the Slider
		Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
		for(int i = 0; i <= 5; i++) {
			labelTable.put( new Integer( i * 1000 ), new JLabel("" + i) );
		}
		slider.setLabelTable( labelTable );
		slider.setPaintLabels(true);
		
		panel.add(slider);
		// in the JFrame
		add(panel, BorderLayout.SOUTH);
		
		// put the frame on
		setLocation(20, 20);
		pack();
		setVisible(true);
		// start the thread that run the cycles of life
		timer = new Timer(5000 - slider.getValue(), this);
	}

	// called by the Timer and the JButtons
	public synchronized void actionPerformed(ActionEvent e) {
		// test the JButtons first
		Object o = e.getSource();
		// the clear button
		if(o == bClear) {
			timer.stop();					// stop timer
			gameRunning = false;			// flag gamme not running
			bPause.setEnabled(false);		// disable pause button
			bGo.setEnabled(true);			// enable go button
			// clear all cells
			for(int r = 1; r < label.length -1; r++) {
				for(int c = 1; c < label[r].length -1; c++) {
					label[r][c].clear();
				}
			}
			// reset generation number and its label
			generation = 0;
			generationLabel.setText("Generation: 0");
			return;
		}
		// the pause button
		if(o == bPause) {
			timer.stop();					// stop timer
			gameRunning = false;			// flag not running
			bPause.setEnabled(false);		// disable myself
			bGo.setEnabled(true);			// enable go button
			return;
		}
		// the go button
		if(o == bGo) {
			bPause.setEnabled(true);				// enable pause button
			bGo.setEnabled(false);					// disable myself
			gameRunning = true;						// flag game is running
			timer.setDelay(5000 - slider.getValue());
			timer.start();
			return;
		}
		// not a JButton so it is the timer
		// set the delay for the next time
		timer.setDelay(5000 - slider.getValue());
		// if the game is not running wait for next time
		if(!gameRunning)
			return;
		++generation;
		generationLabel.setText("Generation: " + generation);
		for(int r = 0; r < label.length; r++) {
			for(int c = 0; c < label[r].length; c++) {
				label[r][c].checkState();
			}
		}
		for(int r = 0; r < label.length; r++) {
			for(int c = 0; c < label[r].length; c++) {
				label[r][c].updateState();
			}
		}
	}

	// to start the whole thing as a Java application
	public static void main(String[] arg) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new GameOfLife2(30, 50);
			}
		});	
	}

	// A class that extends JLabel but also check for the neigbour 
	// when asked to do so
	class LifeLabel extends JLabel implements MouseListener {
		private int state, newState;
		private int nbNeighbour;
		private LifeLabel[] neighbour = new LifeLabel[8];

		LifeLabel() {
			state = newState = 0;			// Dead
			setOpaque(true);				// so color will be showed
			setBackground(color[0]);
			addMouseListener(this);			// to select new LIVE cells
			this.setPreferredSize(dim);
		}
		// to add a neibour
		void addNeighbour(LifeLabel n) {
			neighbour[nbNeighbour++] = n;
		}
		// to see if I should live or not
		void checkState() {
			// number alive around
			int nbAlive = 0;
			// see the state of my neighbour
			for(int i = 0; i < nbNeighbour; i++)
				nbAlive += neighbour[i].state;
			// newState
			if(state == 1) {				// if alive
				if(nbAlive < 2)				// 1.Any live cell with fewer than two live neighbours dies
					newState = 0;
				if(nbAlive > 3)				// 2.Any live cell with more than three live neighbours dies
					newState = 0;
			}
			else {
				if(nbAlive == 3)			// 4.Any dead cell with exactly three live neighbours becomes a live cell
					newState = 1;
			}
		}
		// after the run switch the state to new state
		void updateState() {
			if(state != newState) {		// do the test to avoid re-setting same color for nothing	
				state = newState;
				setBackground(color[state]);
			}
		}

		// called when the game is reset/clear
		void clear() {
			if(state == 1 || newState == 1) {
				state = newState = 0;
				setBackground(color[state]);
			}
		}
		@Override
		public void mouseClicked(MouseEvent arg0) {
		}
		// if the mouse enter a cell and it is down we make the cell alive
		public void mouseEntered(MouseEvent arg0) {
			if(mouseDown) {
				state = newState = 1;
				setBackground(color[1]);				
			}
		}
		@Override
		public void mouseExited(MouseEvent arg0) {
		}
		// if the mouse is pressed on a cell you register the fact that it is down
		// and make that cell alive
		public void mousePressed(MouseEvent arg0) {
			mouseDown = true;
			state = newState = 1;
			setBackground(color[1]);
		}
		// turn off the fact that the cell is down
		public void mouseReleased(MouseEvent arg0) {
			mouseDown = false;
		}		
	}
}



Is This A Good Question/Topic? 6
  • +

Replies To: The Game of Life

#2 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8066
  • View blog
  • Posts: 31,309
  • Joined: 06-March 08

Re: The Game of Life

Posted 12 May 2010 - 10:16 PM

@Aziz.Ahmed why are you giving me today a -1 for a post 6 months old ?
Was This Post Helpful? 0
  • +
  • -

#3 Dogstopper  Icon User is offline

  • The Ninjaducky
  • member icon



Reputation: 2706
  • View blog
  • Posts: 10,578
  • Joined: 15-July 08

Re: The Game of Life

Posted 13 May 2010 - 10:26 AM

I am going to add this to the Java FAQs. We see this issue frequently enough.
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1