Game Of Life

1. 问题背景:Game Of Life

// 英国数学家发明的生命游戏 (Game of Life)
// 使用一个2维数组来实现其规则
// 在这个数组中,每个存储位子都能容纳一个LIFE元胞。世代(gennerations)用于标记时间
// 的流逝。每个世代都会LIFE社会带来生与死。
// 生死规则如下:
// * 定义的元胞都有8个邻居。上下左右,左上左下,右上右下八个方位。
// * 如果一个元胞有一个或零个邻居,会因为孤独而死亡。3个以上的邻居会因为拥挤而死亡。
// * 如果空元胞弟正好有3个邻居,会在空元胞的位子生成一个元胞。
// * 生生死死世代交换。

2. 问题解决策略:

本博客使用Java 搭载Java Swing实现本算法。

3. 软件完成效果如下:





4. 程序代码部分:

     核心代码Game.java

package com.game;

import java.util.ArrayList;
import java.util.Random;

import javax.swing.JFrame;

import com.game.enmu.State;

public class Game {
	private int row;
	private int column;
	private int numberRand;
	private boolean running;
	private Random rand;
	private ArrayList<ArrayList<State>> data;
	private ArrayList<CellStateOfSpecificPlace> back_data;
	private JFrame parent;
	private Thread generate;
	
	public Game(int row, int column, int numberRand){
		this.row = row;
		this.column = column;
		this.numberRand = numberRand;
		this.running = true;
		this.data = new ArrayList<ArrayList<State>>();
		rand = new Random();
		back_data = new ArrayList<CellStateOfSpecificPlace>();
		initinalGame();
		//runGame();
	}
	
	public Game(int row, int column, int numberRand, GameGUI gameGUI) {
		this(row, column, numberRand);
		parent = gameGUI;
	}
	
	/*
	 * initial the game with the specific policy.
	 * */
	public void initinalGame(){
		data.clear();
		for (int rowIndex = 0; rowIndex < row; rowIndex++){
			ArrayList<State> temp = new ArrayList<State>();
			for (int columnIndex = 0; columnIndex < column; columnIndex++){
				temp.add(State.DIE);
			}
			data.add(temp);
		}
		initialPolicy();
	}
	
	/*
	 * Below code is one kind of initial policy of Game Of Life, which you can 
	 * modified by yourself.
	 * */
	public void initialPolicy(){
		int margin = (int)Math.sqrt(numberRand);
		int rowPlace = rand.nextInt(row - margin - 1) + 1;
		int columnPlace = rand.nextInt(column - margin - 1) + 1;
		for (int rowIndex = rowPlace; rowIndex < rowPlace + margin; rowIndex++){
			for (int columnIndex = columnPlace; columnIndex < columnPlace + margin; columnIndex++){
				if (rand.nextBoolean()){
					data.get(rowIndex).set(columnIndex, State.ALIVE);
				} 
			}
		}
	}
	
	/*
	 * Below method run the code in a thread named generate.
	 * */
	public void runGame(){
		if (false == running){
			if (generate.isAlive()){
				generate.interrupt();
				generate = null;
			}
		} else {
			if (null == generate){
				generate = new Thread(new Runnable() {
					@Override
					public void run() {
						while(!Thread.interrupted()){
							//printGame();
							try {
								Thread.sleep(1000);
							} catch (InterruptedException e) {
								e.printStackTrace();
								Thread.currentThread().stop();
							}
							generated();
							if (null != parent){
								parent.repaint();
							}
						}
					}
				});
				generate.start();
			}
		}
	}
	
	/*
	 * generated method to generate the next generation based on current generation.
	 * */
	private void generated() {
		int neighbours = 0;
		State currentCellState = State.DIE;
		State nextCellState = State.DIE;
		back_data.clear();
		for (int rowIndex = 1; rowIndex < row - 1; rowIndex++){
			for (int columnIndex = 1; columnIndex < column - 1; columnIndex++){
				neighbours = getNeighborNumbers(rowIndex, columnIndex);
				currentCellState = data.get(rowIndex).get(columnIndex);
				nextCellState = nextGenerateState(currentCellState, neighbours);
				if (nextCellState != currentCellState){
					back_data.add(new CellStateOfSpecificPlace(rowIndex, columnIndex, nextCellState));
				}
			}
		}
		for (CellStateOfSpecificPlace item:back_data){
			data.get(item.getRowIndex()).set(item.getColumnIndex(), item.getState());
		}
	}
	
