├── 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 | --------------------------------------------------------------------------------