生命游戏

1. 生命游戏简介:

生命游戏(Life Game)是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。它是由3条规则构成的二维元胞自动机(2D Cellular Automata)。

2. 原理

在有N*N个格子的平面上,把每一个格子都可以看成是一个生命体,每个生命都有生和死两种状态,如果该格子生就显示蓝色,死则显示白色。每一个格子旁边都有邻居格子存在,如果我们把3*3的9个格子构成的正方形看成一个基本单位的话,那么这个正方形中心的格子的邻居就是它旁边的8个格子。
每个格子的生死遵循下面的原则:
1. 如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,则保持不变) 。
2. 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;
3. 在其它情况下,该细胞为死(即该细胞若原先为生,则转为死,若原先为死,则保持不变)

3. 代码实现

功能:

1.全屏显示滑翔机发射器的演化过程如下图:

滑翔机发射器演示图

2. 按“Esc”键程序退出

类说明:
Canvas.java 画布类,封装了细胞演化的算法
Cell.java 细胞单元的类
Constant.java 常量类
MainFrame.java 主控程序

算法思路:
用三个链表,cellList(活着的细胞),bornCellList(刚出生的细胞),deadCellList(死去得细胞),遍历cellList,对每个细胞按规则进行繁衍,繁衍一代后,处理cellList,从中去掉deadCellList,加入bornCellList。在如此可完成一轮的繁衍,其中还要用到一个二维数组flagArr来保存细胞状态。
二维数组表示细胞的状态
//取值为1 和 2 1:当代存活的细胞 2:刚产生的下一代细胞
int [][] flagArr;
活着的细胞链表
List cellList = new LinkedList();
新生代的细胞链表
List bornCellList = new LinkedList();
死亡的细胞链表
List deadCellList = new LinkedList();

  1. 清空bornCellList,deadCellList
  2. 遍历cellList,对每个细胞按规则进行演化
  3. 2步骤后deadCellList里面会有一些死去的细胞,遍历deadCellList,将其中细胞的状态标记到flagArr中。
    for(Cell cel:deadCellList)
    flagArr[cel.row][cel.col] = 0;
    从活着的细胞列表中去掉在下一代将要死掉的
    cellList.removeAll(deadCellList);
    将下一代新生成的细胞标记为当代存活的细胞
    for(Cell cel:bornCellList)
    flagArr[cel.row][cel.col]=1;
    将新生成的细胞加入到活着的细胞列表中
    cellList.addAll(bornCellList);

重复执行1到3的步骤,每重复一次,重绘整个细胞面板
Canvas代码:

package com.difeng.lifegame;

import static com.difeng.lifegame.Constant.CELL_HEIGHT;
import static com.difeng.lifegame.Constant.*;
import java.awt.Color;
import java.awt.Graphics;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JPanel;
/**
 * 
 * @author difeng
 *
 */
