|
@@ -1,46 +1,105 @@
|
1
|
|
-import React, { useState } from 'react';
|
|
1
|
+import React from 'react';
|
2
|
2
|
import Cell from './Cell';
|
3
|
3
|
import Controls from './Controls';
|
|
4
|
+import { countLivingNeighbors } from './lib';
|
4
|
5
|
|
5
|
6
|
interface Props {
|
6
|
7
|
width?: number;
|
7
|
8
|
height?: number;
|
|
9
|
+ speed?: number;
|
8
|
10
|
}
|
9
|
11
|
|
10
|
|
-const Board = (props: Props) => {
|
11
|
|
- const width = props.width ?? 5;
|
12
|
|
- const height = props.height ?? 5;
|
|
12
|
+interface State {
|
|
13
|
+ grid: boolean[][];
|
|
14
|
+ running: boolean;
|
|
15
|
+}
|
|
16
|
+
|
|
17
|
+class Board extends React.Component<Props, State> {
|
|
18
|
+ timer: NodeJS.Timeout | null;
|
|
19
|
+
|
|
20
|
+ constructor(props: Props) {
|
|
21
|
+ super(props);
|
|
22
|
+ const height = props.height ?? 5;
|
|
23
|
+ const width = props.height ?? 5;
|
|
24
|
+ this.state = {
|
|
25
|
+ grid: Array(height).fill(Array(width).fill(false)),
|
|
26
|
+ running: false,
|
|
27
|
+ };
|
|
28
|
+ this.timer = null;
|
|
29
|
+ }
|
|
30
|
+
|
|
31
|
+ start = () => {
|
|
32
|
+ this.timer = setInterval(() => {
|
|
33
|
+ console.log('Iterating');
|
|
34
|
+ const { grid } = this.state;
|
|
35
|
+ const newGrid = [...grid].map((row, y) => {
|
|
36
|
+ const newRow = [...row].map((cell, x) => {
|
|
37
|
+ const isAlive = cell;
|
|
38
|
+ const livingNeighbors = countLivingNeighbors(grid, x, y);
|
|
39
|
+
|
|
40
|
+ if (isAlive) {
|
|
41
|
+ if (livingNeighbors === 2 || livingNeighbors === 3) {
|
|
42
|
+ return true;
|
|
43
|
+ } else {
|
|
44
|
+ return false;
|
|
45
|
+ }
|
|
46
|
+ } else {
|
|
47
|
+ if (livingNeighbors === 3) {
|
|
48
|
+ return true;
|
|
49
|
+ }
|
|
50
|
+ }
|
13
|
51
|
|
14
|
|
- const initialGrid: boolean[][] = Array(height).fill(Array(width).fill(false));
|
|
52
|
+ return isAlive;
|
|
53
|
+ });
|
15
|
54
|
|
16
|
|
- const [grid, setGrid] = useState<boolean[][]>(initialGrid);
|
|
55
|
+ return newRow;
|
|
56
|
+ });
|
17
|
57
|
|
18
|
|
- const [running, setRunning] = useState<boolean>(false);
|
|
58
|
+ this.setState({ grid: newGrid });
|
|
59
|
+ }, 500);
|
|
60
|
+ };
|
|
61
|
+
|
|
62
|
+ stop = () => {
|
|
63
|
+ if (this.timer) clearInterval(this.timer);
|
|
64
|
+ };
|
19
|
65
|
|
20
|
|
- const toggleCell = (x: number, y: number) => {
|
21
|
|
- const newGrid = [...grid];
|
|
66
|
+ toggleCell = (x: number, y: number) => {
|
|
67
|
+ const newGrid = [...this.state.grid];
|
22
|
68
|
const newRow = [...newGrid[y]];
|
23
|
69
|
newRow[x] = !newGrid[y][x];
|
24
|
70
|
newGrid[y] = newRow;
|
25
|
|
- setGrid(newGrid);
|
|
71
|
+ this.setState({ grid: newGrid });
|
26
|
72
|
};
|
27
|
73
|
|
28
|
|
- const toggleRunning = () => {
|
29
|
|
- setRunning(!running);
|
|
74
|
+ toggleRunning = () => {
|
|
75
|
+ if (this.state.running) this.stop();
|
|
76
|
+ if (!this.state.running) this.start();
|
|
77
|
+ this.setState({ running: !this.state.running });
|
30
|
78
|
};
|
31
|
79
|
|
32
|
|
- return (
|
33
|
|
- <div className="board">
|
34
|
|
- <Controls running={running} toggle={toggleRunning} />
|
35
|
|
- {grid.map((row, y) => (
|
36
|
|
- <div className="row" key={y}>
|
37
|
|
- {row.map((cell, x) => (
|
38
|
|
- <Cell x={x} y={y} alive={cell} toggle={toggleCell} key={x} />
|
39
|
|
- ))}
|
40
|
|
- </div>
|
41
|
|
- ))}
|
42
|
|
- </div>
|
43
|
|
- );
|
44
|
|
-};
|
|
80
|
+ render() {
|
|
81
|
+ const { grid, running } = this.state;
|
|
82
|
+
|
|
83
|
+ return (
|
|
84
|
+ <div className="board">
|
|
85
|
+ <Controls running={running} toggle={this.toggleRunning} />
|
|
86
|
+ {grid.map((row, y) => (
|
|
87
|
+ <div className="row" key={y}>
|
|
88
|
+ {row.map((cell, x) => (
|
|
89
|
+ <Cell
|
|
90
|
+ disabled={running}
|
|
91
|
+ x={x}
|
|
92
|
+ y={y}
|
|
93
|
+ alive={cell}
|
|
94
|
+ toggle={this.toggleCell}
|
|
95
|
+ key={x}
|
|
96
|
+ />
|
|
97
|
+ ))}
|
|
98
|
+ </div>
|
|
99
|
+ ))}
|
|
100
|
+ </div>
|
|
101
|
+ );
|
|
102
|
+ }
|
|
103
|
+}
|
45
|
104
|
|
46
|
105
|
export default Board;
|