about summary refs log blame commit diff stats
path: root/js/life/life.js
blob: 38d09727e5256626235fb543fb31d717be0f5169 (plain) (tree)






















































































                                                                                                                          
'use strict'

const b = {
    curry: function (fn) {
        const curried = (...args) => {
            if (args.length >= fn.length)
                return fn(...args)
            else
                return (...rest) => curried(...args, ...rest)
        }
        return curried
    },
    pipe: (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value),
};

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startPauseButton = document.getElementById('startPauseButton');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const cellSize = 10;
const rows = Math.floor(canvas.height / cellSize);
const cols = Math.floor(canvas.width / cellSize);

let grid = initializeGrid();
let animationId = null;

function initializeGrid() {
  return Array.from({ length: rows }, () => Array.from({ length: cols }, () => Math.random() > 0.8 ? 1 : 0));
}

const drawCell = b.curry((ctx, cellSize, x, y, cell) => {
  ctx.fillStyle = cell ? 'black' : 'white';
  ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
  ctx.strokeStyle = 'gray';
  ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize);
});

const drawGrid = (ctx, cellSize, grid) => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  grid.forEach((row, y) => row.forEach((cell, x) => drawCell(ctx, cellSize, x, y, cell)));
};

const getNeighbors = (grid, x, y) => [
  grid[y - 1]?.[x - 1], grid[y - 1]?.[x], grid[y - 1]?.[x + 1],
  grid[y]?.[x - 1], /* cell */ grid[y]?.[x + 1],
  grid[y + 1]?.[x - 1], grid[y + 1]?.[x], grid[y + 1]?.[x + 1]
].filter(Boolean).reduce((acc, val) => acc + val, 0);

const nextCellState = b.curry((cell, neighbors) => (cell && neighbors === 2) || neighbors === 3 ? 1 : 0);

const nextGridState = (grid) => grid.map((row, y) => row.map((cell, x) => nextCellState(cell, getNeighbors(grid, x, y))));

const animate = () => {
  drawGrid(ctx, cellSize, grid);
  grid = nextGridState(grid);
  animationId = requestAnimationFrame(animate);
};

const toggleAnimation = () => {
  if (animationId) {
    cancelAnimationFrame(animationId);
    animationId = null;
    startPauseButton.textContent = 'Start';
  } else {
    animate();
    startPauseButton.textContent = 'Pause';
  }
};

startPauseButton.addEventListener('click', toggleAnimation);

canvas.addEventListener('click', (event) => {
  const rect = canvas.getBoundingClientRect();
  const x = Math.floor((event.clientX - rect.left) / cellSize);
  const y = Math.floor((event.clientY - rect.top) / cellSize);
  grid[y][x] = grid[y][x] ? 0 : 1;
  drawGrid(ctx, cellSize, grid);
});

const startGame = b.pipe(initializeGrid, (grid) => {
  drawGrid(ctx, cellSize, grid);
  return grid;
});
grid = startGame();