这行代码出了什么问题? (反应,生命游戏)

时间:2017-08-26 22:22:12

标签: javascript reactjs conways-game-of-life

有一个我不明白的错误。当我按<button>50x70</button>并按<button>run</button>时,我收到此错误消息。 我无法理解为什么,因为它适用于标准的50x50电路板。

为什么这行代码会导致错误? neighbors+=board[x+i][y+j].value https://codepen.io/anon/pen/OjoZKX

TypeError: Cannot read property '0' of undefined
evaluateGen
C:/Www/Projects/gameoflife/src/App.js:65
  62 | 
  63 | for(let i=-1; i <= 1; i++){
  64 |   for(let j=-1; j<= 1; j++){
> 65 |     neighbors+=board[x+i][y+j].value
  66 |   }
  67 | }
  68 | neighbors-=board[x][y].value
View compiled
(anonymous function)
C:/Www/Projects/gameoflife/src/App.js:108
  105 | 
  106 | run(){
  107 |   this.interval = setInterval(() => {
> 108 |       const nextState = evaluateGen(this.state.board)
  109 |       this.setState({paused: false, generation: this.state.generation + 1, board: nextState})
  110 |     }, 50)
  111 | }

&#13;
&#13;
// creates the board with random 1/0 value.
const createBoard = function(width, height) {
  let board = []
  for(var x = 0; x < width; x++){
    board.push([])
    for(var y = 0; y < height; y++){
      if(x === 0 || y === 0) {
        board[x].push({value: 0})
      } 
      else if (x === width-1 || y === height-1){
        board[x].push({value: 0})
      }
      else {
        let number = Math.round(Math.random())
        board[x].push({value: number})
      }
    }
  }
  return board
}

//  Game Of Life rules.
const evaluateCell = function(x, cell){
  let value = x
  if(x < 2){
    value = 0
  }
  else if(x === 2){
    value = cell
  }
  else if(x === 3){
    value = 1
  }
  else if(x > 3){
    value = 0
  }
  else {
    console.log('error: default case evaluateCell()')
    value = 0
  }
  return value
}

// evaluates a generation of board state by going through each cell and counting neighbors and calling evaluateCell accordingly.
const evaluateGen = function(board){
  let nextGen = JSON.parse(JSON.stringify(board));
  
  for(let y = 1; y < board.length - 1; y++){
    for(let x = 1; x < board[y].length - 1; x++){
      
      let neighbors = 0
      
      for(let i=-1; i <= 1; i++){
        for(let j=-1; j<= 1; j++){
          neighbors+=board[x+i][y+j].value
        }
      }
      // remove current cell from neighbors.
      neighbors-=board[x][y].value
      let nextvalue = evaluateCell(neighbors, board[x][y].value)
      nextGen[x][y].value = nextvalue
    }
  }

  return nextGen
}






class App extends React.Component {
  
  constructor(){
    super()
    this.state = {
      width: 50,
      height: 50,
      board: createBoard(50, 50),
      paused: false,
      generation: 0
    }
  }

  generateBoard(width, height) {
    this.pause()
    const board = createBoard(width, height)
    this.setState({board, generation: 0, width, height })
  }
  
  pause(){
    clearInterval(this.interval)
    this.setState({paused: true})
  }

  run(){
    this.interval = setInterval(() => {
        const nextState = evaluateGen(this.state.board)
        this.setState({paused: false, generation: this.state.generation + 1, board: nextState})
      }, 50)
  }

  componentDidMount() {
    this.run()
  }


  componentWillUnmount() {
    clearInterval(this.interval)
  }


  render() {
    
    let {board, generation, width, height} = this.state
    return (
      <div className="App">
        <h3>generation:{generation}</h3>
        {board.map(x => x.map(item => {
          return (
            <div className={`state-${item.value}`}></div>
          )
        }))}
        <button onClick={()=> this.run()}>run</button>
        <button onClick={()=> this.pause()}>pause </button>
        <button onClick={()=> this.generateBoard(width, height)}>generate</button>
        <button onClick={()=> this.generateBoard(50, 70)}>50 x 70</button>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
&#13;
.App div {
  width: 10px; height: 10px;
  outline: 1px solid black;
  
}
.App {
  display: flex;
  flex-wrap: wrap;
  width: 500px; height: 500px;
}

.state-1{
  background-color: red;
}
&#13;
<div id='root'></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

我认为当您尝试计算边缘上的切片的邻居时会出现问题。因为您正在尝试访问不在网格上的切片,所以循环中的某些变量将变为未定义。我建议添加一个if语句来检查这个。类似的东西:

if(x+i >= 0 && y+i >= 0){
    neighbors+=board[x+i][y+j].value
}

答案 1 :(得分:1)

你应该循环直到x和y小于board.length - 2,而不是board.length - 1,因为你正在努力确保它不会评估电路板的边缘。我认为你犯了一个相当常见的错误,就是忘记数组的长度会比你给出的数组的最大索引多一个。

例如,如果其中一行看起来像[4,6,2,3],则其长度为4,但如果您尝试访问行[4],则会得到未定义的行。如果你循环到外部循环中的长度为1,那么你的代码会做什么,然后在内部循环中为每个值添加+1(你这样做是因为你在-1和1之间从i / j循环然后将该值添加到外部索引中。)

另外我认为你已经在内部数组中反转了变量 - 你使用y作为行循环变量而使用x作为列循环外循环中的变量,但这些变量在内循环中是相反的。因此,如果它不是一个正方形,它最终将会出现一个不存在的行。

您的代码整体应如下所示:

const evaluateGen = function(board){
  let nextGen = JSON.parse(JSON.stringify(board));

  for(let x = 1; x < board.length - 2; x++){
    for(let y = 1; y < board[y].length - 2; y++){

      let neighbors = 0

      for(let i=-1; i <= 1; i++){
        for(let j=-1; j<= 1; j++){
          neighbors+=board[x+i][y+j].value
        }
      }
      // remove current cell from neighbors.
      neighbors-=board[x][y].value
      let nextvalue = evaluateCell(neighbors, board[x][y].value)
      nextGen[x][y].value = nextvalue
    }
  }

  return nextGen
}