目录

生命游戏

1 生命游戏的规则

2 生命游戏的意义

对生命游戏进行抽象(属性部分)

1 建立基础的生命游戏类

2 特殊要求

对生命游戏进行抽象(方法部分)

1 回合变动更新方法

2 设置棋盘初值(初始态)方法

3 绘制棋盘的方法

源代码

1 Cell.java

2 Board.java

3 Panel.java


生命游戏

生命游戏是一种零玩家的数学游戏,它的前身是约翰·何顿·康威发明的细胞自动机(又称元胞自动机,细胞自动机是一种研究模型,生命游戏是一种数学游戏,所以我倾向于将它们视为两种东西)。

1 生命游戏的规则

如果一个生命,其周围的同类生命太少,会因为得不到帮助而死亡;如果太多,则会因为得不到足够的生命资源而死亡。 ——英国数学家约翰·康威

在一个有着n*m个格子的方形二维世界中,每个格子都居住着一个细胞。对于世界中的所有细胞而言,其有两个状态活/死,每个细胞在每个时刻(回合)中在且仅在这两个状态中的一个下。

这个世界中所有细胞的状态会随着时刻的变化而变化,在每个时刻下,每个格子中的细胞会根据上一个时刻周围(也就是与其相邻)的格子中的细胞状态来决定。通常,当周围有三个活细胞时,格子中的细胞为活;当周围有两个活细胞时,格子中的细胞维持原状;其他情况下,格子中的细胞死去。

2 生命游戏的意义

对于其前身细胞自动机而言,它可以模拟一些类似于组织结构的复杂现象。对于生命游戏本身而言,首先它是一个很典型的混沌系统,并且在数学和哲学领域上有所启迪;其次我认为它和分形一样是一种直观的显示数学之美的一种方式:因为原本杂乱无序的数据,在生命游戏的演化下有时会出现十分优美并且大多数情况下对称的形状。

对生命游戏进行抽象(属性部分)

1 建立基础的生命游戏类

我们可以将生命游戏发生的场景想象为一个棋盘,将细胞想象为一个棋子。在这个类比下,生命游戏与围棋有一定的相似性。

如果我们将生命游戏想象为一场棋类游戏,那么进行这场游戏必然需要棋盘和棋子。于是我们建立Board类表示棋盘,建立Cell类表示棋子。

对于Cell类,其必然有一个属性live,用于表示状态(生/死)。

对于Board类,作为一个抽象化的棋盘,其长度属性为height,宽度属性为width;它有一个二维数组,用于描述棋盘中的格子,这个二维数组名为board,成员为Cell类的对象。并且,棋盘并不是静止的,它会根据游戏的回合数发生变化。所以Board类有一个用于标记回合数的属性round。

在拥有棋盘(Board)和棋子(Cell)之后,我们还要有一个放置棋盘的场景(桌子),用于显示棋盘和棋子。创建Panel类用于表示生命游戏的场景(UI类)。Panel类中有若干辅助显示的Java窗口组件,表示棋盘的属性lifeboard,和用于表示游戏棋盘长度宽度的属性height与width。

2 特殊要求

除此之外,我还希望生命游戏在运行过程中能够记录自身所有回合中的情况,并且对当前回合的活细胞总数占棋盘比例、当前游戏中有过活细胞的总数占棋盘比例进行计数。

为了满足以上条件,我们给三个类分别添加以下属性:

对于Cell类,添加属性dict用于表示其是否已经存活过,添加列表属性list用于记录其所有存活的回合数。

对于Board类,添加属性live、dict,分别用于表示棋盘本回合存活细胞数量,棋盘所有存活过的细胞数量。

对于Panel类,添加几个用于调整显示回合数和进度的窗口组件。

最后,为了便于计算和显示,我们给Board类添加二维数组state用于表示上一回合的棋盘状态,添加属性count表示棋盘中的格子数量。

对生命游戏进行抽象(方法部分)

1 回合变动更新方法

之前我们讲到游戏本身会根据回合数发生变化,所以我们以1秒作为回合变动的单位。在此情况下,每变动一秒,让Panel类更新一次;而Panel类每更新一次,就会对其属性lifeboard进行一次更新;Board类每更新一次就会对其二维Cell数组中的所有成员进行更新。

