---
name: "conways-game-of-life"
description: "Interactive cellular automaton simulator with HTML5 Canvas. Click to toggle cells, watch patterns evolve with colorful generation-based hue shifting."
metadata:
  version: "1.0.0"
disable-model-invocation: true
---

# Conway's Game of Life

An interactive implementation of Conway's Game of Life using HTML5 Canvas.

## What it does

This skill generates a fully interactive cellular automaton simulator:

- **Click** cells to toggle them alive/dead
- **Start/Pause** the simulation with the play button
- **Step** through one generation at a time
- **Random** seed fills the grid with a random pattern
- **Clear** resets the grid
- Configurable grid size and simulation speed
- Wrapping edges (toroidal grid)

## Rules

1. Any live cell with 2 or 3 neighbors survives
2. Any dead cell with exactly 3 neighbors becomes alive
3. All other cells die or stay dead

## Usage

```
Provide this skill to an AI agent to generate an interactive Game of Life.
The agent should create the HTML file and open it in a browser.
```


---

## Files

### `game-of-life.html`

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Conway's Game of Life</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    background: #0a0a0a;
    color: #e0e0e0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    min-height: 100vh;
    padding: 20px;
  }
  h1 {
    font-size: 1.5rem;
    margin-bottom: 12px;
    background: linear-gradient(135deg, #a855f7, #ec4899);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
  }
  .controls {
    display: flex;
    gap: 8px;
    margin-bottom: 12px;
    flex-wrap: wrap;
    justify-content: center;
  }
  button {
    padding: 6px 14px;
    border: 1px solid #333;
    border-radius: 6px;
    background: #1a1a2e;
    color: #e0e0e0;
    cursor: pointer;
    font-size: 0.85rem;
    transition: all 0.15s;
  }
  button:hover { border-color: #a855f7; background: #1a1a3e; }
  button.active { background: #a855f7; border-color: #a855f7; color: white; }
  .info {
    font-size: 0.8rem;
    color: #888;
    margin-bottom: 8px;
  }
  canvas {
    border: 1px solid #222;
    border-radius: 4px;
    cursor: crosshair;
  }
</style>
</head>
<body>
<h1>Conway's Game of Life</h1>
<div class="controls">
  <button id="playBtn">Play</button>
  <button id="stepBtn">Step</button>
  <button id="randomBtn">Random</button>
  <button id="clearBtn">Clear</button>
</div>
<div class="info" id="info">Generation: 0 | Alive: 0</div>
<canvas id="canvas"></canvas>
<script>
const COLS = 60, ROWS = 40, CELL = 12;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = COLS * CELL;
canvas.height = ROWS * CELL;

let grid = Array.from({ length: ROWS }, () => new Uint8Array(COLS));
let running = false;
let generation = 0;
let intervalId = null;

function countNeighbors(r, c) {
  let count = 0;
  for (let dr = -1; dr <= 1; dr++) {
    for (let dc = -1; dc <= 1; dc++) {
      if (dr === 0 && dc === 0) continue;
      const nr = (r + dr + ROWS) % ROWS;
      const nc = (c + dc + COLS) % COLS;
      count += grid[nr][nc];
    }
  }
  return count;
}

function step() {
  const next = Array.from({ length: ROWS }, () => new Uint8Array(COLS));
  for (let r = 0; r < ROWS; r++) {
    for (let c = 0; c < COLS; c++) {
      const n = countNeighbors(r, c);
      if (grid[r][c]) {
        next[r][c] = (n === 2 || n === 3) ? 1 : 0;
      } else {
        next[r][c] = (n === 3) ? 1 : 0;
      }
    }
  }
  grid = next;
  generation++;
}

function draw() {
  ctx.fillStyle = '#0a0a0a';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  let alive = 0;
  for (let r = 0; r < ROWS; r++) {
    for (let c = 0; c < COLS; c++) {
      if (grid[r][c]) {
        alive++;
        const hue = (generation * 2 + r * 3 + c * 3) % 360;
        ctx.fillStyle = `hsl(${hue}, 70%, 55%)`;
        ctx.fillRect(c * CELL + 1, r * CELL + 1, CELL - 2, CELL - 2);
      }
    }
  }
  // Grid lines
  ctx.strokeStyle = '#1a1a1a';
  ctx.lineWidth = 0.5;
  for (let r = 0; r <= ROWS; r++) {
    ctx.beginPath();
    ctx.moveTo(0, r * CELL);
    ctx.lineTo(canvas.width, r * CELL);
    ctx.stroke();
  }
  for (let c = 0; c <= COLS; c++) {
    ctx.beginPath();
    ctx.moveTo(c * CELL, 0);
    ctx.lineTo(c * CELL, canvas.height);
    ctx.stroke();
  }
  document.getElementById('info').textContent = `Generation: ${generation} | Alive: ${alive}`;
}

function randomize() {
  for (let r = 0; r < ROWS; r++)
    for (let c = 0; c < COLS; c++)
      grid[r][c] = Math.random() < 0.3 ? 1 : 0;
  generation = 0;
  draw();
}

function clear() {
  grid = Array.from({ length: ROWS }, () => new Uint8Array(COLS));
  generation = 0;
  draw();
}

function togglePlay() {
  running = !running;
  document.getElementById('playBtn').textContent = running ? 'Pause' : 'Play';
  document.getElementById('playBtn').classList.toggle('active', running);
  if (running) {
    intervalId = setInterval(() => { step(); draw(); }, 120);
  } else {
    clearInterval(intervalId);
  }
}

canvas.addEventListener('click', (e) => {
  const rect = canvas.getBoundingClientRect();
  const c = Math.floor((e.clientX - rect.left) / CELL);
  const r = Math.floor((e.clientY - rect.top) / CELL);
  if (r >= 0 && r < ROWS && c >= 0 && c < COLS) {
    grid[r][c] = grid[r][c] ? 0 : 1;
    draw();
  }
});

document.getElementById('playBtn').addEventListener('click', togglePlay);
document.getElementById('stepBtn').addEventListener('click', () => { step(); draw(); });
document.getElementById('randomBtn').addEventListener('click', randomize);
document.getElementById('clearBtn').addEventListener('click', clear);

// Start with a random pattern
randomize();
// Auto-play after 500ms so sandbox captures animation
setTimeout(() => { if (!running) togglePlay(); }, 500);
</script>
</body>
</html>
```
