import React from 'react'; import Cell from './Cell'; import Controls from './Controls'; import { generateNewGrid, updateGridDimensions } from './lib'; const DEFAULT_WIDTH = 30; const DEFAULT_HEIGHT = 20; const DEFAULT_SPEED = 100; const DEFAULT_CELL_SIZE = 30; const INITIAL_GRID = Array(DEFAULT_HEIGHT).fill( Array(DEFAULT_WIDTH).fill(false), ); const INITIAL_STATE: State = { grid: [...INITIAL_GRID], running: false, generations: 0, cellSize: DEFAULT_CELL_SIZE, speed: DEFAULT_SPEED, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, }; interface State { grid: boolean[][]; running: boolean; cellSize: number; speed: number; width: number; height: number; generations: number; } class Board extends React.Component<{}, State> { timer: NodeJS.Timeout | null; constructor(props: {}) { super(props); this.state = INITIAL_STATE; this.timer = null; } start = () => { this.timer = setInterval(() => { const grid = generateNewGrid(this.state.grid); this.setState({ grid, generations: this.state.generations + 1 }); if (!grid.map((row) => row.includes(true)).includes(true)) { this.stop(); alert(`No more living cells, stopping`); } }, this.state.speed); }; stop = () => { if (this.timer) clearInterval(this.timer); }; reset = () => { if (window.confirm('Are you sure?')) { this.stop(); this.setState(INITIAL_STATE); } }; toggleCell = (x: number, y: number) => { const newGrid = [...this.state.grid]; const newRow = [...newGrid[y]]; newRow[x] = !newGrid[y][x]; newGrid[y] = newRow; this.setState({ grid: newGrid }); }; toggleRunning = () => { if (this.state.running) this.stop(); if (!this.state.running) this.start(); this.setState({ running: !this.state.running }); }; updateSpeed = (speed: number) => { this.setState({ speed }); if (this.state.running) { this.stop(); this.start(); } }; updateCellSize = (f: (size: number) => number) => { this.setState({ cellSize: f(this.state.cellSize) }); }; updateDimensions = (dimensions: { width?: number; height?: number }) => { if (dimensions.width) { this.setState({ width: dimensions.width }); } if (dimensions.height) { this.setState({ height: dimensions.height }); } const grid = updateGridDimensions( { width: this.state.width, height: this.state.height }, dimensions, this.state.grid, ); this.setState({ grid }); }; download = () => { const state = JSON.stringify(this.state); const element = document.createElement('a'); const file = new Blob([state], { type: 'text/plain' }); element.href = URL.createObjectURL(file); element.download = 'export.json'; document.body.appendChild(element); element.click(); }; upload = () => { if ( window.confirm('This will replace any changes you have made. Continue?') ) { const element = document.createElement('input'); element.type = 'file'; element.click(); element.addEventListener('change', (e) => { const files = (e.currentTarget as HTMLInputElement).files; if (files && files.length > 0) { const file = files[0]; file.text().then((text) => { const newState = JSON.parse(text); this.setState(newState); }); } }); } }; render() { const { cellSize, generations, grid, height, running, speed, width, } = this.state; return (
{grid.map((row, y) => (
{row.map((cell, x) => ( ))}
))}
{generations} generation{generations === 1 ? '' : 's'}
); } } export default Board;