|
4 | 4 | (function() { |
5 | 5 | const FPS = 30; |
6 | 6 | const FRAME_TIME = 1000 / FPS; |
7 | | - |
8 | | - // ========== GAME OF LIFE ========== |
9 | | - const lifeCanvas = document.getElementById('life-canvas'); |
10 | | - const lifeCtx = lifeCanvas.getContext('2d'); |
11 | | - const lifeStartBtn = document.getElementById('life-start'); |
12 | | - const lifeResetBtn = document.getElementById('life-reset'); |
13 | | - |
14 | 7 | const LIFE_GRID_SIZE = 80; |
| 8 | + |
| 9 | + let lifeCanvas, lifeCtx, lifeStartBtn, lifeResetBtn; |
15 | 10 | let lifeGrid, lifeNext, lifeRunning, lifeAnimId, lifeLastTime; |
16 | 11 |
|
17 | 12 | function initLife() { |
18 | 13 | lifeCanvas.width = lifeCanvas.offsetWidth; |
19 | 14 | lifeCanvas.height = lifeCanvas.offsetHeight; |
20 | 15 | lifeGrid = new Uint8Array(LIFE_GRID_SIZE * LIFE_GRID_SIZE); |
21 | 16 | lifeNext = new Uint8Array(LIFE_GRID_SIZE * LIFE_GRID_SIZE); |
22 | | - // Random seed |
23 | 17 | for (let i = 0; i < lifeGrid.length; i++) { |
24 | 18 | lifeGrid[i] = Math.random() < 0.3 ? 1 : 0; |
25 | 19 | } |
|
47 | 41 | const idx = y * LIFE_GRID_SIZE + x; |
48 | 42 | const neighbors = countNeighbors(x, y); |
49 | 43 | if (lifeGrid[idx] === 1) { |
50 | | - // Alive: survive with 2-3 neighbors |
51 | 44 | lifeNext[idx] = (neighbors === 2 || neighbors === 3) ? 1 : 0; |
52 | 45 | } else { |
53 | | - // Dead: birth with exactly 3 neighbors |
54 | 46 | lifeNext[idx] = (neighbors === 3) ? 1 : 0; |
55 | 47 | } |
56 | 48 | } |
|
83 | 75 | lifeAnimId = requestAnimationFrame(loopLife); |
84 | 76 | } |
85 | 77 |
|
86 | | - lifeStartBtn.addEventListener('click', () => { |
87 | | - if (lifeRunning) { |
| 78 | + window.addEventListener('DOMContentLoaded', () => { |
| 79 | + lifeCanvas = document.getElementById('life-canvas'); |
| 80 | + lifeCtx = lifeCanvas.getContext('2d'); |
| 81 | + lifeStartBtn = document.getElementById('life-start'); |
| 82 | + lifeResetBtn = document.getElementById('life-reset'); |
| 83 | + |
| 84 | + lifeStartBtn.addEventListener('click', () => { |
| 85 | + if (lifeRunning) { |
| 86 | + lifeRunning = false; |
| 87 | + lifeStartBtn.textContent = 'Start'; |
| 88 | + } else { |
| 89 | + lifeRunning = true; |
| 90 | + lifeStartBtn.textContent = 'Pause'; |
| 91 | + lifeAnimId = requestAnimationFrame(loopLife); |
| 92 | + } |
| 93 | + }); |
| 94 | + |
| 95 | + lifeResetBtn.addEventListener('click', () => { |
88 | 96 | lifeRunning = false; |
89 | 97 | lifeStartBtn.textContent = 'Start'; |
90 | | - } else { |
91 | | - lifeRunning = true; |
92 | | - lifeStartBtn.textContent = 'Pause'; |
93 | | - lifeAnimId = requestAnimationFrame(loopLife); |
94 | | - } |
95 | | - }); |
96 | | - |
97 | | - lifeResetBtn.addEventListener('click', () => { |
98 | | - lifeRunning = false; |
99 | | - lifeStartBtn.textContent = 'Start'; |
100 | | - cancelAnimationFrame(lifeAnimId); |
101 | | - initLife(); |
102 | | - }); |
| 98 | + cancelAnimationFrame(lifeAnimId); |
| 99 | + initLife(); |
| 100 | + }); |
103 | 101 |
|
104 | | - // Initialize on load |
105 | | - window.addEventListener('load', () => { |
106 | 102 | initLife(); |
107 | 103 | }); |
108 | 104 |
|
109 | | - // Handle resize |
110 | 105 | window.addEventListener('resize', () => { |
111 | | - initLife(); |
| 106 | + if (lifeCanvas) initLife(); |
112 | 107 | }); |
113 | 108 | })(); |
0 commit comments