function countNeighbors(grid, x, y) {
  // Define the offsets for the neighboring cells
  const neighborOffsets = [
      [-1, -1], [-1, 0], [-1, 1],
      [0, -1],           [0, 1],
      [1, -1],  [1, 0],  [1, 1]
  ];

  // Use reduce to iterate over the neighborOffsets and count the number of live neighbors
  return neighborOffsets.reduce((count, [dx, dy]) => {
      // Calculate the coordinates of the neighboring cell
      const nx = x + dx;
      const ny = y + dy;

      // Check if the neighboring cell is within the grid boundaries and if it is alive
      if (nx >= 0 && ny >= 0 && nx < grid.length && ny < grid[0].length && grid[nx][ny]) {
          // If the neighboring cell is alive, increment the count
          return count + 1;
      }

      // If the neighboring cell is not alive or is outside the grid boundaries, return the current count
      return count;
  }, 0);
}

function step(grid) {
  // Use map to iterate over each cell in the grid and calculate the next state based on the rules of the game
  return grid.map((row, x) =>
      row.map((cell, y) => {
          // Count the number of live neighbors for the current cell
          const neighbors = countNeighbors(grid, x, y);

          // Apply the rules of the game to determine the next state of the cell
          // If the cell has 3 live neighbors or has 2 live neighbors and is already alive, it stays alive
          // Otherwise, it becomes dead
          return neighbors === 3 || (neighbors === 2 && cell) ? 1 : 0;
      })
  );
}

function printGrid(grid) {
  grid.forEach(row => console.log(row.map(cell => cell ? '🟢' : '⚪️').join('')));
  console.log('\n');
}

function simulate(initial, steps) {
  let grid = initial;
  Array.from({ length: steps }).forEach(() => {
      printGrid(grid);
      grid = step(grid);
  });
}


const initialBoard = [
  [0, 1, 0],
  [0, 0, 1],
  [1, 1, 1],
  [0, 0, 0],
];

simulate(initialBoard, 10);

const rpentomino = [
  [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
  [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];

// simulate(rpentomino, 10);

// big glider
const bigGlider = [
  [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
  [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];

// simulate(bigGlider, 10);

const randomBoard = Array.from({ length: 22 }, () =>
  Array.from({ length: 22 }, () => Math.round(Math.random()))
);

simulate(randomBoard, 50);