细胞自动机与表现数据分离
细胞自动机的Java实现
一丶
先说说这个题目吧,还是第一次接触这种类型的题目:生命游戏中,对于任意细胞,规则如下:
每个细胞有两种状态-存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。
当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)
可以把最初的细胞结构定义为种子,当所有在种子中的细胞同时被以上规则处理后, 可以得到第一代细胞图。按规则继续处理当前的细胞图,可以得到下一代的细胞图,周而复始。
1.看到这个题目的第一反应是将整个生命环境看作一个矩阵,其中的每一个细胞以二维坐标的形式存在于环境中,那么这个细胞类就有三个属性在生命游戏中的:x坐标,y坐标,生命状态。但是,这样做了以后发现两个问题:
1)环境边界处的细胞生命状态判断实现很难。
2)细胞之间的生命状态无法实现关联关系。
2.之后是将单个细胞单独做一个(BasicUnit类),还是以3*3九宫格的方式判段我们抽象出来的单元细胞的生命状态,再将其放入生命游戏沙盘(LifeGame类)中演变,当然,还是写一个细胞信息类(Cell类)将从BasicUnit类中判断得来的细胞生命状态和在LifeGame类中的坐标两个信息存储起来。
3.到此,整个生命游戏的实现逻辑就构建好了,接下来就是编码实现的事情了。
4.编码过程中遇到很多坑,这里就不多提了,碰到的坑都是为了以后少进坑做贡献。
5.下面讲这三个类代码贴出来(不能直接跑,用了单元测试,测试每个类的逻辑是否实现,要跑的话需要自己写一个run类)
BasicUnit类:
package org.coach.tdd.template; /** * Created by lzy on 17-6-3. */ /** * BasicUnit类判断单元细胞的生命状态。 */ public class BasicUnit { private static final int DEATH = 0; private static final int LIVE = 1; private int[][] testUnit = new int[3][3]; //将一个细胞及其周围相邻细胞看作一个基本单元 private int status = DEATH; public void setTestUnit(int[][] testUnit) { this.testUnit = testUnit; } /** * . * 获得单元细胞生命状态 * * @param basicUnit 该细胞周围细胞生命状态 * @return 该细胞生命状态 */ public int getUnitCelltatus(int[][] basicUnit) { int numberofcell = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (1 == basicUnit[i][j]) { if (1 == i && 1 == j) { } else { numberofcell += 1; } } } } if (3 == numberofcell) { return 1; } else if (2 == numberofcell) { return basicUnit[1][1]; } return 0; } }
Cell类:
package org.coach.tdd.template; /** * Created by lzy on 17-6-3. */ /** * Cell类存储细胞的坐标,生命状态,周围细胞状态。 */ public class Cell { private int location_x = 0; private int location_y = 0; private int cellStatus = 0; private int[][] aroundCells = new int[3][3]; /** * 构造方法的描述 * * @param location_x 细胞的x坐标 * @param location_y 细胞的y坐标 * @param cellStatus 细胞的状态 */ public Cell(int location_x, int location_y, int cellStatus) { this.location_x = location_x; this.location_y = location_y; this.cellStatus = cellStatus; } public int getLocation_x() { return location_x; } public int getLocation_y() { return location_y; } public int getCellStatus() { return cellStatus; } public void setCellStatus(int cellStatus) { this.cellStatus = cellStatus; } /** * 获取更新后的细胞状态 * * @return */ public int getAfterTurnCellStatus() { BasicUnit basicUnit = new BasicUnit(); this.setCellStatus(basicUnit.getUnitCelltatus(aroundCells)); return this.getCellStatus(); } }
LifeGame类:
package org.coach.tdd.template; /** * Created by lzy on 17-6-3. */ /** * LifeGame类生命游戏具体操作类 */ public class LifeGame { public static final int LEFTUP = 1; public static final int RIGHTUP = 2; public static final int LEFTDOWN = 3; public static final int RIGHTDOWN = 4; private int framesize = 0; //框架大小,表示为正方体框架的边长 private int[][] frame; private int[][] nextframe; public LifeGame(int framesize) { this.framesize = framesize; } public int[][] getNextframe() { return nextframe; } public void setFrame(int[][] frame) { this.frame = frame; } public void init() { frame = new int[framesize][framesize]; nextframe = new int[framesize][framesize]; } public void putCell(Cell cell) { frame[cell.getLocation_x()][cell.getLocation_y()] = cell.getCellStatus(); } public int locationIsCorner(int x, int y) { if (0 == x && 0 == y) { return LEFTUP; } else if (0 == x && framesize - 1 == y) { return RIGHTUP; } else if (framesize - 1 == x && 0 == y) { return LEFTDOWN; } else if (framesize - 1 == x && framesize - 1 == y) { return RIGHTDOWN; } return 0; } public int locationIsLine(int x, int y) { if (0 == x && 0 != y && framesize - 1 != y) { return 11; } else if (framesize - 1 == x && 0 != y && framesize - 1 != y) { return 12; } else if (0 == y && 0 != x && framesize - 1 != x) { return 13; } else if (framesize - 1 == y && 0 != x && framesize - 1 != x) { return 14; } return 0; } /** * 获取指定坐标细胞周围细胞状态 * * @param x cell_x坐标 * @param y cell_y坐标 * @return 指定坐标细胞周围细胞状态 */ public int[][] getAroundStatus(int x, int y) { //corner int[][] aroundUnit = new int[3][3]; int isCorner = locationIsCorner(x, y); int isLine = locationIsLine(x, y); if (isCorner != 0) { switch (isCorner) { case 1: { aroundUnit = new int[][]{{frame[x][y + 1], frame[x + 1][y], frame[x + 1][y + 1]}, {0, frame[x][y], 0}, {0, 0, 0}}; break; } case 2: { aroundUnit = new int[][]{{frame[x][y - 1], frame[x + 1][y - 1], frame[x + 1][y]}, {0, frame[x][y], 0}, {0, 0, 0}}; break; } case 3: { aroundUnit = new int[][]{{frame[x - 1][y], frame[x - 1][y + 1], frame[x][y + 1]}, {0, frame[x][y], 0}, {0, 0, 0}}; break; } case 4: { aroundUnit = new int[][]{{frame[x - 1][y], frame[x - 1][y - 1], frame[x][y - 1]}, {0, frame[x][y], 0}, {0, 0, 0}}; break; } default: break; } } //line else if (isLine != 0) { switch (isCorner) { case 11: { aroundUnit = new int[][]{{frame[x][y - 1], frame[x][y + 1], frame[x + 1][y - 1]}, {frame[x + 1][y], frame[x][y], frame[x + 1][y + 1]}, {0, 0, 0}}; break; } case 12: { aroundUnit = new int[][]{{frame[x - 1][y - 1], frame[x - 1][y], frame[x - 1][y + 1]}, {frame[x][y - 1], frame[x][y], frame[x][y + 1]}, {0, 0, 0}}; break; } case 13: { aroundUnit = new int[][]{{frame[x - 1][y], frame[x - 1][y + 1], frame[x][y + 1]}, {frame[x + 1][y], frame[x][y], frame[x + 1][y + 1]}, {0, 0, 0}}; break; } case 14: { aroundUnit = new int[][]{{frame[x - 1][y - 1], frame[x - 1][y], frame[x][y - 1]}, {frame[x + 1][y - 1], frame[x][y], frame[x + 1][y]}, {0, 0, 0}}; break; } default: break; } } else { aroundUnit = new int[][]{{frame[x - 1][y - 1], frame[x - 1][y], frame[x - 1][y + 1]}, {frame[x][y - 1], frame[x][y], frame[x][y + 1]}, {frame[x + 1][y - 1], frame[x + 1][y], frame[x + 1][y + 1]}}; } return aroundUnit; } /** * 更新环境中所有细胞生命状态 * * @return 环境中是否还在活着的细胞 */ public boolean isfreshFrame() { // boolean flag =false; boolean hasLife = false; BasicUnit basicUnit = new BasicUnit(); for (int i = 0; i < framesize; i++) { for (int j = 0; j < framesize; j++) { int[][] aroundUnit = getAroundStatus(i, j); int status = basicUnit.getUnitCelltatus(aroundUnit); nextframe[i][j] = status; } } for (int i = 0; i < framesize; i++) { for (int j = 0; j < framesize; j++) { System.out.print(frame[i][j]); } System.out.println(""); } System.out.println(""); for (int i = 0; i < framesize; i++) { for (int j = 0; j < framesize; j++) { if (1 == nextframe[i][j]) { hasLife = true; } System.out.print(nextframe[i][j]); } System.out.println(""); } // flag=true; return hasLife; } /** * 运行整个生命游戏 * * @param lifeGame 生命游戏的初始对象 * @return 运行成功 */ public boolean run(LifeGame lifeGame) { while (lifeGame.isfreshFrame()) { lifeGame.setFrame(lifeGame.getNextframe()); System.out.println(""); } return true; } }
表现与数据分离
表现与数据数据分离
表现:顾名思义,就是表达出来的现象,在前端来看,就是html+css,就是平常所看到的的网页的架子;
数据:一般是从后端数据库或从哪爬过来的数据,然后在前台显示出来,即是网页中各个结构上的内容;
好处与坏处
好处:模块化 –> 容易测试 –> 降低bug频率;
坏处:程序结构复杂,比较耗时,上手有学习曲线;
应用场景:
项目具有明显的数据需求,比如要与很多Service交互,业务流程复杂,表单很多
非应用场景:
项目是典型的静态信息展示型页面,或是微型的内部app,或是产品idea验证时期的MVP。
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="../jquery-1.7.1.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { //定义一个controller var piliController = { //选择视图 start: function () { this.view.start(); }, //将用户操作映射到模型更新上 set: function (name) { this.model.setPerson(name); } }; piliController.model = { piliKV: { '叶小钗': '刀狂剑痴', '一页书': '百世经纶', '素还真': '清香白莲' }, curPerson: null, //数据模型负责业务逻辑和数据存储 setPerson: function (name) { this.curPerson = this.piliKV[name] ? name : null; this.onchange(); }, //通知数据同步更新 onchange: function () { piliController.view.update(); }, //相应视图对当前状态的查询 getPiliAction: function () { return this.curPerson ? this.piliKV[this.curPerson] + this.curPerson : '???'; } }; piliController.view = { //用户触发change事件 start: function () { $('#pili').change(this.onchange); }, onchange: function () { piliController.set($('#pili').val()); }, update: function () { $('#end').html(piliController.model.getPiliAction()); } }; piliController.start(); }); </script> </head> <body> <select id="pili"> <option value="叶小钗">叶小钗</option> <option value="一页书">一页书</option> <option value="素还真">素还真</option> </select> <div id="end"></div> </body> </html>
- 我们好像就在view中使用了选择器获取dom。
- 我们的数据似乎在model中,我们可以随意改变,但是并不会影响到我们dom。
- view和model是完全独立的,我们的controller恰好把他们串联起来了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)