生命游戏其实是一个零玩家游戏,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相反,如果周围活细胞过少,这个细胞会因太孤单而死去。实际中,你可以设定周围活细胞的数目怎样时才适宜该细胞的生存。如果这个数目设定过低,世界中的大部分细胞会因为找不到太多的活的邻居而死去,直到整个世界都没有生命;如果这个数目设定过高,世界中又会被生命充满而没有什么变化。实际中,这个数目一般选取2或者3;这样整个生命世界才不至于太过荒凉或拥挤,而是一种动态的平衡。这样的话,游戏的规则就是:当一个方格周围有2或3个活细胞时,方格中的活细胞在下一个时刻继续存活;即使这个时刻方格中没有活细胞,在下一个时刻也会“诞生”活细胞。在这个游戏中,还可以设定一些更加复杂的规则,例如当前方格的状况不仅由父一代决定,而且还考虑祖父一代的情况。你还可以作为这个世界的上帝,随意设定某个方格细胞的死活,以观察对世界的影响。
在游戏的进行中,杂乱无序的细胞会逐渐演化出各种精致、有形的结构;这些结构往往有很好的对称性,而且每一代都在变化形状。一些形状已经锁定,不会逐代变化。有时,一些已经成形的结构会因为一些无序细胞的“入侵”而被破坏。但是形状和秩序经常能从杂乱中产生出来。
这个游戏被许多计算机程序实现了。Unix世界中的许多Hacker喜欢玩这个游戏,他们用字符代表一个细胞,在一个计算机屏幕上进行演化。著名的GNUEmacs编辑器中就包括这样一个小游戏。
细胞自动机(又称元胞自动机),名字虽然很深奥,但是它的行为却是非常美妙的。所有这些怎样实现的呢?我们可以把计算机中的宇宙想象成是一堆方格子构成的封闭空间,尺寸为N的空间就有NN个格子。而每一个格子都可以看成是一个生命体,每个生命都有生和死两种状态,如果该格子生就显示蓝色,死则显示白色。每一个格子旁边都有邻居格子存在,如果我们把33的9个格子构成的正方形看成一个基本单位的话,那么这个正方形中心的格子的邻居就是它旁边的8个格子。
每个格子的生死遵循下面的原则:
1. 如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,则保持不变) 。
2. 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;
3. 在其它情况下,该细胞为死(即该细胞若原先为生,则转为死,若原先为死,则保持不变)
设定图像中每个像素的初始状态后依据上述的游戏规则演绎生命的变化,由于初始状态和迭代次数不同,将会得到令人叹服的优美图案。
下面是使用JAVAFX实现的游戏源代码:
该游戏我们可以使用MVC模式来进行设计:
下面是其类图:
该类图是基于JAVAFX开发下的类图,一些关键和必要的函数已经给出了。
主函数类
package App;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import view.MainGameView;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = new MainGameView();
primaryStage.setTitle("生命游戏");
primaryStage.setScene(new Scene(root, 800, 700));
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
System.exit(0);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
细胞实体类:
package entity;
import javafx.concurrent.Task;
import javafx.scene.layout.Pane;
public class CellRender extends Pane {
/**细胞的宽.*/
private int width;
/**细胞的高.*/
private int hight;
/**细胞是否活着,1为活着,0为死亡.*/
private int isAlive;
/**细胞周期.*/
private int circleTime;
/**细胞活着的状态.*/
public static final int ALIVE = 1;
/**细胞死亡的状态.*/
public static final int DIEDED = 0;
public CellRender( int width, int hight,int isAlive) {
this.width = width;
this.hight = hight;
this.isAlive = isAlive;
this.setPrefSize(width,hight);
paintComponent();
}
public int getIsAlive() {
return isAlive;
}
public void setIsAlive(int isAlive)
{
this.isAlive = isAlive;
}
public void setCircleTime(int circleTime) {
this.circleTime = circleTime;
}
/**
* @Description :不断监听该细胞的生死状态,如果生,则绿色,死则白色
* @Date 16:33 2020/5/15 0015
* @Param * @param :
* @return void
**/
public void paintComponent(){
Task<String> task =new Task<String> () {
@Override
protected String call() throws Exception {
while (isAlive < 5) {
if (isAlive == DIEDED){
//细胞死亡
this.updateValue("-fx-background-color: white;");
}else{
//细胞活着
this.updateValue("-fx-background-color:green;");
}
//每隔100毫秒判定一次
Thread.sleep(100);
}
return null;
}
};
//绑定样式
this.styleProperty().bind(task.valueProperty());
//任务线程启动
new Thread(task).start();
}
}
业务逻辑类:
package service;
import entity.CellRender;
/**
* @author 陌意随影
* @create 2020-05-11 19:51
* @desc 判断细胞是否死亡的业务逻辑
**/
public class GameService {
/**
* @Param * @param cellRenders:所有的细胞的二维数组
* @param row :第row行
* @param col :第col列
* @return boolean:返回细胞活着(true),死亡(false)
* @Description :
* 1.如果细胞的周围有3活着的个细胞则该细胞活着。
* 2.如果有两个活着的细胞,则该细胞的状态不变,原来死亡的还是死亡,活着的还是活着。
* 3.其它情况下该细胞军事死亡。
* @Date 19:53 2020/5/11 0011
**/
public boolean isAlive(CellRender[][] cellRenders, int row, int col) {
//统计某个细胞周围细胞的总数
int count = 0;
//左上
count+= ((row==0||col==0)?0:cellRenders[row-1][col-1].getIsAlive());
//上
count+= (row==0?0:cellRenders[row-1][col].getIsAlive());
//右上
count+= ((row==0||col==cellRenders[0].length-1)?0:cellRenders[row-1][col+1].getIsAlive());
//左
count+= (col==0?0:cellRenders[row][col-1].getIsAlive());
//右
count+= (col==cellRenders[0].length-1?0:cellRenders[row][col+1].getIsAlive());
//左下
count+= ((row==cellRenders.length-1||col==0)?0:cellRenders[row+1][col-1].getIsAlive());
//下
count+= (row==cellRenders.length-1?0:cellRenders[row+1][col].getIsAlive());
//右下
count+= ((row==cellRenders.length-1|| col==cellRenders[0].length-1)?0:cellRenders[row+1][col+1].getIsAlive());
if (count == 3) {
//1.如果细胞的周围有3活着的个细胞则该细胞活着。
return true;
}
if (count == 2) {
// 2.如果有两个活着的细胞,则该细胞的状态不变,原来死亡的还是死亡,活着的还是活着。
return cellRenders[row][col].getIsAlive() == 1;
}
//3.其它情况下该细胞军事死亡。
return false;
}
}
视图类
package view;
import controller.GameController;
import entity.CellRender;
import javafx.concurrent.Task;
import javafx.scene.layout.GridPane;
import java.util.Random;
/**
* @author 陌意随影
* @create 2020-05-11 15:02
* @desc 游戏视图
**/
public class GameView extends GridPane {
/**面板的大小*/
private static final int WIDTH = 800;
private static final int HIGHT = 800;
/**细胞二维数字的行和列数*/
private static final int row =10;
private static final int col= 10;
/**每个细胞的宽和高*/
int cellWith = WIDTH /row;
int cellhight = HIGHT /col;
/**游戏的控制类*/
private GameController gameController = null;
public static final int DEFAULT_CIRCLETIME = 100;
/**细胞周期*/
private int circleTime = DEFAULT_CIRCLETIME;
/**细胞的二维数组*/
private static CellRender[][] cellRenders = new CellRender[row][col];
public GameView() {
//设置大小
this.setPrefSize(WIDTH, HIGHT);
this.gameController = new GameController();
//细胞界面的初始化
init();
}
/**
* @Description :细胞送生长
* @Date 16:32 2020/5/15 0015
* @Param * @param :
* @return void
**/
public void grow() {
for (int i = 0; i< cellRenders.length;i ++ ){
for (int j = 0; j < cellRenders[0].length; j++) {
//细胞任务线程开始启动
new Thread(new CellTask(i,j)).start();
}
}
}
/**
* @Description :初始化界面,并给细胞随机设置状态
* @Date : 2020/5/15 0015
* @Param * @param :
* @return void
**/
private void init() {
Random random = new Random();
for (int i = 0; i< row;i ++ ){
for (int j = 0; j < col; j++) {
//使用随机生成数生成细胞的状态
cellRenders[i][j]=new CellRender(cellWith,cellhight,random.nextInt(3)==1?CellRender.ALIVE:CellRender.DIEDED);
//将细胞添加到面板中
this.add(cellRenders[i][j],j,i);
}
}
}
/**
* @Description :不断循环判断细是否死亡的任务
* @Date 16:32 2020/5/15 0015
* @Param * @param null :
* @return
**/
class CellTask extends Task{
int row;
int col;
public CellTask(int row, int col){
this.row =row;
this.col = col;
}
@Override
protected Object call() throws Exception {
while (true){
boolean isAlive = gameController.isAliveControl(cellRenders, row, col);
if (isAlive) {
//设置 细胞的状态
cellRenders[row][col].setIsAlive(CellRender.ALIVE);
}else{
//设置细胞的状态
cellRenders[row][col].setIsAlive(CellRender.DIEDED);
}
Thread.sleep(circleTime);
if ( 1 > 2){
//让程序一直循环
break;
}
}
return null;
}
}
public void setCircleTime(int circleTime) {
this.circleTime = circleTime;
}
}
主视图类:
package view;
import javafx.collections.FXCollections;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
/**
* @author 陌意随影
* @create 2020-05-17 14:01
* @desc 主程序窗口
**/
public class MainGameView extends VBox {
private GameView gameView = null;
private HBox hBox = null;
private ComboBox<Integer> circleTime = null;
private Button startBtn = null;
private static Integer[] time = new Integer[]{
100,200,300,400,500,600,700,900,1000
};
public MainGameView() {
this.gameView = new GameView();
this.hBox=new HBox();
this.startBtn = new Button("开始");
this.circleTime = new ComboBox<Integer>(FXCollections.observableArrayList(time));
initConponents();
initEvent();
this.getChildren().addAll(gameView,hBox);
}
private void initEvent() {
//给开始按钮添加点击事件
this.startBtn.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
gameView.grow();
}
});
//给细胞周期选择添加点击事件
this.circleTime.setOnHidden(new EventHandler<Event>() {
@Override
public void handle(Event event) {
Integer selectedItem = circleTime.getSelectionModel().getSelectedItem();
System.out.println(selectedItem);
gameView.setCircleTime(selectedItem);
}
});
}
private void initConponents() {
Label circleTimeText = new Label("细胞周期");
hBox.setPrefHeight(200);
this.hBox.getChildren().addAll(circleTimeText,circleTime,startBtn);
circleTimeText.setPrefHeight(30);
circleTimeText.setStyle("-fx-font-size: 16");
circleTimeText.setPadding(new Insets(0,10,0,0));
circleTime.setPrefHeight(30);
this.circleTime.getSelectionModel().select(0);
hBox.setAlignment(Pos.CENTER);
}
}
控制器类
package controller;
import service.GameService;
import entity.CellRender;
/**
* @author 陌意随影
* @create 2020-05-11 22:14
* @desc 游戏控制器
**/
public class GameController {
GameService gameService = new GameService();
/**
* @Param * @param cellRenders:所有的细胞的二维数组
* @param row :第row行
* @param col :第col列
* @return boolean:返回细胞活着(true),死亡(false)
* @Description :
* 1.如果细胞的周围有3活着的个细胞则该细胞活着。
* 2.如果有两个活着的细胞,则该细胞的状态不变,原来死亡的还是死亡,活着的还是活着。
* 3.其它情况下该细胞军事死亡。
* @Date 19:53 2020/5/11 0011
**/
public boolean isAliveControl(CellRender[][] cellRenders,int row,int col){
return gameService.isAlive(cellRenders,row,col);
}
}
测试截图:
最终细胞达到平衡的时候:
顺便附上该游戏的GitHub地址:
[LifeGame游戏的GitHub地址](https://github.com/LJF2402901363/lifeGame.git)