Conway's Game of Life as a React web app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Board.tsx 3.2KB

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