zoukankan      html  css  js  c++  java
  • 生命游戏

    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
    生命游戏还有其它游戏的算法来高效的实现,上述只是自己刚接触时的想法。不过这个游戏确实挺有意思的,其实它也属于分形学。有兴趣的朋友还可以阅读《混沌与分形》这本书。也可以去看看极客们制作的漂亮的分形图形

  • 相关阅读:
    校门外的树
    学生档案
    冒泡排序法
    寻找最大数序列
    初识结构体
    找零钱
    冒泡的应用
    关于数组的逆序重放
    关于质数
    字符串转换为日期格式
  • 原文地址:https://www.cnblogs.com/difeng/p/5224291.html
Copyright © 2011-2022 走看看