	/*
	 * Below method get the cell's neighbor number.
	 * */
	private int getNeighborNumbers(int rowIndex, int columnIndex){
		int counter = 0;
		for (int i = rowIndex - 1; i <= rowIndex + 1; i++){
			for (int j = columnIndex - 1; j <= columnIndex + 1; j++){
				if (State.ALIVE == data.get(i).get(j)){
					counter++;
				}
			}
		}
		counter += State.ALIVE == data.get(rowIndex).get(columnIndex) ? -1:0;
		return counter;
	}
	
	/*
	 * below code to debug the state of the game.
	 * */
	private void printGame(){
		System.out.println("-------------------");
		for (ArrayList<State> rowData : data){
			for (State ColumnData : rowData){
				System.out.print(ColumnData + " ");
			}
			System.out.println();
		}
		System.out.println("-------------------");
	}
	
	/*
	 * judge whether the next generation of cell should be alive.
	 * */
	public State nextGenerateState(State alive, int neighbourNumber) {
		if (State.ALIVE == alive && (2 == neighbourNumber || 3 == neighbourNumber)){
			return State.ALIVE;
		}
		if (State.DIE == alive && 3 == neighbourNumber){
			return State.ALIVE;
		}
		return State.DIE;
	}
	
	public int getRow() {
		return row;
	}

	public void setRow(int row) {
		this.row = row;
	}

	public int getColumn() {
		return column;
	}

	public ArrayList<ArrayList<State>> getData() {
		return data;
	}

	public boolean isRunning() {
		return running;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	public static void main(String[] args){
		Game game = new Game(15, 15, 25);
	}

}

   辅助类 CellStateOfSpecificPlace.java,用于记录上一代发生状态改变的位置:
package com.game;

import com.game.enmu.State;

public class CellStateOfSpecificPlace {
	private int rowIndex;
	private int columnIndex;
	private State state;
	
	public CellStateOfSpecificPlace() {
		this(0, 0, State.DIE);
	}

	public CellStateOfSpecificPlace(int rowIndex, int columnIndex, State state) {
		super();
		this.rowIndex = rowIndex;
		this.columnIndex = columnIndex;
		this.state = state;
	}

	public int getRowIndex() {
		return rowIndex;
	}

	public void setRowIndex(int rowIndex) {
		this.rowIndex = rowIndex;
	}

	public int getColumnIndex() {
		return columnIndex;
	}

	public void setColumnIndex(int columnIndex) {
		this.columnIndex = columnIndex;
	}

	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}
	
	
}

辅助类State.java,定义Cell的状态的枚举类型,可以用于非界面的控制台打印输出
package com.game.enmu;

public enum State {
	DIE,
	ALIVE;
	public String toString(){
		return name().substring(0, 1);
	}
}

GameBoard.java定义用于在Jswing中显示的包涵game的画图面板

package com.game;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

import com.game.enmu.State;

public class GameBoard extends JPanel{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Game game;  
	private int squre;
      
    public GameBoard() {  
        //this.setBounds(x, y, width, height);  
    }  
    public GameBoard(Game game){  
        this.game = game;  
    }  
      
    public void paint(Graphics g){  
        Graphics2D gg = (Graphics2D)g;  
        squre = (this.getWidth() - 50) / Math.max(game.getRow(), game.getColumn());   
        int x = 25;  
        int y = 10;  
        gg.setStroke(new BasicStroke(0.5f, BasicStroke.CAP_ROUND,   
                BasicStroke.JOIN_ROUND, 1.0f,new float[]{5f, 5f},0f));  
        gg.setColor(Color.blue);  
        for (int i = 0; i < game.getRow(); i++){  
            for (int j = 0; j < game.getColumn(); j++){  
                gg.drawRect(x + j * squre, y + i * squre, squre, squre);  
            }  
        }  
        for (int i = 0; i < game.getRow(); i++){  
            for (int j = 0; j < game.getColumn(); j++){  
                if (State.ALIVE == game.getData().get(i).get(j)){  
                    gg.setColor(Color.blue);  
                    gg.fillOval(x + j * squre, y + i * squre, squre, squre);  
                } else {  
                    gg.setColor(Color.WHITE);  
                    gg.fillOval(x + j * squre, y + i * squre, squre, squre);  
                } 
            }  
        }  
          
        gg.setStroke(new BasicStroke(5f));  
        gg.setColor(Color.blue);  
        gg.drawRect(x - 1, y - 1, squre * game.getColumn() + 2, squre * game.getRow() + 2);  
        gg.dispose();  
          
    }  
}

