vivian blog

build the game of life

build the game of life(204)

相关资料

game of life(英文版)
康威生命游戏(中文版)


规则

  • 当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
  • 当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
    当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
  • 当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)

分析

核心算法部分

  • 创建一个2d数组来储存细胞
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
createGrid() {
var grid = [];
for (var i = 0; i < 25; i++) {
var row = [];
for (var j = 0; j < 40; j++) {
var random = Math.random()/1; //简单的random算法,可百度
if (random < 0.3) {
row.push(1);
} else {
row.push(0);
}
}
grid.push(row);
}
return grid;
}
  • 由规则可知,其主要难点是找出每个细胞的邻居总数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
calculateNeighbour(x,y) {
//注意25乃是arr.length;
//40乃是arr[i].length;
let topRow = x-1<0?(25-1):x-1;
let bottomRow = (x+1===25)?0:x+1;
let leftColumn = y-1<0?(40-1):y-1;
let rightColumn = (y+1===40)?0:y+1;
let total = 0;
total += (this.state.Grid[topRow][leftColumn]>0?1:0);
total += (this.state.Grid[topRow][y]>0?1:0);
total += (this.state.Grid[topRow][rightColumn]>0?1:0);
total += (this.state.Grid[x][leftColumn]>0?1:0);
total += (this.state.Grid[x][rightColumn]>0?1:0);
total += (this.state.Grid[bottomRow][leftColumn]>0?1:0);
total += (this.state.Grid[bottomRow][y]>0?1:0);
total += (this.state.Grid[bottomRow][rightColumn]>0?1:0);
return total;
}
  • 对grid中所有元素都应用生命游戏规则
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
update() {
var copyGrid = [];
var len = this.state.Grid.length;
var Len = this.state.Grid[0].length;
for (var i = 0; i < len ; i++) {
let row = [];
for (var j = 0; j < this.state.Grid[i].length; j++) {
var neighbours = this.calculateNeighbour(i,j);
if(this.state.Grid[i][j]){
if(neighbours<2||neighbours>3){
row.push(0);
}else {
row.push(1);
}
}else{
if(neighbours===3){
row.push(2);
}else{
row.push(0);
}
}
}
copyGrid.push(row);
}
return copyGrid;
}

至此核心算法算是完成。

render 部分

使用React框架,想要了解更多React框架知识,请移步此处

  • GameFeedBack部分
    – 主要是显示当前alive的总数和generation的总数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class GameFeedback extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="feedback col-md-12 col-sm-12 col-lg-12">
<div className="population col-md-6">
<h5>Population: {this.props.population}</h5>
</div>
<div className="generations col-md-6">
<h5>Generations: {this.props.generations}</h5>
</div>
</div>
);
}
}
  • ButtonTool部分
    – 注意此处onclick事件所调用的方法均在App组件中定义,是为了避免this的混淆,用this.props获取方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ButtonTool extends React.Component {
constructor(props) {
super(props);
}
render() {
var text = 'Start';
if (this.props.running) {
text = 'Parse';
}
return (
<div className="btn-group" role="group" aria-label="...">
<button onClick = {this.props.parseGame} id="playOrParse" type="button" className="btn btn-primary">{text}</button>
<button onClick = {this.props.clear} id="clear" type="button" className="btn btn-primary">clear</button>
<button onClick = {this.props.restart} id="restart" type="button" className="btn btn-primary">restart</button>
</div>
);
}
}
  • Board部分
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
class Board extends React.Component {
//无论何时何地,只要状态改变,即会调用render函数
constructor(props) {
super(props);
}
render() {
return (
<div className = 'tableContainer'>
<table>
<tbody>
{this.props.Grid.map((row,i) =>
<tr key={i}>{row.map((cell,j) =>
<Cell
X = {i}
Y = {j}
key = {j}
handleClick={this.props.onClick.bind(this)}
status={cell}
/>)}
</tr>)}
</tbody>
</table>
</div>
);
}
}
  • Cell部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cell extends React.Component {
constructor(props) {
super(props);
}
render() {
let fill = '';
if(this.props.status===1){
fill = 'alive';
}else if(this.props.status===2){
fill = 'newBorn';
}else{
fill='';
}
return (<td className ={fill} onClick={this.props.handleClick.bind(this,this.props.X,this.props.Y)}></td>);
}
}
  • App部分
    – 此部分主要是将先前的所有组件连接在一起,并且定义交互时需要调用的方法,以及game的过程方法