引子

这是16年最后的一个练手项目,一贯的感觉就是,做项目容易,写说明文档难。更何况是一个唤起抑郁感觉的项目,码下的每个字,心就如加了一个千斤的砝码。

2016年,有些事我都已忘记,但我现在还记得。2.14那天我见到了六年没见面高中同桌浩哥。为了缓解闰土式的尴尬,我提议到车站附近公园走走。

浩哥被北风吹得不停地发抖,而我为了抑制寒冷,一根接一根地抽烟。

“这蚂蚁好大只。”

“踩死了对这个种群也没什么影响!” 言罢,浩哥一脚踩死了这只蚂蚁。

“如果我们放过它,它存在的价值是什么呢?”

十年前,浩哥永远一副对现状不满的态度——唤起了我的正能量。这方面我们形成了若干共同认知,这是我们为什么能成为朋友。

对现状不满,所以要变得更强。

但是十年后,我们都在思考:“我是谁,我在哪里,我往何处去?”

于是我们,聊起了哲学话题,谈起了读过的书——自私的基因,生命是什么,沦为民工小说的三体。

当年我们分到一个了生化班,他为选了化学,而不是物理而后悔,在他看来,物理才是真正的理科。而我受他的影响,放弃生物最终选了更加理科的化学。

结果他大学学了管理,我学了药学,最后陷入进生物的泥坑里。

在谈话中,我一直试图诱使浩哥回答这个问题:岁月一年一年弄冷一个人,但你心中还有当初那份温暖吗?

如果你想有价值地活下去,答案应该是肯定的。但是结局...

根据维基百科条目 Conway's Game of Life(康威生命游戏),康威生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。

给出一个m*n的细胞矩阵,每个细胞都有一个初始状态:生存(1)或死亡(0)。每个细胞的变化都与它周围8个细胞有关,规则如下:

当前细胞为存活状态时,当周围存活细胞不到2个时, 该细胞变成死亡状态。(模拟生命数量稀少)

当前细胞为存活状态时,当周围有2个或3个存活的细胞时, 该细胞保持原样。

当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)

当前细胞为死亡状态时,当周围恰好有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)

请按照规则编写出可视化的Game of Life游戏,类似于下面这个示例:

https://codepen.io/FreeCodeCamp/full/reGdqx/

游戏可以以任意状态开始,用户可以暂停或者重置游戏,并且能够观察到当前游戏进行的世代


组件的逻辑

首先得分组件,当组件混乱时,用ps画个图是个很好的方法。就省得打太多字了。

和井字棋项目一样,采用的是一维数组。

最麻烦的还是GameBoard,为了方便,还是采用table布局。

要求点击面板,相应的格子变绿。

var GameBoard=React.createClass({
renderSquare:function(index,value){
return (
<Square
index={index}
createLife={()=>this.props.createLife(index)}
value={this.props.status[index]}
key={index.toString()} />
);
},
getSquare:function(rows,cols){
var index=rows*cols;
var arr=[];
for(var i=index;i<index+cols;i++){
arr.push(this.renderSquare(i));
}
return arr;
},
getBoardRow:function(rows,cols){
var arr=[];
for(var i=0;i<rows;i++){
arr.push(<tr key={i}>{this.getSquare(i,cols)}</tr>);
}
return arr;
}, render:function(){
return (
<tbody>
{this.getBoardRow(this.props.size.rows,this.props.size.cols)}
</tbody>
);
}
}); var Square=React.createClass({
handleClick:function(){
this.props.createLife(this.props.index);
}, render:function(){
if(this.props.value=='true'){
return (
<td style={{background:'green'}}></td>
);
}else{
return (
<td onClick={this.handleClick}></td>
);
}
}
});

状态

毫无疑问,这些组件的最终状态是以Game为主的。所以状态放在最上层。

那么需要哪些状态呢?一般来说,定时器里需要更新的都是状态。按钮相关都是状态

而之所以做游戏信息面板可以把状态很好的可视化出来。

  1. 培养皿尺寸——初始为70*50

  2. 游戏代数

  3. 繁殖速度

  4. 是否开始

  5. 存活率曲线——用来存放历代存活率的数组。

    我曾经考虑过它是否作为状态,结果是应该,因为它是动态变化的。

  6. 主游戏面板的细胞状态。是个随机一维数组

初始状态

