I'm trying to implement Conway's Game of Life and it's not working right, but I can't find where the problem is. Currently I'm using setInterval()
to get a new generation of the board each time, but I tried some error checking using setTimeout()
, then checking the before and after tables using console.table
, and couldn't figure it out. My checkNeighbors function works correctly (i.e., identifies birth to death and death to birth) for some of the cells but not all of them.
Can anyone help me figure out where the problem is?
(I get the new board from the newBoard function, which I call in my mapDispatchToProps function.)
Here's my code and a link to a Codepen: https://codepen.io/lieberscott/pen/qxrVbE?editors=0110
// React & Redux libraries setup
const { Component } = React;
const { createStore, applyMiddleware } = Redux;
const { Provider } = ReactRedux;
const { connect } = ReactRedux;
const { combineReducers } = Redux;
let grid = [];
let width = 25;
let height = 50;
for (let i = 0; i < height; i++) {
grid.push([]);
for (let j = 0; j < width; j++) {
grid[i][j] = Math.random() > 0.85;
}
}
const initState = {
arr: grid
};
const reducer = (state = initState, action) => {
let newState;
// console.table(state.arr);
switch (action.type) {
case "NEW_ARR":
newState = {
arr: action.arr
};
// console.table(newState.arr);
return newState;
break;
default:
return state;
break;
}
}
const store = createStore(reducer);
const newBoard = (arr, newArr) => {
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
let score = checkNeighbors(arr, i, j);
if (arr[i][j] == false && score == 3) {
newArr[i][j] = true;
}
else if (arr[i][j] == true && (score > 3 || score < 2)) {
newArr[i][j] = false;
}
else { // unessential line since already set newArr = arr
newArr[i][j] = arr[i][j];
}
}
}
return newArr;
}
function checkNeighbors(array, x, y) {
let score = 0;
for (let i = -1; i <= 1; i++) {
let h = (i + x + height) % height;
for (let j = -1; j <= 1; j++) {
let w = (j + y + width) % width;
score += array[h][w];
}
}
score -= array[x][y];
return score;
}
const Cell = (props) => {
return (
<td className={props.black ? "black" : ""}>
</td>
);
}
class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
setInterval(() => this.props.newGeneration(this.props.data.arr), 90)
}
render() {
return (
<table onClick={this.toggle}>
<thead>
</thead>
<tbody>
{this.props.data.arr.map((row, i) =>
<tr key={i}> {row.map((cell, j) =>
<Cell key={j} black={cell}/>)}
</tr> )}
</tbody>
</table>
);
}
}
const mapStateToProps = (state) => {
return {
data: state
};
};
const mapDispatchToProps = (dispatch) => {
return {
newGeneration: (array) => {
let arr1 = array.slice(0);
let arr2 = array.slice(0);
let newArr = newBoard(arr1, arr2);
dispatch(newGeneration(newArr));
}
}
}
function newGeneration(newArr) {
return {
type: "NEW_ARR",
arr: newArr
};
}
App = connect(mapStateToProps, mapDispatchToProps)(App); // use same name as class apparently?
const main = window.document.getElementById("main");
// Provider wraps our app
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
main);
I sort of managed to figure it out. In my newBoard function, the values in the original array (arr) were being changed (even though in my code I was ostensibly only changing the newArr values).
So instead I changed the newBoard function, creating a new array WITHIN the function, instead of passing one in. This worked:
https://codepen.io/lieberscott/pen/EQWGXW?editors=0110
const newBoard = (arr) => {
let newArr = [];
for (let i = 0; i < height; i++) {
newArr.push([]);
for (let j = 0; j < width; j++) {
let score = checkNeighbors(arr, i, j);
if (arr[i][j] == false && score == 3) {
newArr[i].push(true);
}
else if (arr[i][j] == true && (score > 3 || score < 2)) {
newArr[i].push(false);
}
else {
newArr[i].push(arr[i][j]);
}
}
}
return newArr;
}
Any comments on why the original behavior was taking place, though, I think would be valuable.