123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- 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 (
- <div className="container">
- <Controls
- running={running}
- speed={speed}
- width={width}
- height={height}
- toggle={this.toggleRunning}
- updateCellSize={this.updateCellSize}
- updateDimensions={this.updateDimensions}
- updateSpeed={this.updateSpeed}
- reset={this.reset}
- download={this.download}
- upload={this.upload}
- />
- <div className="board-container">
- <div className="board">
- {grid.map((row, y) => (
- <div className="row" key={y} style={{ height: cellSize }}>
- {row.map((cell, x) => (
- <Cell
- disabled={running}
- x={x}
- y={y}
- cellSize={cellSize}
- alive={cell}
- toggle={this.toggleCell}
- key={x}
- />
- ))}
- </div>
- ))}
- </div>
- </div>
- <div className="counter">
- <span>
- {generations} generation{generations === 1 ? '' : 's'}
- </span>
- </div>
- </div>
- );
- }
- }
-
- export default Board;
|