既然培养皿的细胞是一维数组,就可以做一个随机数,培养皿随机填充细胞,覆盖率为30%。如果为有细胞,值为"ture",反之为"false"

var Game=React.createClass({
getInitialState:function(){
// 把棋盘初始化为一个数组。阈值为70%。
var arr=Array(50*70);
for(var i=0;i<arr.length;i++){
arr[i]=Math.random();
if(arr[i]>0.7){
arr[i]='true';
}else{
arr[i]='false';
}
} return {
size:{
rows:50,
cols:70
},
generation:1,
status:arr,
bStart:false,
speed:200,
lifeCurve:[]
}
},
。。。。

有了状态,把属性传下去就行了。


算法

前面的状态设置和传递都不是什么大的问题,写各种函数也不算麻烦。所以就省略了。

而说到算法,其实当时第一时间就想到井字棋项目。无非是对每个格子做上下左右斜的判断。

所要做的就是统计个数。

但是培养皿可能没那么大,所以妨做多一个判断。

比如index是3499。要判断index+1——但status的第3500项不存在,就让它减去status的长度3500.

如果index是0,要判断index-1,可以让它在原基础上加上3500.

还有一个问题,当一行结束后,index+1就跳到下一行了。也可以根据this.state.rows的值做判断(是否整除),在这里不是很大的影响,就不判断了。

好了。这就是定时器内最核心的算法了

						this.setState(function(prev){
var status=prev.status.slice();
var _status=status.slice(); var rows=prev.size.rows;
var cols=prev.size.cols;
var total=cols*rows; for(var i=0;i<status.length;i++){
var leftTop=status[i-cols-1]?i-cols-1:i-cols-1+total;
var top=status[i-cols]?i-cols:i-cols+total;
var rightTop=status[i-cols+1]?i-cols+1:i-cols+1+total; var left=status[i-1]?i-1:i-1+total;
var right=status[i+1]?i+1:i+1-total; var leftBot=status[i+cols-1]?i+cols-1:i+cols-1-total;
var bot=status[i+cols]?i+cols:i+cols-total;
var rightBot=status[i+cols+1]?i+cols+1:i+cols+1-total; var around=
[
status[leftTop],
status[top],
status[rightTop],
status[left],
status[right],
status[leftBot],
status[bot],
status[rightBot]
]; var count=around.filter(function(_item){
return _item=='true';
}).length; if(status[i]=='true'){
if(count<2){
_status[i]='false';
}else if(count==2||count==3){
_status[i]='true';
}else if(count>3){
_status[i]='false';
}
}else if(status[i]=='false'){
if(count==3){
_status[i]='true';
}
}
} this.getLifeCurve() return {
generation:prev.generation+1,
status:_status
}
});

算法问题搞定,看看还有哪些坑。

事实上我觉得坑是最有价值的部分

定时器

我遭遇到最大的坑是定时器。

原来的定时器是放在componentDidMount里面的。但是我后来怀疑了。

之所以有这个怀疑,还是基础javascript的问题。因为点击切换速度,值已经改变了但是定时器一旦打开就按照既定计划执行了。间隔时间改变不了。

所以采用这样的写法

var _this=this;
(function resetTimer(){
_this.timer=setInterval(function(){
//balabala...
clearInterval(_this.timer);
resetTimer();
},_this.state.speed)
})();

简单的生命曲线

生命曲线图的展示属于数据可视化的内容,但实际上只要把这个数组拿到手,怎么显示其实就是其它技术的问题了。

实际上,这里采用的是简洁的方法,就是在黑色背景下累加高度为1px的白色div,并把生存率乘以100再四舍五入。

把这个div的宽度作为尚存率的显示数值。

每次渲染的时候就把这个数组放到黑色背景中。

getLifeCurve:function(){
var lifeCurve=this.state.lifeCurve.slice();
if(lifeCurve.length>500){
return false;
}//大于500步时记录终止。 var covered=parseInt(this.state.status.slice().filter(function(item){
return item=='true';
}).length/this.state.status.length*10000)/100;
lifeCurve.push(covered);
this.setState({
lifeCurve:lifeCurve
});
},

结束

demo地址:http://s.codepen.io/dangjingtao/debug/qqeXyz

活着就是对抗熵增原理的过程。

