-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgame-of-life.js
More file actions
146 lines (126 loc) · 4.59 KB
/
Copy pathgame-of-life.js
File metadata and controls
146 lines (126 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Game of Life implementation
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('gameOfLife');
if (!canvas) return; // Exit if canvas not found
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// Create two grids for double buffering
let grid = new Uint8Array(width * height);
let nextGrid = new Uint8Array(width * height);
// Control animation speed (in milliseconds between updates)
const updateInterval = 100; // ms
let lastUpdateTime = 0;
// Initialize with a glider gun pattern (Gosper's glider gun)
function initializeGrid() {
// Clear the grid
grid.fill(0);
// Add a Gosper glider gun pattern
const pattern = [
[0, 4], [0, 5], [1, 4], [1, 5], // Block
[10, 4], [10, 5], [10, 6], [11, 3], [11, 7], [12, 2], [12, 8], [13, 2], [13, 8], [14, 5], [15, 3], [15, 7], [16, 4], [16, 5], [16, 6], [17, 5], // Left side of gun
[20, 2], [20, 3], [20, 4], [21, 2], [21, 3], [21, 4], [22, 1], [22, 5], [24, 0], [24, 1], [24, 5], [24, 6], // Right side of gun
[34, 2], [34, 3], [35, 2], [35, 3] // Block
];
// Place the pattern in the center of the grid
const offsetX = Math.floor(width / 2) - 20;
const offsetY = Math.floor(height / 2) - 10;
pattern.forEach(([x, y]) => {
const gridX = x + offsetX;
const gridY = y + offsetY;
if (gridX >= 0 && gridX < width && gridY >= 0 && gridY < height) {
grid[gridY * width + gridX] = 1;
}
});
}
// Function to get the state of a cell
function getCell(x, y) {
// Check if coordinates are outside the grid
if (x < 0 || x >= width || y < 0 || y >= height) {
return 0; // Return 0 (dead) for cells outside the grid
}
return grid[y * width + x];
}
// Function to count live neighbors
function 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;
count += getCell(x + dx, y + dy);
}
}
return count;
}
// Function to update the grid
function updateGrid() {
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = y * width + x;
const neighbors = countNeighbors(x, y);
const alive = grid[idx] === 1;
// Apply Conway's Game of Life rules
if (alive && (neighbors < 2 || neighbors > 3)) {
nextGrid[idx] = 0; // Dies from loneliness or overcrowding
} else if (alive && (neighbors === 2 || neighbors === 3)) {
nextGrid[idx] = 1; // Survives
} else if (!alive && neighbors === 3) {
nextGrid[idx] = 1; // Birth
} else {
nextGrid[idx] = 0; // Remains dead
}
}
}
// Swap grids
[grid, nextGrid] = [nextGrid, grid];
}
// Function to render the grid
function renderGrid() {
// Create an ImageData object
const imageData = ctx.createImageData(width, height);
const data = imageData.data;
// Fill the ImageData
for (let i = 0; i < grid.length; i++) {
const value = grid[i] ? 0 : 255; // Invert colors: 0 for black (alive cells), 255 for white (dead cells)
const idx = i * 4;
data[idx] = value; // R
data[idx + 1] = value; // G
data[idx + 2] = value; // B
data[idx + 3] = 255; // A (always fully opaque)
}
// Put the ImageData on the canvas
ctx.putImageData(imageData, 0, 0);
}
// Animation loop
function animate(timestamp) {
// Only update if enough time has passed
if (!lastUpdateTime || timestamp - lastUpdateTime >= updateInterval) {
updateGrid();
renderGrid();
lastUpdateTime = timestamp;
}
requestAnimationFrame(animate);
}
// Initialize and start the animation
initializeGrid();
renderGrid();
requestAnimationFrame(animate);
// Add click interaction to add random cells
canvas.addEventListener('click', function(event) {
const rect = canvas.getBoundingClientRect();
const scaleX = width / rect.width;
const scaleY = height / rect.height;
const x = Math.floor((event.clientX - rect.left) * scaleX);
const y = Math.floor((event.clientY - rect.top) * scaleY);
// Add a random pattern around the clicked point
for (let dy = -5; dy <= 5; dy++) {
for (let dx = -5; dx <= 5; dx++) {
const nx = (x + dx + width) % width;
const ny = (y + dy + height) % height;
if (Math.random() > 0.7) {
grid[ny * width + nx] = 1;
}
}
}
});
});