如图所示,我们将三个类中用于更新的方法命名为update:

在一个回合中,从panel开始,依次调用Panel->Board->Cell*(count)的update方法,对整个游戏进行更新。

2 设置棋盘初值(初始态)方法

在逻辑上,我为“设置棋盘初值”创建了三个方法,它们分别是:

Board类下的set_cell,其输入一个设置为活的(初始状态下所有细胞皆为死)细胞的坐标,调用此细胞的update方法来给细胞赋值(此update调用时round为1,也就是回合一就是本游戏的初始态)。

Panel类下的set_board,它在Panel类初始化时调用,通过更改其内容可以更改游戏初始态的生成方式。

Panel类下的set_random,它默认在set_board中被调用,生成一个与方法内局部变量密度系数g有关的随机棋盘,g=[0…1],g越大,生成的棋盘中活细胞数量越少。

其实我并不建议读者使用随机生成方法生成棋盘,因为随机生成的棋盘并没有手动调用set_cell画出的棋盘精彩,通常随机棋盘产生不出什么有意思的图形。但是如果读者手动调用方法,哪怕是画个矩形都能出现很有意思的情况。set_random方法只是为读者测试而用。当然,如果读者想要研究棋盘中初始细胞的稠密程度与最终棋盘的情况的关系,还是可以调用这个方法(这是个很有意思的研究,很可惜我没有这个时间和能力)

3 绘制棋盘的方法

从源码中可以很容易地看出,棋盘的主UI界面在一个JPanel实例panel上,它是Panel类的一个属性。在绘制棋盘的过程中,我们使用panel的句柄在其上绘制图形,长宽都是10像素。

Panel类下的drawcell方法一次绘制一个细胞;Panel类下的draw方法调用drawcell对棋盘状态进行遍历,一次绘制整个棋盘。

源代码

代码采用的当然如标题所言是Java,使用的图形组件来自javax.swing.*。

1 Cell.java

游戏中细胞(棋子)的类。

