{
  "name": "dot-matrix-life-default",
  "type": "registry:ui",
  "tier": "free",
  "dependencies": [],
  "registryDependencies": [
    "https://www.craftui.space/r/craftui-base.json"
  ],
  "files": [
    {
      "path": "components/craftui/loaders/dot-matrix-life/default.tsx",
      "content": "'use client';\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\ninterface DotMatrixLifeProps {\n  width?: number;\n  height?: number;\n  color?: string;\n  dotSize?: number;\n  isAnimating?: boolean;\n  duration?: number;\n  gridSize?: number;\n}\n\nconst BASE_OPACITY = 0.06;\nconst ALIVE_OPACITY = 0.95;\n\ntype Grid = boolean[][];\n\nfunction nextGeneration(grid: Grid, n: number): Grid {\n  return Array.from({ length: n }, (_, r) =>\n    Array.from({ length: n }, (_, c) => {\n      let count = 0;\n      for (let dr = -1; dr <= 1; dr++) {\n        for (let dc = -1; dc <= 1; dc++) {\n          if (dr === 0 && dc === 0) continue;\n          // Toroidal wrapping\n          if (grid[(r + dr + n) % n]![(c + dc + n) % n]) count++;\n        }\n      }\n      if (grid[r]![c]) return count === 2 || count === 3;\n      return count === 3;\n    })\n  );\n}\n\nfunction gridKey(grid: Grid): string {\n  return grid.map(r => r.map(c => c ? '1' : '0').join('')).join('|');\n}\n\nfunction makeInitialGrid(n: number): Grid {\n  const g: Grid = Array.from({ length: n }, () => Array(n).fill(false));\n  const mid = Math.floor(n / 2);\n  // Horizontal blinker in center row — reliable period-2 oscillator\n  g[mid]![Math.max(0, mid - 1)] = true;\n  g[mid]![mid] = true;\n  g[mid]![Math.min(n - 1, mid + 1)] = true;\n  // Add a second blinker offset to create more visual activity\n  if (n >= 4) {\n    const r2 = Math.floor(n / 4);\n    const c2 = Math.floor((3 * n) / 4);\n    g[r2]![Math.max(0, c2 - 1)] = true;\n    g[r2]![c2] = true;\n    g[r2]![Math.min(n - 1, c2 + 1)] = true;\n  }\n  // A third blinker for grids large enough\n  if (n >= 5) {\n    const r3 = Math.floor((3 * n) / 4);\n    const c3 = Math.floor(n / 4);\n    g[Math.max(0, r3 - 1)]![c3] = true;\n    g[r3]![c3] = true;\n    g[Math.min(n - 1, r3 + 1)]![c3] = true;\n  }\n  return g;\n}\n\nexport function DotMatrixLife({\n  width = 100,\n  height = 100,\n  color = 'currentColor',\n  dotSize = 12,\n  isAnimating = true,\n  duration = 0.5,\n  gridSize = 5,\n}: DotMatrixLifeProps) {\n  const n = Math.max(3, Math.min(6, gridSize));\n  const stride = 100 / (n + 1);\n  const r = Math.min(dotSize, stride * 0.82) / 2;\n\n  const initial = useMemo(() => makeInitialGrid(n), [n]);\n  const [grid, setGrid] = useState<Grid>(initial);\n  const historyRef = useRef<Set<string>>(new Set([gridKey(initial)]));\n\n  // Reset when n changes\n  useEffect(() => {\n    const g = makeInitialGrid(n);\n    setGrid(g);\n    historyRef.current = new Set([gridKey(g)]);\n  }, [n]);\n\n  const stepMs = Math.max(80, duration * 1000);\n\n  useEffect(() => {\n    if (!isAnimating) return;\n    const id = setInterval(() => {\n      setGrid(prev => {\n        const next = nextGeneration(prev, n);\n        const key = gridKey(next);\n        // Dead grid or revisited state → reset\n        const allDead = next.every(row => row.every(c => !c));\n        if (allDead || historyRef.current.has(key)) {\n          const fresh = makeInitialGrid(n);\n          historyRef.current = new Set([gridKey(fresh)]);\n          return fresh;\n        }\n        // Keep only last 16 states to avoid unbounded memory\n        if (historyRef.current.size > 16) {\n          const arr = Array.from(historyRef.current);\n          historyRef.current = new Set(arr.slice(-8));\n        }\n        historyRef.current.add(key);\n        return next;\n      });\n    }, stepMs);\n    return () => clearInterval(id);\n  }, [isAnimating, stepMs, n]);\n\n  const opacities = useMemo(\n    () => grid.flatMap(row => row.map(cell => (cell ? ALIVE_OPACITY : BASE_OPACITY))),\n    [grid]\n  );\n\n  return (\n    <svg\n      width={width}\n      height={height}\n      viewBox=\"0 0 100 100\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-label=\"Loading\"\n      role=\"img\"\n    >\n      {Array.from({ length: n * n }, (_, i) => (\n        <circle\n          key={i}\n          cx={stride + (i % n) * stride}\n          cy={stride + Math.floor(i / n) * stride}\n          r={r}\n          fill={color}\n          opacity={opacities[i]}\n          style={{ transition: 'opacity 120ms ease' }}\n        />\n      ))}\n    </svg>\n  );\n}\n",
      "type": "registry:ui",
      "target": "~/components/craftui/loaders/dot-matrix-life/default.tsx"
    }
  ],
  "meta": {
    "displayName": "Dot Matrix Life - Dot Matrix Life",
    "description": "Each generation, every dot lives or dies based on its 8 neighbours following Conway's rules. Seeded with multiple blinker oscillators that interact across the toroidal grid.",
    "category": "loaders",
    "tags": [
      "dots",
      "matrix",
      "life",
      "cellular",
      "automaton",
      "organic",
      "emergent",
      "conway"
    ],
    "animationType": "loop"
  }
}