20220511
Life类的定义
每个Life对象都需要包含一个矩形数组,用1表示存活的细胞,0表示死亡的细胞。这样表示的好处是,如果要计算一个特定细胞的邻居,我们只需要把相邻的加起来就行了。因为每代的更新中都需要重复的计算每个细胞的邻居数量,所以Life类应当包含一个成员方法neighborCount来做这个任务。因为client的代码不需要知道这个方法,所以设置成private
计算邻居
可以预见(这说法特别像数学证明里的显然可得,然而初学者设计程序并不会有这种经验,只能是踩过坑才知道),如果需要边界细胞的邻居需要更多的判断条件,我们可以做一些优化,让数组的实际大小比定义的大一些,周围留出一圈,这样既避免了从0开始数的麻烦,也避免掉了多余的判断,让所有单元格计算邻居的时候可以用相同的代码
更新矩阵
更新的动作是相当明确的,我们首先计算一个新的矩阵用于记录更新后的数据,然后完整的把新的数据复制回原地即可(直接用新的不好吗?)
更新过程的算法比较容易,我们遍历非边界的地方,然后判断存活邻居的个数,实际上只需要判断2个和3个就行,其他的无论如何都是死亡。
这里需要注意下,如果需求让你打印死亡或存活的原因,就不能这么更新了。这也是很多搞算法的同学容易忽略的,算法题往往只需要一个正确的结果,记录这个结果一个bit位或者一个int就能搞定,而真实的需求往往会要求记录更多的信息。在这个例子里,可能的需求就是记录下每个细胞存活和死亡的代数和原因,后面可能还有可能查出相同复活和死亡原因的细胞分布,或者统计每个细胞存活的代数。什么?你没记?你怎么能不记呢?不记到时候加需求你怎么做啊?hhhhhh
程序的运行应当尽可能的留痕以供人工查阅或后期处理,记录尽可能原始的数据,而不是像AI神经网络一样输出一个莫名其妙大概率正确的结果而不知道为什么
如果考虑实际需求,那么数组的类型就不能是int了,还得再套个对象,记录当前细胞每代更新对自己的影响结果这种原始数据备用,可以用于计算它的出生代数、死亡代数、存活实际等其他数据,以防产品后面加需求。这是一个来自刚工作几年没少踩坑的开发的经验之谈,信不信由你
输入和输出
程序一般设计上是给很多人使用的,输入和输出的方法通常是最长的,输入的程序需要完整检查用户出入的正确性和一致性,输出要合理的格式化,要好好考虑什么应该输出什么不应该
编程原则:
保持你的输出和输出再不同的方法里,来让他们可以被轻易的修改,让你的系统有一定的custom tailored
提示语打印程序是一个非常简单的程序,只负责打印提示
测试用例drivers
可以写一小段程序给一个方法提供一些特定的输入,单独测试一个方法的运行是否正常
程序跟踪和debug
一个很常用的工具就是打印语句,详细的打印程序执行时所处的位置和相关变量的状态,生产环境一般用日志框架来处理,有的公司会有完整的日志收集、搜索、监控平台来做这件事情,自己写着玩直接print就好
脚手架代码也可以在debug的时候提供额外的帮助,用过之后记得删掉就好。当你的程序出现奇怪的错误时,脚手架代码可以放在1个或2个主程序的关键位置,用于快速确认问题出现的具体行数。
程序测试原则
测试数据的质量远比测试数据的数量要重要
程序测试可以证明bug的存在型,但无法证明bug不存在
程序测试方法
黑盒测试
使用普通数据、特殊的正常数据、边界附近的数据、非法的数据(参考测试工程师进酒吧)
白盒测试
分析程序的的每个分支,对每个执行路径都进行测试,大型程序种这种测试并不现实,一般选择性测试主要的执行路径
Ticking-Box测试
不知道怎么翻译,翻译成随缘测试吧hhh
写完了让用户自己去用吧,出了问题一定是你不会用,才不是bug呢
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
import java.util.Scanner; import javax.print.attribute.standard.PrinterLocation; public class Main0510_1 { /** * 前置条件:用户指定了初始布局 * 预期结果:根据规则打印所有存活的细胞 * 依赖:Life类和成员方法initialize、print、update */ public static void main(String[] args) { Life conf = new Life(); instructions(); conf.initialize(); conf.print(); System.out.println("是否继续下一代?"); while (userSaysYes()) { conf.update(); conf.print(); System.out.println("是否继续下一代?"); } } private static void instructions() { System.out.println("欢迎来到xxx游戏"); System.out.println("这个游戏使用的布局大小是" + Life.MAX_ROW + "x" + Life.MAX_COL); System.out.println("每个细胞都可以是活的也可以是死的"); System.out.println("它们将一代代的根据邻居的存活数量不断迭代"); } public static boolean userSaysYes() { try { Scanner in = new Scanner(System.in); boolean userInput = in.nextBoolean(); return userInput; } catch (Exception e) { return userSaysYes(); } } } class Life { public static final int MAX_ROW = 20; public static final int MAX_COL = 60; private int gird[][] = new int[MAX_ROW + 2][MAX_COL + 2]; // 多两行两列用于方便计数,省的判断边界 private int neighborCount(int row, int col) { int count = 0; for (int i = row - 1; i < row + 1; i++) { for (int j = col - 1; j < col + 1; j++) { count += gird[i][j];// 把存活的邻居记录上 } } count -= gird[row][col];// 减去掉自己,因为自己不是自己的邻居 return count; } public void initialize() { System.out.println("请输入存活细胞的坐标,以(-1,-1)结束,"); System.out.println("格式为行<空格>列,从1开始数:"); Scanner in = new Scanner(System.in); while (true) { int row = in.nextInt(); int col = in.nextInt(); // 正常程序建议in.nextLine()读入整行后trim // 然后先正则判断下格式对不对,避免脑残乱输入 if (row == -1 || col == -1) { break; } boolean rowCorrect = row >= 1 && row <= MAX_ROW; boolean colCorrect = col >= 1 && col <= MAX_COL; if (!rowCorrect || !colCorrect) { System.out.println("行或列输入错误,MDZZ,再试一次吧"); continue; } gird[row][col] = 1; } } public void print() { for (int row = 1; row <= MAX_ROW; row++) { for (int col = 1; col <= MAX_COL; col++) { int cell = gird[row][col]; System.out.print(cell); } System.out.println(); } } public void update() { int newGird[][] = new int[MAX_ROW + 2][MAX_COL + 2]; for (int row = 1; row <= MAX_ROW; row++) { for (int col = 1; col <= MAX_COL; col++) { int neighborCount = neighborCount(row, col); if (neighborCount == 2) { newGird[row][col] = gird[row][col]; } else if (neighborCount == 3) { newGird[row][col] = 1; } else { newGird[row][col] = 0; } } gird = newGird; } } } |
20220511结束,耗时一个半小时,明天继续看程序的评估
0 Comments