1. 问题背景:Game Of Life

// 英国数学家发明的生命游戏 (Game of Life)
// 使用一个2维数组来实现其规则
// 在这个数组中,每个存储位子都能容纳一个LIFE元胞。世代(gennerations)用于标记时间
// 的流逝。每个世代都会LIFE社会带来生与死。
// 生死规则如下:
// * 定义的元胞都有8个邻居。上下左右,左上左下,右上右下八个方位。
// * 如果一个元胞有一个或零个邻居,会因为孤独而死亡。3个以上的邻居会因为拥挤而死亡。
// * 如果空元胞弟正好有3个邻居,会在空元胞的位子生成一个元胞。
// * 生生死死世代交换。

2. 问题解决策略:

本博客使用Java 搭载Java Swing实现本算法。

3. 软件完成效果如下:

4. 程序代码部分:

核心代码Game.java
package com.game;import java.util.ArrayList;
import java.util.Random;import javax.swing.JFrame;import com.game.enmu.State;public class Game {private int row;private int column;private int numberRand;private boolean running;private Random rand;private ArrayList<ArrayList<State>> data;private ArrayList<CellStateOfSpecificPlace> back_data;private JFrame parent;private Thread generate;public Game(int row, int column, int numberRand){this.row = row;this.column = column;this.numberRand = numberRand;this.running = true;this.data = new ArrayList<ArrayList<State>>();rand = new Random();back_data = new ArrayList<CellStateOfSpecificPlace>();initinalGame();//runGame();}public Game(int row, int column, int numberRand, GameGUI gameGUI) {this(row, column, numberRand);parent = gameGUI;}/** initial the game with the specific policy.* */public void initinalGame(){data.clear();for (int rowIndex = 0; rowIndex < row; rowIndex++){ArrayList<State> temp = new ArrayList<State>();for (int columnIndex = 0; columnIndex < column; columnIndex++){temp.add(State.DIE);}data.add(temp);}initialPolicy();}/** Below code is one kind of initial policy of Game Of Life, which you can * modified by yourself.* */public void initialPolicy(){int margin = (int)Math.sqrt(numberRand);int rowPlace = rand.nextInt(row - margin - 1) + 1;int columnPlace = rand.nextInt(column - margin - 1) + 1;for (int rowIndex = rowPlace; rowIndex < rowPlace + margin; rowIndex++){for (int columnIndex = columnPlace; columnIndex < columnPlace + margin; columnIndex++){if (rand.nextBoolean()){data.get(rowIndex).set(columnIndex, State.ALIVE);} }}}/** Below method run the code in a thread named generate.* */public void runGame(){if (false == running){if (generate.isAlive()){generate.interrupt();generate = null;}} else {if (null == generate){generate = new Thread(new Runnable() {@Overridepublic void run() {while(!Thread.interrupted()){//printGame();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();Thread.currentThread().stop();}generated();if (null != parent){parent.repaint();}}}});generate.start();}}}/** generated method to generate the next generation based on current generation.* */private void generated() {int neighbours = 0;State currentCellState = State.DIE;State nextCellState = State.DIE;back_data.clear();for (int rowIndex = 1; rowIndex < row - 1; rowIndex++){for (int columnIndex = 1; columnIndex < column - 1; columnIndex++){neighbours = getNeighborNumbers(rowIndex, columnIndex);currentCellState = data.get(rowIndex).get(columnIndex);nextCellState = nextGenerateState(currentCellState, neighbours);if (nextCellState != currentCellState){back_data.add(new CellStateOfSpecificPlace(rowIndex, columnIndex, nextCellState));}}}for (CellStateOfSpecificPlace item:back_data){data.get(item.getRowIndex()).set(item.getColumnIndex(), item.getState());}}/** Below method get the cell's neighbor number.* */private int getNeighborNumbers(int rowIndex, int columnIndex){int counter = 0;for (int i = rowIndex - 1; i <= rowIndex + 1; i++){for (int j = columnIndex - 1; j <= columnIndex + 1; j++){if (State.ALIVE == data.get(i).get(j)){counter++;}}}counter += State.ALIVE == data.get(rowIndex).get(columnIndex) ? -1:0;return counter;}/** below code to debug the state of the game.* */private void printGame(){System.out.println("-------------------");for (ArrayList<State> rowData : data){for (State ColumnData : rowData){System.out.print(ColumnData + " ");}System.out.println();}System.out.println("-------------------");}/** judge whether the next generation of cell should be alive.* */public State nextGenerateState(State alive, int neighbourNumber) {if (State.ALIVE == alive && (2 == neighbourNumber || 3 == neighbourNumber)){return State.ALIVE;}if (State.DIE == alive && 3 == neighbourNumber){return State.ALIVE;}return State.DIE;}public int getRow() {return row;}public void setRow(int row) {this.row = row;}public int getColumn() {return column;}public ArrayList<ArrayList<State>> getData() {return data;}public boolean isRunning() {return running;}public void setRunning(boolean running) {this.running = running;}public static void main(String[] args){Game game = new Game(15, 15, 25);}}

辅助类 CellStateOfSpecificPlace.java,用于记录上一代发生状态改变的位置:

package com.game;import com.game.enmu.State;public class CellStateOfSpecificPlace {private int rowIndex;private int columnIndex;private State state;public CellStateOfSpecificPlace() {this(0, 0, State.DIE);}public CellStateOfSpecificPlace(int rowIndex, int columnIndex, State state) {super();this.rowIndex = rowIndex;this.columnIndex = columnIndex;this.state = state;}public int getRowIndex() {return rowIndex;}public void setRowIndex(int rowIndex) {this.rowIndex = rowIndex;}public int getColumnIndex() {return columnIndex;}public void setColumnIndex(int columnIndex) {this.columnIndex = columnIndex;}public State getState() {return state;}public void setState(State state) {this.state = state;}}

辅助类State.java,定义Cell的状态的枚举类型,可以用于非界面的控制台打印输出

package com.game.enmu;public enum State {DIE,ALIVE;public String toString(){return name().substring(0, 1);}
}

GameBoard.java定义用于在Jswing中显示的包涵game的画图面板

package com.game;import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;import javax.swing.JPanel;import com.game.enmu.State;public class GameBoard extends JPanel{/*** */private static final long serialVersionUID = 1L;private Game game;  private int squre;public GameBoard() {  //this.setBounds(x, y, width, height);  }  public GameBoard(Game game){  this.game = game;  }  public void paint(Graphics g){  Graphics2D gg = (Graphics2D)g;  squre = (this.getWidth() - 50) / Math.max(game.getRow(), game.getColumn());   int x = 25;  int y = 10;  gg.setStroke(new BasicStroke(0.5f, BasicStroke.CAP_ROUND,   BasicStroke.JOIN_ROUND, 1.0f,new float[]{5f, 5f},0f));  gg.setColor(Color.blue);  for (int i = 0; i < game.getRow(); i++){  for (int j = 0; j < game.getColumn(); j++){  gg.drawRect(x + j * squre, y + i * squre, squre, squre);  }  }  for (int i = 0; i < game.getRow(); i++){  for (int j = 0; j < game.getColumn(); j++){  if (State.ALIVE == game.getData().get(i).get(j)){  gg.setColor(Color.blue);  gg.fillOval(x + j * squre, y + i * squre, squre, squre);  } else {  gg.setColor(Color.WHITE);  gg.fillOval(x + j * squre, y + i * squre, squre, squre);  } }  }  gg.setStroke(new BasicStroke(5f));  gg.setColor(Color.blue);  gg.drawRect(x - 1, y - 1, squre * game.getColumn() + 2, squre * game.getRow() + 2);  gg.dispose();  }
}

GameGUI.java 定义了软件的整体外观,软件的入口:

package com.game;import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;public class GameGUI extends JFrame{/*** */private static final long serialVersionUID = 1L;private JPanel mainframe;  private GameBoard board;  private JPanel control;   private BorderLayout border;  private JButton start;  private JButton stop;private JButton exit;  private JButton restart;private int row;  private int column;  private Game game;   private ActionListener actionlistener;public GameGUI(String title, int row, int column){super(title);this.row = row;this.column = column;initial();  setSize(900, 900);  this.setResizable(false);  this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  setLocationByPlatform(true);  this.setVisible(true);  this.requestFocus(); }private void initial() {createComponent();  layOut();  listener();  }private void createComponent() {mainframe = new JPanel();  control = new JPanel();  border = new BorderLayout();   start = new JButton("Start");    game = new Game(this.row, this.column, 81, this);  board = new GameBoard(game);  stop = new JButton("Stop");exit = new JButton("Exit");restart = new JButton("Re-Start");}private void layOut() {this.getContentPane().add(mainframe);   mainframe.setLayout(border);  mainframe.add(board, BorderLayout.CENTER);   mainframe.add(control, BorderLayout.EAST);  Box ve = Box.createVerticalBox();  ve.add(start);  ve.add(Box.createVerticalStrut(50));  stop.setSize(start.getWidth(), start.getHeight());  ve.add(stop); ve.add(Box.createVerticalStrut(50));ve.add(exit);ve.add(Box.createVerticalStrut(50));ve.add(restart);control.add(ve);}private void listener() {actionlistener = new ActionListener() {   @Override  public void actionPerformed(ActionEvent e) {  if (((JButton)(e.getSource())).getText().equals("Start")){  game.setRunning(true); game.runGame();} else if (((JButton)(e.getSource())).getText().equals("Exit")){  System.exit(0);  } else if (((JButton)(e.getSource())).getText().equals("Stop")){  game.setRunning(false); game.runGame();} else if (((JButton)(e.getSource())).getText().equals("Re-Start")){  game.setRunning(true);game.initinalGame();repaint();game.runGame();}}  };   start.addActionListener(actionlistener);  stop.addActionListener(actionlistener); restart.addActionListener(actionlistener); exit.addActionListener(actionlistener);}public static void main(String[] args) {GameGUI game = new GameGUI("Game Of Life", 25, 25);}}

最后一点测试代码,算是TDD(测试驱动开发):

package com.game.test;import static org.junit.Assert.*;//import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;import com.game.Game;
import com.game.enmu.State;public class TestGame {private Game mockGame;private Game game;@Beforepublic void setUp() throws Exception {//mockGame = EasyMock.createMock(Game.class);//mockGame.initinalGame();game = new Game(5, 5, 9);}@Afterpublic void tearDown() throws Exception {}@Testpublic void AliveCellMoreThanThreeNeibourShouldBeDie() {State result = game.nextGenerateState(State.ALIVE, 3 + 1);assertEquals(State.DIE, result);}@Testpublic void AliveCellLessThanOneNeibourShouldBeDie() {State result = game.nextGenerateState(State.ALIVE, 1);assertEquals(State.DIE, result);}@Testpublic void AliveCellWithTwoOrThreeNeibourShouldBeAlive() {State result = game.nextGenerateState(State.ALIVE, 2);assertEquals(State.ALIVE, result);result = game.nextGenerateState(State.ALIVE, 3);assertEquals(State.ALIVE, result);}@Testpublic void DieCellWithThreeNeibourShouldBeAlive() {State result = game.nextGenerateState(State.DIE, 3);assertEquals(State.ALIVE, result);}}

有好久没有写博客了,感谢你的再次惠顾。

最新文章

  1. SQL Server Extended Events 进阶 3:使用Extended Events UI
  2. 序列化模块--json模块--pickle模块-shelve模块
  3. 【CSS】【13】文字的排版
  4. BoW模型用于图像检索的一般化流程
  5. 素数计算之埃氏筛法、欧拉筛法
  6. 【教程】Jupyter notebook基本使用教程
  7. plc和pc串口通讯接线_Plc与pc串口调试手册
  8. 怎样改动SharePoint管理中心的语言
  9. linux 上自动执行caffe,linux下caffe的命令运行脚本
  10. Java第二章:数据类型和运算符
  11. 如何选择和部署长尾关键词
  12. js ajax 跨域上传文件,使用 Javascript 实现跨域上传文件到存储
  13. 路径规划-人工势场法(Artifical Potential Field)
  14. 整数补码加减法运算法则
  15. [计算机网络]网络层
  16. PySpark | Spark3新特性 | Spark核心概念
  17. 跳转QQ群android代码,android 怎么跳转直接到qq群
  18. java毕业设计怎么做?
  19. vue时间戳和时间的相互转换
  20. MIUI10国际版系统自定义字体设置办法

热门文章

  1. 畅聊无极限 微微网络电话聊天新玩法
  2. 支付宝又惹怒Windows Phone用户了
  3. 深富策略:流动性大概率有利 A股小幅攀升
  4. Yahoo! 用户密码泄漏安全启示录
  5. 2022-2028年全球集成驱动系统收入年复合增长率CAGR为 5.3%
  6. oracle 分批提取数据,Oracle创建关系分批抽取测试数据
  7. PhysX3.4文档(14) -- Scene Queries
  8. 使用腾讯地图批量转换地址为经纬度坐标
  9. 【最通俗易懂】C#有限状态机
  10. 折弯机使用说明书_折弯机的操作流程和应用方法