生命游戏

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值