React Game of Life

In this example below you will see how to do a React Game of Life with some HTML / CSS and Javascript

Thumbnail
This awesome code was written by Cory2911, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright Cory2911 ©
  • HTML
  • CSS
  • JavaScript
    h1
   a(href='https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life' target="_blank") React Game of Life (Click to Learn More)

div(class="boardContainer")
   #reactContainer

/*Downloaded from https://www.codeseek.co/Cory2911/react-game-of-life-wWjgWP */
    /*Colors & Fonts*/
$headerFontColor: #b1c1cb
$headerBackgroundColor: #333333
$mainBackgroundGradientColor: linear-gradient(to right, #1d1414, #792a2a, #1d1414)
$buttonFontSize: 130%

/*Element Widths*/
$reactContainerWidth: 90%
$boardWidth: 70%
$sideMenuWidth: 15%
$cellWidth: 4%
$buttonWidth: 70%


* 
  box-sizing: border-box

body 
  margin: 0
  padding: 0

h1, h1 a
  background-color: $headerBackgroundColor
  color: $headerFontColor
  text-align: center
  margin: 0

h2
  text-align: center
  margin-top: 15px
  
.boardContainer 
  background: $mainBackgroundGradientColor
  height: 100vh
  padding-top: 7%


.menuOptions 
  background-color: $headerBackgroundColor
  display: block
  width: $boardWidth
  margin: 0% auto
  padding: 1%
  

  span
    @extend button
    display: block
    float: right
    margin-right: 5%
    padding-top: 1%
    
button
  font-size: $buttonFontSize
  color: $headerFontColor
  background-color: $headerBackgroundColor
  margin: 0% 2%
  padding: 2%
  
#reactContainer 
  width: $reactContainerWidth
  display: block
  margin-left: 5%
  min-width: 800px

.gameCellRows 
  float: left
  width: 70%
  height: 60vh

  //FlexBox Styles  
  display: flex
  flex-direction: column
  flex-wrap: nowrap
  justify-content: center
  align-items: stretch
  align-content: stretch

.gameCellRow
  
  //FlexBox Styles
  flex: 1 1

  display: flex
  flex-direction: row
  flex-wrap: nowrap
  justify-content: center
  align-items: stretch
  align-content: stretch

.gameCell
  box-sizing: border-box
  display: block
  float: left
  border: 1px solid #333333

  //FlexBox Styles
  flex: 1 1

.gameCell.isalivefalse
  background-color: black

.gameCell.isalivetrue 
  background-color: red
    
.sideMenu
  background-color: $headerBackgroundColor
  color: $headerFontColor
  float: left
  width: $sideMenuWidth
  padding: 0% 0% 2% 0%
  display: block
  height: 60vh
  
  button
    display: block
    margin: 20px auto
    width: $buttonWidth
    padding: 7px






/*Downloaded from https://www.codeseek.co/Cory2911/react-game-of-life-wWjgWP */
    //Begin React Component Definitions
const GameBoard = React.createClass({
   getInitialState: function() {
      return {
         generation: 0,
         speed: 200,
         cellsWide: 35,
         board: this.getInitialBoard(),
      }
   },



   changeSpeedToSlow: function() {
      this.activateConfigurations(500, this.state.cellsWide);
   },

   changeSpeedToNormal: function() {
      this.activateConfigurations(200, this.state.cellsWide);
   },

   changeSpeedToFast: function() {
      this.activateConfigurations(75, this.state.cellsWide);
   },

   changeSizeToSmall: function() {
      this.setState({
         generation: 0,
         cellsWide: 35,
         board: this.getInitialBoard(35, 35)
      })
      this.activateConfigurations(this.state.speed, 35);
   },

   changeSizeToMedium: function() {
      this.setState({
         generation: 0,
         cellsWide: 50,
         board: this.getInitialBoard(50, 50)
      })
      this.activateConfigurations(this.state.speed, 50);
   },

   changeSizeToLarge: function() {
      this.setState({
         generation: 0,
         cellsWide: 75,
         board: this.getInitialBoard(75, 75)
      })
      this.activateConfigurations(this.state.speed, 75);
   },

   activateConfigurations: function activateConfiguration(timerSpeed = this.state.speed, cellsWide) {
      clearInterval(this.state.timerId);
      
      var timerId = setInterval(function() {
         if (!this.state.paused){
         this.setState({
            generation: this.state.generation + 1,
            board: this.getNextBoard(),
            timerId: timerId,
         })
         }
      }.bind(this), timerSpeed)

   },
   
   pauseGame: function pauseGame(){
      if (this.state.generation != 0){
      this.setState({
         paused: !this.state.paused,
      })
      }
   },
   
   playGame: function playGame(){
     var cellsWide = this.state.cellsWide,
         generation = this.state.generation,
         board = generation === 0 ? this.getInitialBoard(cellsWide, cellsWide) : this.state.board;
      
     this.setState({
        paused: false,
        board: board
     }) 
   },
   
   clearBoard: function clearBoard(){
     var cellsWide = this.state.cellsWide,
         newBoard = [];      
    
      for (let newRow = 0; newRow < cellsWide; newRow++){
         newBoard.push([])
         for (let newCell = 0; newCell < cellsWide; newCell++){
            newBoard[newRow][newCell] = false ;           
         }         
      }
      
      this.setState({
         generation: 0,
         board: newBoard,  
         paused: true,
      })
      
   },

   render: function() {
      return (
         <div>
            <MenuOptions {...this.state} 
               playGame={this.playGame}
               pauseGame={this.pauseGame}
               clearBoard={this.clearBoard}
               />
            <LeftSideMenu {...this.state} 
               getCellSize={this.getCellSize}
               changeSizeToSmall={this.changeSizeToSmall}
               changeSizeToMedium={this.changeSizeToMedium}
               changeSizeToLarge={this.changeSizeToLarge}
               />
            
            <GameRows {...this.state} 
               />
            
            <RightSideMenu {...this.state} 
               changeSpeedToSlow={this.changeSpeedToSlow}
               changeSpeedToNormal={this.changeSpeedToNormal}
               changeSpeedToFast={this.changeSpeedToFast} />     
         </div>
      )
   },

   getInitialBoard: function getInitialBoard(desiredWidth = 35, desiredHeight = 35) {
      const initialBoard = [];

      for (let heightCount = 0; heightCount < desiredHeight; heightCount++) {
         initialBoard.push([]);

         for (let widthCount = 0; widthCount < desiredWidth; widthCount++) {
            initialBoard[heightCount].push(Boolean(Math.round(Math.random())))
         }
      }
      return initialBoard;
   },

   getNextBoard: function getNextBoard() {
      const currentBoard = this.state.board,
         nextBoard = this.cloneGameBoard(currentBoard)
      let numberOfRows = currentBoard.length - 1,
         numberOfRowCells = currentBoard[0].length - 1;

      for (let rowIndex = 0; rowIndex < numberOfRows; rowIndex++) {
         for (let cellIndex = 0; cellIndex < numberOfRowCells; cellIndex++) {
            nextBoard[rowIndex][cellIndex] = this.isAliveInNextGen(cellIndex, rowIndex, currentBoard)            
         }
      }
         return nextBoard      

   },

   cloneGameBoard: function cloneGameBoard(boardArray) {
            var newArray = [],
                len = boardArray.length;
      
            for (var i = 0; i < len; i++) {
               if (boardArray[i] instanceof Array) {
                  newArray[i] = cloneGameBoard(boardArray[i]);
               } else {
                  newArray[i] = boardArray[i];
               }
            }
            return newArray;
   },

   isAliveInNextGen: function isAliveInNextGen(cellIndex, rowIndex, board) {
      const liveCellCount = this.countLiveNeighbors(cellIndex, rowIndex, board);

      if (board[rowIndex][cellIndex] === false) {
         if (liveCellCount !== 3) {
            return false
         } else {
            return true
         }
      } else if (board[rowIndex][cellIndex] === true) {
         if (liveCellCount < 3) {
            return false
         } else if (liveCellCount > 4) {
            return false
         } else if (liveCellCount === 3) {
            return true
         } else if (liveCellCount === 4) {
            return true
         }
      }

   },

   countLiveNeighbors: function countLiveNeighbors(cellIndex, rowIndex, board) {
      let aliveCount = 0;

      for (let row = rowIndex - 1; row <= rowIndex + 1; row++) {
         if (board[row] === undefined) {
            continue;
         } else {
            for (let cell = cellIndex - 1; cell <= cellIndex + 1; cell++) {
               if (board[row][cell] === undefined) {
                  continue;
               } else if (board[row][cell] === true) {
                  aliveCount = aliveCount + 1;
               }
            }
         }
      }
      return aliveCount;
   },

   componentDidMount: function() {
      var timerId = setInterval(function GameBoardDidMount() {
      if (!this.state.paused){   
      var nextBoard = this.getNextBoard(),
          generation = this.state.generation + 1;
         this.setState({
            generation: generation,
            board: nextBoard,
            timerId: timerId
         })
          }
      }.bind(this), this.state.speed)
     

   },

})

const MenuOptions = React.createClass({
   render: function() {
      return (
         <div className="menuOptions">
          <button onClick={this.props.playGame}>Play</button>
          <button onClick={this.props.pauseGame}>Pause</button>
          <button onClick={this.props.clearBoard}>Reset</button>
            <span><strong>Generation: </strong>{this.props.generation}</span> 
        </div>
      )
   }

})

const LeftSideMenu = React.createClass({
   render: function() {
      return (
         <div className="sideMenu leftSideMenu">
             <h2>Board Size</h2>
             <button onClick={this.props.changeSizeToLarge}>Large</button>
             <button onClick={this.props.changeSizeToMedium}>Medium</button>
             <button onClick={this.props.changeSizeToSmall}>Small</button>    
         </div>
      )
   },
})

const RightSideMenu = React.createClass({
   render: function() {
      return (
         <div className="sideMenu rightSideMenu">
         <h2>Sim Speed</h2>
         <button onClick={this.props.changeSpeedToFast}>Fast</button>
         <button onClick={this.props.changeSpeedToNormal}>Normal</button>
         <button onClick={this.props.changeSpeedToSlow}>Slow</button>
      </div>
      )

   }

})

const GameRows = React.createClass({
   render: function() {
      return (
         <div
            className='gameCellRows'
         >
            {this.props.board.map((row, rowIndex) => <GameCellRow key={rowIndex} row={row} {...this.props} /> )}
         </div>
      )
   }

})

const GameCellRow = React.createClass({

   render: function() {
      const cellRow = this.props.row;

      return (
         <div
            className='gameCellRow'  
            >
            {cellRow.map((cell, cellIndex) => <GameCell key={cellIndex} cell={cell} {...this.props}/>)}
         </div>
      )

   }
})

const GameCell = React.createClass({

   render: function() {
      return ( <
         div 
         className = {
            'gameCell isalive' + this.props.cell
         } >
            < /div>
      )

   }

})

ReactDOM.render(<GameBoard />, document.getElementById('reactContainer'));

Comments