public class Canvas extends JPanel implements Runnable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	//取值为1 和 2   1:当代存活的细胞      2:刚产生的下一代细胞
	int [][] flagArr;
	//活着的细胞
	List<Cell> cellList = new LinkedList<Cell>();
	//新生的细胞
	List<Cell> bornCellList = new LinkedList<Cell>();
	//死亡的细胞
	List<Cell> deadCellList = new LinkedList<Cell>();
	//方向数组
	final int [][] dir = {
			{-1,-1},{-1,0},
			{-1,1},{0,-1},
			{0,1},{1,-1},
			{1,0},{1,1},
	};
	//滑翔机发射器的图案矩阵
    final int[][] plant = {
    		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},	
    		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0},	
    		{0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1},	
    		{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1},	
    		{1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},	
    		{1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0},	
    		{0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},	
    		{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    		{0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}	
    };
    
	public Canvas(){
		setBackground(Color.black);
		init();
	}
	
	/**
	 * 
	 * 初始化
	 * 
	 */
	public void init(){
		CELL_WIDTH = 20;
		CELL_HEIGHT = 20;
		COL_NUM = SCREEN_WIDTH/CELL_WIDTH;
		ROW_NUM= SCREEN_HEIGHT/CELL_HEIGHT;
		flagArr = new int[ROW_NUM][COL_NUM];
		//滑翔机发射器的起始坐标
		int x = 5,y=5;
		//初始化滑翔机发射器
		for(int i = 0;i < plant.length;i++){
			for(int j = 0;j < plant[0].length;j++){
				if(plant[i][j] == 1){
					int r = x + i;
					int col = y + j;
					if(flagArr[r][col]==0){
						Cell cel = new Cell(r, col,true);
						cellList.add(cel);
						flagArr[r][col] = 1;
					}
				}
			}
		}
	}
	public void evolution(){
		//清空中间计算的列表
		bornCellList.clear();
		deadCellList.clear();
		
		//遍历当代细胞列表,让细胞开始繁衍生长,计算下一代的状态
		for(Cell cel:cellList){
			//细胞生长
			cellEvolution(cel); 
		}
		
		//将下一代将要死掉的细胞标记为死亡
		for(Cell cel:deadCellList){
			flagArr[cel.row][cel.col] = 0;
		}
		
		//从活着的细胞列表中去掉在下一代将要死掉的
		cellList.removeAll(deadCellList);
		
		//将下一代新生成的细胞标记为当代存活的细胞
		for(Cell cel:bornCellList){
			flagArr[cel.row][cel.col]=1;
		}
		//将新生成的细胞加入到活着的细胞列表中
		cellList.addAll(bornCellList);
	}
	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		//绘制细胞棋盘
		g.setColor(new Color(33,33,33));
		for(int i=0;i<=ROW_NUM;i++){
			g.drawLine(0,i*CELL_HEIGHT,SCREEN_WIDTH,i*CELL_HEIGHT);
		}
		for(int i=0;i<=COL_NUM;i++){
			g.drawLine(i*CELL_WIDTH,0,i*CELL_WIDTH,SCREEN_HEIGHT);
		}
		//开始画细胞
		g.setColor(Color.GREEN);
		for(int i=0;i<cellList.size();i++){
			Cell cel = cellList.get(i);
			if(cel!=null){
				g.fill3DRect(cel.col*CELL_WIDTH,cel.row*CELL_HEIGHT,CELL_WIDTH,CELL_HEIGHT,true);		
			}
		}
	}
	
	/**
	 * 细胞繁衍
	 * @param cel
	 */
	public  void cellEvolution(Cell cel){
		int row = 0;
		int col = 0;
		int cellNum = 0;
		for(int i=0;i<dir.length;i++){
			row = cel.row + dir[i][0];
			col = cel.col + dir[i][1];
			if(row>-1 && row<ROW_NUM && col>-1 && col<COL_NUM){
				if(flagArr[row][col]==1){
					cellNum++;
				}else{
					//此位置周围有三个存活的细胞且此位置暂无生成的新细胞,则此位置应产生一个细胞
					if(computeRound(row,col) == 3 && flagArr[row][col] != 2){
						Cell newCell = new Cell(row, col,true);
						//标志此位置新生成一个细胞
						flagArr[row][col] = 2;
						bornCellList.add(newCell);
					}
				}  
			}
		}
		//细胞死亡判断条件
		if(cellNum < 2 || cellNum > 3){
			deadCellList.add(cel);  
		}
	}
	
	/**
	 * 计算一个细胞周围有多少个存活的细胞
	 * @param row
	 * @param col
	 * @return
	 * 
	 */
	public  int computeRound(int row,int col){
		int cellNum = 0;
		int r=0;
		int cl=0;
		for(int i = 0;i<dir.length;i++){
			r = row + dir[i][0];
			cl =  col + dir[i][1];
			if(r > -1 && r < ROW_NUM && cl > -1 && cl < COL_NUM){
				if(flagArr[r][cl]==1){
					cellNum++;
				}
			}
		}
		return cellNum;
	}
	
	public void run() {
		while(true){
			try {
				Thread.sleep(REFRESH_INTERVALS);
				//重新绘制
				repaint();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			evolution();
		}
	}
}

Cell代码:

package com.difeng.lifegame;
/**
 * 
 * @author difeng
 *
 */
public class Cell {
      int  row;
      int  col;
      boolean islive;
	public Cell(int row, int col, boolean islive) {
		super();
		this.row = row;
		this.col = col;
		this.islive = islive;
	}     
}

Constant的代码:

package com.difeng.lifegame;

import java.awt.Dimension;
import java.awt.Toolkit;
/**
 * 
 * @author difeng
 *
 */
public class Constant {
     static Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
     public static int SCREEN_WIDTH = (int) dimension.getWidth();
     public static int SCREEN_HEIGHT = (int)dimension.getHeight();
     public static int ROW_NUM;
     public static int COL_NUM;
     public static final int CELL_WIDTH = 20;
     public static final int CELL_HEIGHT = 20;
     public static final int REFRESH_INTERVALS = 100;
}

主控程序:

package com.difeng.lifegame;
import java.awt.BorderLayout;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
/**
 * 
 * @author difeng
 *
 */
public class MainFrame {
    public static void main(String[] args) {
		JFrame life = new JFrame();
		Canvas canvas = new Canvas();
		GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
	    life.getContentPane().add(canvas,BorderLayout.CENTER);
	    life.setUndecorated(true);
	    //设置全屏显示
	    gd.setFullScreenWindow(life);
	    //添加程序退出的事件监听
	    life.addKeyListener(new KeyAdapter() {
	    	@Override
			public void keyPressed(KeyEvent e) {
				// TODO Auto-generated method stub
				super.keyPressed(e);
				if(e.getKeyCode()==KeyEvent.VK_ESCAPE){
					System.exit(0);
				}
			}
		});
	    new Thread(canvas).start();
    }
}

4. 总结

1.搞清楚问题的本质,也就是原理部分。
2.然后思考寻找合理的数据结构。再考虑类的设计和协作。
3.coding
生命游戏还有其它游戏的算法来高效的实现,上述只是自己刚接触时的想法。不过这个游戏确实挺有意思的,其实它也属于分形学。有兴趣的朋友还可以阅读《混沌与分形》这本书。也可以去看看极客们制作的漂亮的分形图形

posted @ 2016-02-28 17:18  迪峰  阅读(708)  评论(0编辑  收藏  举报