import java.util.ArrayList;public class Cell { //细胞类,是生命游戏中所有格子的类public boolean dict; //是否有过活细胞 public boolean live; //本回合下是否有细胞(活着)private ArrayList<Integer> list=new ArrayList<Integer>(); //活细胞回合数列表,其值为细胞存活的回合值Cell(){this.dict=false;this.live=false;this.list=new ArrayList<Integer>();}public boolean is_thisround(int round){ //返回一个表示此回合下是否有活细胞的boolreturn this.list.contains(round);}public void update(boolean bool,int round){ //每回合之后更新细胞状态的函数if(bool){this.dict=true;this.list.add(round);}this.live=bool;}}

2 Board.java

游戏中棋盘的类。

public class Board { //存放所有cell的棋盘类private int width;private int height; //棋盘的高度和宽度public Cell[][] board; //细胞的二维数组public int round; //回合数private boolean state[][]; //此回合更新之前的细胞状态public int live; //本回合存活的细胞数量public int dict; //棋盘上所有存活过细胞的格子数量public int count; //就是棋盘上格子的总数Board(int width,int height){this.round=1;this.live=0;this.dict=0;this.count=width*height;this.width=width;this.height=height;this.board=new Cell[this.height][this.width];this.state=new boolean[this.height][this.width];for(int i=0;i<this.height;i++){for(int j=0;j<this.width;j++){this.board[i][j]=new Cell();this.state[i][j]=false;}}}public void set_cell(int x,int y){ //设置初值 //不能不设置初值,不然生命游戏不会开始运转this.board[x][y].update(true,this.round);}private int count_neighbor(int x,int y){ //返回此细胞周围活着的细胞数量 int[] m=new int[]{-1,0,1};int live=0; //周围存活细胞计数//修正数字for(int i=0;i<3;i++){if(x+m[i]>=this.height|x+m[i]<0){continue;}for(int j=0;j<3;j++){if(y+m[j]>=this.width|y+m[j]<0){continue;}if(this.state[x+m[i]][y+m[j]]==true){ //相邻范围内的细胞是活着的计数就加1live++;}}}return live;}private void round_judge(int x,int y){ //判断并更新某细胞在此轮的状态//这里有个问题,那就是更新是一次更新整个棋盘还是一步一步更新。//我们用state保存上个状态的棋盘,一个状态一个状态更新 这样可以保证棋盘更新的同步性int live_count=3; //复活计数int normal_count=2; //维持原状计数int judge=this.count_neighbor(x, y);if(judge==live_count){this.board[x][y].update(true,this.round); //如果复活则复活状态更新}else if(judge==normal_count){this.board[x][y].update(this.state[x][y],this.round); //维持原状则将state中的状态代入更新}else{this.board[x][y].update(false,this.round);}}private void to_state(){ //将Cell.live的值传给state,虽然没有什么复用的可能但是出于逻辑关系还是分开来写for(int i=0;i<this.height;i++){for(int j=0;j<this.width;j++){this.state[i][j]=this.board[i][j].live;}} //应该一开始就用一维数组,另外设计一个一维转二维的函数,可以减少一定的代码量}public boolean[][] round_board(int round){ //这个方法返回传入回合的棋盘boolean[][] state=new boolean[this.height][this.width];for(int i=0;i<this.height;i++){for(int j=0;j<this.width;j++){state[i][j]=this.board[i][j].is_thisround(round);}}return state;}public boolean[][] this_board(){ //这个方法返回本回合的棋盘 //当然,用上面的方法返回也可以,就是开销大一点boolean[][] state=new boolean[this.height][this.width];for(int i=0;i<this.height;i++){for(int j=0;j<this.width;j++){state[i][j]=this.board[i][j].live;}}return state;}private int count_live(){ //计算board中存活的细胞数量 //因为我也不常用java,想知道它有类似块的结构吗?有的话这些使用for的代码会简洁很多int count=0;for(int i=0;i<this.height;i++){for(int j=0;j<this.width;j++){if(this.board[i][j].live){count++;}}}return count;}private int count_dict(){ //计算board中已经有过存活细胞的块数量int count=0;for(int i=0;i<this.height;i++){for(int j=0;j<this.width;j++){if(this.board[i][j].dict){count++;}       }}return count;}public void update(){ //更新一轮this.to_state(); //传值给statefor(int i=0;i<this.height;i++){for(int j=0;j<this.width;j++){this.round_judge(i, j); //对state中数据计算更新出新的board//也就是,这里计算的是state数据,更新的是board,所以每一轮都要先把board的更改移到state中}}this.live=this.count_live();this.dict=this.count_dict(); //更新两个数据this.round++; //计数增加}}

3 Panel.java

游戏整体界面的类,也是main函数所在的类。

import javax.swing.JButton;import javax.swing.event.ChangeEvent;import javax.swing.event.ChangeListener;import javax.swing.*;import java.awt.*;import java.awt.event.*;public class Panel { // 画板类,是本程序的UI部分public JFrame main; // 主框架public JPanel panel; // 画板部分public JPanel _settingp; // 下方的设置面板public JPanel _progressp; // 右边的进度条面板public JPanel _roundp; // 上方的回合数面板 public JButton start; // 开始按钮public JButton stop; // 暂停按钮public JProgressBar cell; // 细胞进度(细胞/总区域)public JProgressBar dict; // 细胞占领进度 (有过细胞的格子/总区域)public JSlider round; // 回合数滑块 //这玩意先不赋最大最小值,它在按下暂停按键时才会出现。public Board lifeboard; // 棋盘public JTextArea num; //回合数文本框private boolean run; // 是否运行中布尔值private boolean ch; // 滑块是否更改的布尔值public static final int Width = 800; //固定的高度和宽度 public static final int Height = 600;Panel() {// 棋盘等其他值的初始化this.lifeboard = new Board(Width / 10, Height / 10); // 我们设置格子为80*60,也就是一个格子十像素this.run = false;this.ch=false;// main窗口this.main = new JFrame("生命游戏");this.main.setSize(1015, 700); // 设置大小this.main.setLayout(new BorderLayout()); // 设置布局为方位布局(虽然这是默认布局)this.main.setLocation(0, 0); // 设置显示位置// panel画板this.panel = new JPanel();this.panel.setBackground(java.awt.Color.LIGHT_GRAY);this.panel.setPreferredSize(new Dimension(Width, Height));// 其实我还有更好的想法,那就是给新增的格子和老的格子加上不同的颜色,逐步渐进,到底反弹,肯定会更好看this.main.add(this.panel, BorderLayout.CENTER); // 将画板加到中间// _settingp用于设置的面板this._settingp = new JPanel();this.start = new JButton("开始");this.start.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {run = true;}});this.stop = new JButton("暂停");this.stop.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {run = false;}});this._settingp.setLayout(new FlowLayout()); // 流布局this._settingp.add(this.start);this._settingp.add(this.stop);this.main.add(this._settingp, BorderLayout.SOUTH); // 设置面板放在下面// _progressp用于进度条和滑块的面板this.cell = new JProgressBar(0, this.lifeboard.count);JTextField cellt=new JTextField("本回合活细胞比例");this.cell.setStringPainted(true);this.dict = new JProgressBar(0, this.lifeboard.count);JTextField dictt=new JTextField("活细胞比例");this.dict.setStringPainted(true); // 设置绘制百分比this._progressp = new JPanel();this._progressp.setPreferredSize(new Dimension(200,700));this._progressp.setLayout(new FlowLayout());this._progressp.add(cellt);this._progressp.add(this.cell);this._progressp.add(dictt);this._progressp.add(this.dict);滑块部分this.round = new JSlider();this.round.setMinimum(0);this.round.setMajorTickSpacing(5);this.round.setMinorTickSpacing(2);this.round.setPaintLabels(true);this.round.setPaintTicks(true);this.round.setOrientation(SwingConstants.VERTICAL); // 设置滑块垂直this.round.setPreferredSize(new Dimension(100,450));this.round.addChangeListener(new ChangeListener() {@Overridepublic void stateChanged(ChangeEvent e) {ch = true; // 当滑块值变动时,ch置true}});this._progressp.add(this.round);this.round.setVisible(false); // 初始不可见this.main.add(this._progressp, BorderLayout.EAST); // 进度条面板放在右边// _roundp用于显示回合数的面板this.num=new JTextArea("回合0");this.num.setEditable(false);this._roundp=new JPanel();this._roundp.setLayout(new FlowLayout());this._roundp.add(this.num);this.main.add(this._roundp,BorderLayout.NORTH);this.main.setVisible(true); // 可见}public void set_board(){ //给棋盘绘制初始细胞 //通过更改这个部分,决定一个生命游戏的初始态this.set_random();}private void set_random(){ //随机生成一个棋盘,可在set_board中调用double g=0.89; //密度系数for(int i=0;i<60;i++){for(int j=0;j<80;j++){if(Math.random()>g){this.lifeboard.set_cell(i,j);}}}}private void draw() { // 重绘棋盘的方法,在ch为假时按照lifeboard.board绘制棋盘,在ch为真时按照lifeboard.round_board()绘制棋盘boolean[][] board;if (ch == true) {board = this.lifeboard.round_board(this.round.getValue());} else {board = this.lifeboard.this_board();}for (int i = 0; i < Height / 10; i++) {for (int j = 0; j < Width / 10; j++) {drawcell(i, j, board[i][j]);}}}private void drawcell(int x, int y, boolean live) { // 重绘棋盘的一个格子Graphics j = this.panel.getGraphics(); // 获取上下文if (live) {j.setColor(java.awt.Color.black);} else {j.setColor(java.awt.Color.white);}j.fillRect(y * 10, x * 10, 10, 10); // 绘制图形}public void update() { // 这个方法一秒调用一次,为一回合if (this.run == true) {this.round.setVisible(false); // 运行中不能调用滑块this.ch = false; // 所以更改标志也要置falsethis.lifeboard.update(); // 更新棋盘this.cell.setValue(this.lifeboard.live); // 更新进度条this.dict.setValue(this.lifeboard.dict);this.num.setText("回合"+this.lifeboard.round);} else {this.round.setMaximum(this.lifeboard.round); // 重设滑块最大值this.round.setVisible(true);}this.draw(); // 重新绘制棋盘}public static void main(String args[]) {Panel panel = new Panel();panel.set_board(); //设置初始棋盘while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}panel.update();}}}

Java实现生命游戏Plus相关推荐

  1. 用Java实现生命游戏

    用Java实现生命游戏 一.题目: 编写一个生命游戏: 规则如下: 1. 一个人可以有8个邻居: 2. 一个人若只有一个邻居,在下一代会孤独的死去: 3. 若有2或3个邻居,在下一代依然活着: 4. ...

  2. Java案例:Java版生命游戏

    目录 一.生命游戏 1.生命游戏概述 2.生命演化规则:B3/S23 二.生命游戏Java实现

  3. [Leedcode][JAVA][第289题][生命游戏]

    [问题描述] 根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机.给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞.每个细胞都具 ...

  4. java swing 代码_java swing编写gui生命游戏代码,新手上路

    项目描述 生命游戏其实是一个零玩家游戏,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量.如果相邻方格活着 ...

  5. 生命游戏(Java)

    在研究元胞自动机理论过程中,Conway发明生命游戏(Game of Life.GoL),在1970s风靡一时. 这是0人游戏,即按照初始的设置,游戏自动演化.在类似围棋的棋盘中,每一个格子可以是空格 ...

  6. 蓝桥杯------2017 Java B组 国赛:第二题 生命游戏

    题目描述: 康威生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机.   这个游戏在一个无限大的2D网格上进行. 初始时,每个小方格中居住着一个活着或死了的细胞. 下一时刻每个细胞的状态 ...

  7. Java B组蓝桥杯第八届国赛:生命游戏

    标题:生命游戏 康威生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机.   这个游戏在一个无限大的2D网格上进行. 初始时,每个小方格中居住着一个活着或死了的细胞. 下一时刻每个细胞的 ...

  8. 生命游戏Java实现

    关于生命游戏 之前在学校看到ThoughtWorks举办的线下结对编程的比赛一等奖是大疆无人机,冲着无人机就拉着实验室小伙伴马总一起报了个名.然后题目就是实现一个界面版的生命游戏,所以才了解了生命游戏 ...

  9. 分布与并行计算—生命游戏(Java)

    生命游戏其实是一个零玩家游戏,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量.如果相邻方格活着的细胞数量 ...

最新文章

  1. 高效学习方法论的学习笔记
  2. java的WebService实践(cxf)
  3. 汇编:转移目的地址在内存中
  4. vue中的if判断和for循环语句
  5. nyoj 710 外星人的供给站
  6. 【Elasticsearch】玩转 Elasticsearch 7.8 的 SQL 功能
  7. V-SQL的简单使用
  8. 计算机应用历年高考真题,春季高考历年真题-2013年天津市春季高考计算机试卷...
  9. Multisim 14.1 安装步骤
  10. Linux串口通信编程
  11. 为什么你的努力可能是没用的?
  12. Pearson相关系数, Spearman相关系数,Kendall相关系数的区别
  13. 一个人不孤单 想一个人才孤单
  14. echart图表x轴横轴逐步左移动
  15. Qt自定义控件(IP输入框,windows下)
  16. 5、店铺管理 - 后端功能开发 - 微擎小程序模块应用开发
  17. 亚马逊国际获得AMAZON商品详情 API 返回值说明
  18. 解决Client not ready yet..Timed out waiting for process to appear on 无法自动启动安卓应用问题
  19. linux进程通信的异同,进程间通信方式的比较
  20. SVG动画编程及其应用

热门文章

  1. 前端加载动画-三点加载
  2. 什么是元认知?其效果和锻炼方法的介绍!
  3. 无胁科技-TVD每日漏洞情报-2022-10-26
  4. ros1使用过程中遇到的问题记录
  5. Android智能识别 - 银行卡区域裁剪(原理篇)
  6. 如何用计算机作函数图象,信息技术应用 用计算机画函数图象优秀教学设计
  7. 颜色大全英语python_Python颜色分类及格式
  8. (前端)图片如何从模糊到清晰渐显
  9. 从新生宿舍到浙江大学计算机学院,2020年浙江大学新生宿舍环境条件,大一新生男生女生宿舍内部图片【多图】...
  10. LINUX通过python连接ACCESS(.mdb和.accdb文件)数据库