在这个规则下的生命看起来太残酷了,最后覆盖率大多在3%左右。但是只有这样,才有了各种图案。

2017,愿每个有梦想的人,无悔地怒放出绚烂的生命之花。

React项目(二):生命游戏的更多相关文章

  1. react学习二 生命周期

    转自:https://www.cnblogs.com/gdsblog/p/7348375.html react 中compent getDefaultProps object getDefaultPr ...

  2. C++走向远洋——25(项目二,游戏类)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:game.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  3. 22.2、react生命周期与react脚手架(二)

    一.类:es6 <script type="text/babel"> class Person{ age = 10; constructor(name){ this.n ...

  4. React项目中使用Mobx状态管理(二)

    并上一节使用的是普通的数据状态管理,不过官方推荐使用装饰器模式,而在默认的react项目中是不支持装饰器的,需要手动启用. 官方参考 一.添加配置 官方提供了四种方法, 方法一.使用TypeScrip ...

  5. 七天接手react项目 —— 生命周期&受控和非受控组件&Dom 元素&Diffing 算法

    生命周期&受控和非受控组件&Dom 元素&Diffing 算法 生命周期 首先回忆一下 vue 中的生命周期: vue 对外提供了生命周期的钩子函数,允许我们在 vue 的各个 ...

  6. 小白从零开始阿里云部署react项目+node服务接口(二:node服务+web)

    我们用极简的方式来创建服务,没有任何附加功能 1 新建一个server文件夹 2 使用npm init 或者yarn init  一路enter 3  yarn add  express cors  ...

  7. 生命游戏/Game of Life的Java实现(转)

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

  8. 生命游戏/Game of Life的Java实现

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

  9. Python实现生命游戏

    1. 生命游戏是什么 生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机.它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个 ...

随机推荐

  1. 网络安全——Base64编码、MD5、SHA1-SHA512、HMAC(SHA1-SHA512)哈希

    据说今天520是个好日子,为什么我想起的是502.500.404这些?还好服务器没事! 一.Base64编码 Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之 ...

  2. 【转】深入浅出JavaScript之this

    JavaScript中的this比较灵活,根据在不同环境下,或者同一个函数在不同方式调用下,this都有可能是不同的.但是有一个总的原则,那就是this指的是,调用函数的那个对象. 下面是我的学习笔记 ...

  3. jQuery解决iframe高度自适应代码

    网上查了好多用着都不行,自己搞定了:在包含iframe的页面中加入以下脚本,基本思想是在iframe加载内容后重新设置高度,下面代码尽在IE6中用过,没在其他浏览器中测试. 代码如下: <scr ...

  4. 从Sql Server表中随机获取一些记录最简单的方法

    * FROM test ORDER BY NewID() 注意,使用时,请将‘test’改为真实的表名.

  5. tftp-nfs开发环境搭建

    嵌入式开发通常使用主机-开发板的开发模式,在裸板开发中,我们通常使用串口调试工具传递文件,比如windows平台的超级终端,SecuCRT以及Linux平台的ckermit(题外话:ckermit比w ...

  6. 【python之路1】python安装与环境变量配置

    直接搜索 Python,进入官网,找到下载,根据个人电脑操作系统下载相应的软件.小编的是windows os .下载python-2.7.9.msi 安装包  双击安装程序,进入安装步骤.在安装过程中 ...

  7. TCP/IP协议(二)tcp/ip基础知识

    今天凌晨时候看书,突然想到一个问题:怎样做到持续学习?然后得出这样一个结论:放弃不必要的社交,控制欲望,克服懒惰... 然后又有了新的问题:学习效率时高时低,状态不好怎么解决?这也是我最近在思考的问题 ...

  8. java socket收发http协议内容

    来自:https://www.oschina.net/code/snippet_2009881_48232 import java.io.BufferedReader; import java.io. ...

  9. 记一次企业级爬虫系统升级改造(二):基于AngleSharp实现的抓取服务

    爬虫系统升级改造正式启动: 在第一篇文章,博主主要介绍了本次改造的爬虫系统的业务背景与全局规划构思: 未来Support云系统,不仅仅是爬虫系统,是集爬取数据.数据建模处理统计分析.支持全文检索资源库 ...

  10. 基于C/S架构的3D对战网络游戏C++框架 _06搭建C/S架构的基本通信框架(尚未写完会重新编辑后再发出)

    本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...