// Initialize the game when the page loads document.addEventListener('DOMContentLoaded', () => { // Create game with 80x60 grid and 10px cells const game = new GameOfLife(80, 60, 10); }); class GameOfLife { constructor(width, height, cellSize = 10) { this.width = width; this.height = height; this.cellSize = cellSize; this.grid = this.createGrid(); this.nextGrid = this.createGrid(); this.running = false; this.generation = 0; this.births = 0; this.deaths = 0; this.animationId = null; this.speed = 10; // Canvas setup this.canvas = document.getElementById('gameCanvas'); this.ctx = this.canvas.getContext('2d'); this.canvas.width = width * cellSize; this.canvas.height = height * cellSize; // Event listeners this.setupEventListeners(); this.setupPresets(); // Initial draw this.draw(); this.updateStats(); } createGrid() { return Array(this.height).fill().map(() => Array(this.width).fill(0)); } randomize() { for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { this.grid[y][x] = Math.random() > 0.7 ? 1 : 0; } } this.generation = 0; this.draw(); } setupEventListeners() { // Start/Pause/Reset buttons document.getElementById('startBtn').addEventListener('click', () => this.start()); document.getElementById('pauseBtn').addEventListener('click', () => this.pause()); document.getElementById('resetBtn').addEventListener('click', () => this.reset()); document.getElementById('randomBtn').addEventListener('click', () => this.randomize()); // Speed control document.getElementById('speed').addEventListener('input', (e) => { this.speed = parseInt(e.target.value); }); // Canvas click to toggle cells this.canvas.addEventListener('click', (e) => { if (this.running) return; const rect = this.canvas.getBoundingClientRect(); const x = Math.floor((e.clientX - rect.left) / this.cellSize); const y = Math.floor((e.clientY - rect.top) / this.cellSize); if (x >= 0 && x < this.width && y >= 0 && y < this.height) { this.grid[y][x] = this.grid[y][x] ? 0 : 1; this.draw(); } }); } setupPresets() { document.querySelector('[data-preset="glider"]').addEventListener('click', () => { this.reset(); // Glider pattern this.grid[5][5] = 1; this.grid[6][6] = 1; this.grid[6][7] = 1; this.grid[5][7] = 1; this.grid[4][7] = 1; this.draw(); }); document.querySelector('[data-preset="spaceship"]').addEventListener('click', () => { this.reset(); // Lightweight spaceship this.grid[10][10] = 1; this.grid[10][13] = 1; this.grid[11][9] = 1; this.grid[11][13] = 1; this.grid[12][9] = 1; this.grid[12][10] = 1; this.grid[12][11] = 1; this.grid[12][12] = 1; this.draw(); }); document.querySelector('[data-preset="pulsar"]').addEventListener('click', () => { this.reset(); // Pulsar pattern const pulsarPoints = [ [2,4],[2,5],[2,6],[2,10],[2,11],[2,12], [4,2],[4,7],[4,9],[4,14], [5,2],[5,7],[5,9],[5,14], [6,2],[6,7],[6,9],[6,14], [7,4],[7,5],[7,6],[7,10],[7,11],[7,12], [9,4],[9,5],[9,6],[9,10],[9,11],[9,12], [10,2],[10,7],[10,9],[10,14], [11,2],[11,7],[11,9],[11,14], [12,2],[12,7],[12,9],[12,14], [14,4],[14,5],[14,6],[14,10],[14,11],[14,12] ]; pulsarPoints.forEach(([y, x]) => { this.grid[y][x] = 1; }); this.draw(); }); document.querySelector('[data-preset="gosper"]').addEventListener('click', () => { this.reset(); // Gosper glider gun const gunPoints = [ [5,1],[5,2],[6,1],[6,2], [5,11],[6,11],[7,11], [4,12],[8,12], [3,13],[9,13], [3,14],[9,14], [6,15], [4,16],[8,16], [5,17],[6,17],[7,17], [6,18], [3,21],[4,21],[5,21], [3,22],[4,22],[5,22], [2,23],[6,23], [1,25],[2,25],[6,25],[7,25], [3,35],[4,35],[3,36],[4,36] ]; gunPoints.forEach(([y, x]) => { this.grid[y][x] = 1; }); this.draw(); }); } countNeighbors(x, y) { let count = 0; for (let dy = -1; dy <= 1; dy++) { for (let dx = -1; dx <= 1; dx++) { if (dx === 0 && dy === 0) continue; const nx = (x + dx + this.width) % this.width; const ny = (y + dy + this.height) % this.height; count += this.grid[ny][nx]; } } return count; } update() { // Reset counters this.births = 0; this.deaths = 0; // Compute next generation for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { const neighbors = this.countNeighbors(x, y); const cell = this.grid[y][x]; let nextState; // Apply Conway's Game of Life rules if (cell === 1) { nextState = (neighbors === 2 || neighbors === 3) ? 1 : 0; if (nextState === 0) this.deaths++; } else { nextState = (neighbors === 3) ? 1 : 0; if (nextState === 1) this.births++; } this.nextGrid[y][x] = nextState; } } // Swap grids [this.grid, this.nextGrid] = [this.nextGrid, this.grid]; this.generation++; // Update stats display this.updateStats(); } draw() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Draw live cells with gradient const gradient = this.ctx.createLinearGradient(0, 0, this.canvas.width, this.canvas.height); gradient.addColorStop(0, '#4facfe'); gradient.addColorStop(1, '#00f2fe'); this.ctx.fillStyle = gradient; for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { if (this.grid[y][x] === 1) { this.ctx.fillRect( x * this.cellSize, y * this.cellSize, this.cellSize - 1, this.cellSize - 1 ); } } } // Draw grid lines this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)'; this.ctx.lineWidth = 0.5; // Vertical lines for (let x = 0; x <= this.width; x++) { this.ctx.beginPath(); this.ctx.moveTo(x * this.cellSize, 0); this.ctx.lineTo(x * this.cellSize, this.canvas.height); this.ctx.stroke(); } // Horizontal lines for (let y = 0; y <= this.height; y++) { this.ctx.beginPath(); this.ctx.moveTo(0, y * this.cellSize); this.ctx.lineTo(this.canvas.width, y * this.cellSize); this.ctx.stroke(); } } updateStats() { document.getElementById('generation').textContent = this.generation; document.getElementById('population').textContent = this.getPopulation(); document.getElementById('births').textContent = this.births; document.getElementById('deaths').textContent = this.deaths; } getPopulation() { let count = 0; for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { count += this.grid[y][x]; } } return count; } gameLoop() { this.update(); this.draw(); // Adjust speed (higher value = faster) const speedFactor = (21 - this.speed) * 20; setTimeout(() => { if (this.running) { this.animationId = requestAnimationFrame(() => this.gameLoop()); } }, speedFactor); } start() { if (!this.running) { this.running = true; this.gameLoop(); } } pause() { this.running = false; if (this.animationId) { cancelAnimationFrame(this.animationId); this.animationId = null; } } reset() { this.pause(); this.grid = this.createGrid(); this.nextGrid = this.createGrid(); this.generation = 0; this.births = 0; this.deaths = 0; this.updateStats(); this.draw(); } }