GameGUI.java 定义了软件的整体外观,软件的入口:

package com.game;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GameGUI extends JFrame{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JPanel mainframe;  
    private GameBoard board;  
    private JPanel control;   
    private BorderLayout border;  
    private JButton start;  
    private JButton stop;
    private JButton exit;  
    private JButton restart;
    private int row;  
    private int column;  
    private Game game;   
    private ActionListener actionlistener;
	
	public GameGUI(String title, int row, int column){
		super(title);
		this.row = row;
		this.column = column;
		initial();  
        setSize(900, 900);  
        this.setResizable(false);  
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
        setLocationByPlatform(true);  
        this.setVisible(true);  
        this.requestFocus(); 
	}
	
	

	private void initial() {
		createComponent();  
        layOut();  
        listener();  
	}



	private void createComponent() {
		mainframe = new JPanel();  
        control = new JPanel();  
        border = new BorderLayout();   
        start = new JButton("Start");    
        game = new Game(this.row, this.column, 81, this);  
        board = new GameBoard(game);  
        stop = new JButton("Stop");
        exit = new JButton("Exit");
        restart = new JButton("Re-Start");
	}



	private void layOut() {
		this.getContentPane().add(mainframe);   
        mainframe.setLayout(border);  
        mainframe.add(board, BorderLayout.CENTER);   
        mainframe.add(control, BorderLayout.EAST);  
        Box ve = Box.createVerticalBox();  
        ve.add(start);  
        ve.add(Box.createVerticalStrut(50));  
        stop.setSize(start.getWidth(), start.getHeight());  
        ve.add(stop); 
        ve.add(Box.createVerticalStrut(50));
        ve.add(exit);
        ve.add(Box.createVerticalStrut(50));
        ve.add(restart);
        control.add(ve);
        
	}



	private void listener() {
        actionlistener = new ActionListener() {   
            @Override  
            public void actionPerformed(ActionEvent e) {  
                if (((JButton)(e.getSource())).getText().equals("Start")){  
                    game.setRunning(true); 
                    game.runGame();
                } else if (((JButton)(e.getSource())).getText().equals("Exit")){  
                    System.exit(0);  
                } else if (((JButton)(e.getSource())).getText().equals("Stop")){  
                	game.setRunning(false); 
                    game.runGame();
                } else if (((JButton)(e.getSource())).getText().equals("Re-Start")){  
                	game.setRunning(true);
                	game.initinalGame();
                	repaint();
                    game.runGame();
                }
            }  
        };   
        start.addActionListener(actionlistener);  
        stop.addActionListener(actionlistener); 
        restart.addActionListener(actionlistener); 
        exit.addActionListener(actionlistener);
	}



	public static void main(String[] args) {
		GameGUI game = new GameGUI("Game Of Life", 25, 25);

	}

}

最后一点测试代码,算是TDD(测试驱动开发):

package com.game.test;

import static org.junit.Assert.*;

//import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.game.Game;
import com.game.enmu.State;

public class TestGame {

	private Game mockGame;
	private Game game;


	@Before
	public void setUp() throws Exception {
		//mockGame = EasyMock.createMock(Game.class);
		//mockGame.initinalGame();
		game = new Game(5, 5, 9);
	}

	@After
	public void tearDown() throws Exception {
	}
	
	
	@Test
	public void AliveCellMoreThanThreeNeibourShouldBeDie() {
		State result = game.nextGenerateState(State.ALIVE, 3 + 1);
		assertEquals(State.DIE, result);
	}
	
	@Test
	public void AliveCellLessThanOneNeibourShouldBeDie() {
		State result = game.nextGenerateState(State.ALIVE, 1);
		assertEquals(State.DIE, result);
	}
	
	@Test
	public void AliveCellWithTwoOrThreeNeibourShouldBeAlive() {
		State result = game.nextGenerateState(State.ALIVE, 2);
		assertEquals(State.ALIVE, result);
		result = game.nextGenerateState(State.ALIVE, 3);
		assertEquals(State.ALIVE, result);
	}
	
	@Test
	public void DieCellWithThreeNeibourShouldBeAlive() {
		State result = game.nextGenerateState(State.DIE, 3);
		assertEquals(State.ALIVE, result);
	}

}

     有好久没有写博客了,感谢你的再次惠顾。



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值