├── src
├── react-app-env.d.ts
├── index.tsx
└── App.tsx
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── .gitignore
├── tsconfig.json
└── package.json
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benawad/react-conway-game-of-life/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benawad/react-conway-game-of-life/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/benawad/react-conway-game-of-life/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 |
5 | ReactDOM.render(, document.getElementById("root"));
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "game-of-life",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/jest": "24.0.23",
7 | "@types/node": "12.12.7",
8 | "@types/react": "16.9.11",
9 | "@types/react-dom": "16.9.4",
10 | "immer": "^5.0.0",
11 | "react": "^16.11.0",
12 | "react-dom": "^16.11.0",
13 | "react-scripts": "3.2.0",
14 | "typescript": "3.7.2"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": "react-app"
24 | },
25 | "browserslist": {
26 | "production": [
27 | ">0.2%",
28 | "not dead",
29 | "not op_mini all"
30 | ],
31 | "development": [
32 | "last 1 chrome version",
33 | "last 1 firefox version",
34 | "last 1 safari version"
35 | ]
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useCallback, useRef } from "react";
2 | import produce from "immer";
3 |
4 | const numRows = 50;
5 | const numCols = 50;
6 |
7 | const operations = [
8 | [0, 1],
9 | [0, -1],
10 | [1, -1],
11 | [-1, 1],
12 | [1, 1],
13 | [-1, -1],
14 | [1, 0],
15 | [-1, 0]
16 | ];
17 |
18 | const generateEmptyGrid = () => {
19 | const rows = [];
20 | for (let i = 0; i < numRows; i++) {
21 | rows.push(Array.from(Array(numCols), () => 0));
22 | }
23 |
24 | return rows;
25 | };
26 |
27 | const App: React.FC = () => {
28 | const [grid, setGrid] = useState(() => {
29 | return generateEmptyGrid();
30 | });
31 |
32 | const [running, setRunning] = useState(false);
33 |
34 | const runningRef = useRef(running);
35 | runningRef.current = running;
36 |
37 | const runSimulation = useCallback(() => {
38 | if (!runningRef.current) {
39 | return;
40 | }
41 |
42 | setGrid(g => {
43 | return produce(g, gridCopy => {
44 | for (let i = 0; i < numRows; i++) {
45 | for (let k = 0; k < numCols; k++) {
46 | let neighbors = 0;
47 | operations.forEach(([x, y]) => {
48 | const newI = i + x;
49 | const newK = k + y;
50 | if (newI >= 0 && newI < numRows && newK >= 0 && newK < numCols) {
51 | neighbors += g[newI][newK];
52 | }
53 | });
54 |
55 | if (neighbors < 2 || neighbors > 3) {
56 | gridCopy[i][k] = 0;
57 | } else if (g[i][k] === 0 && neighbors === 3) {
58 | gridCopy[i][k] = 1;
59 | }
60 | }
61 | }
62 | });
63 | });
64 |
65 | setTimeout(runSimulation, 100);
66 | }, []);
67 |
68 | return (
69 | <>
70 |
81 |
95 |
102 |
108 | {grid.map((rows, i) =>
109 | rows.map((col, k) => (
110 |
{
113 | const newGrid = produce(grid, gridCopy => {
114 | gridCopy[i][k] = grid[i][k] ? 0 : 1;
115 | });
116 | setGrid(newGrid);
117 | }}
118 | style={{
119 | width: 20,
120 | height: 20,
121 | backgroundColor: grid[i][k] ? "pink" : undefined,
122 | border: "solid 1px black"
123 | }}
124 | />
125 | ))
126 | )}
127 |
128 | >
129 | );
130 | };
131 |
132 | export default App;
133 |
--------------------------------------------------------------------------------