TeacherPuffy commited on
Commit
2719f89
·
verified ·
1 Parent(s): ea678b8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +131 -85
index.html CHANGED
@@ -107,19 +107,22 @@
107
  <div class="rules">
108
  <h2>Simulation Rules</h2>
109
  <ul>
110
- <li>Cancer cells die when surrounded by 3+ cells within 40px</li>
 
111
  <li>Cancer cells convert isolated healthy cells (no nearby healthy)</li>
112
  <li>Cells reproduce based on type-specific rates</li>
113
- <li>Cancer cells move faster than healthy cells</li>
114
  <li>Neural networks control movement decisions</li>
115
- <li>Adjust parameters below to influence outcomes</li>
116
  </ul>
117
  </div>
118
 
119
  <div class="controls">
120
  <h2>Simulation Parameters</h2>
121
  <div class="param-group">
122
- <label>Hidden Dimension: <input type="number" id="hiddenDim" value="4" min="1"></label>
 
 
 
123
  <label>Initial Healthy: <input type="number" id="initialHealthy" value="15" min="1"></label>
124
  <label>Initial Cancer: <input type="number" id="initialCancer" value="8" min="1"></label>
125
  <label>Mutation Rate: <input type="number" id="mutationRate" value="0.1" step="0.01" min="0"></label>
@@ -174,7 +177,9 @@
174
  const cellRadius = 5;
175
  let populationChart = null;
176
  let generationsData = [];
177
- let currentHiddenDim = 4;
 
 
178
 
179
  function getNormal(mean = 0, std = 1) {
180
  let u, v, s;
@@ -277,6 +282,96 @@
277
  }
278
  }
279
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  function updateChart() {
281
  const ctx = document.getElementById('populationChart').getContext('2d');
282
 
@@ -323,92 +418,40 @@
323
  `).join('');
324
  }
325
 
326
- function saveGenerationData() {
327
- if(generation % 10 === 0) {
328
- const healthy = cells.filter(c => c.type === 'healthy').length;
329
- const cancer = cells.filter(c => c.type === 'cancer').length;
330
- const speeds = cells.map(c => c.speed);
331
- const avgSpeed = speeds.reduce((a,b) => a + b, 0) / speeds.length || 0;
332
-
333
- generationsData.push({
334
- generation,
335
- healthy,
336
- cancer,
337
- avgSpeed
338
- });
339
-
340
- if(generationsData.length > 20) generationsData.shift();
341
- updateChart();
342
- updateTable();
343
- }
344
- }
345
-
346
- function checkCollisions() {
347
- cells.forEach((cell, i) => {
348
- if(cell.type === 'cancer') {
349
- const neighbors = cells.filter(c => c !== cell &&
350
- Math.hypot(c.x - cell.x, c.y - cell.y) < 40);
351
- if(neighbors.length >= 3) {
352
- cells.splice(i, 1);
353
- return;
354
  }
355
 
356
- cells.forEach((other) => {
357
- if(other.type === 'healthy' &&
358
- Math.hypot(cell.x - other.x, cell.y - other.y) < cellRadius * 2) {
359
- const healthyNeighbors = cells.filter(c =>
360
- c !== other && c.type === 'healthy' &&
361
- Math.hypot(c.x - other.x, c.y - other.y) < 60
362
- );
363
- if(healthyNeighbors.length === 0) {
364
- other.type = 'cancer';
365
- }
366
- }
367
- });
368
  }
369
- });
370
- }
371
-
372
- function reproduceCells() {
373
- const healthyReproRate = parseInt(document.getElementById('healthyRepro').value);
374
- const healthyCells = cells.filter(c => c.type === 'healthy');
375
- const healthyCandidates = [...healthyCells].sort(() => Math.random() - 0.5).slice(0, healthyReproRate);
376
- healthyCandidates.forEach(cell => cells.push(new Cell('healthy', cell)));
377
-
378
- const cancerReproRate = parseInt(document.getElementById('cancerRepro').value);
379
- const cancerCells = cells.filter(c => c.type === 'cancer');
380
- const cancerCandidates = [...cancerCells].sort(() => Math.random() - 0.5).slice(0, cancerReproRate);
381
- cancerCandidates.forEach(cell => cells.push(new Cell('cancer', cell)));
382
- }
383
-
384
- function updateStatus() {
385
- document.getElementById('genCount').textContent = generation;
386
- document.getElementById('healthyCount').textContent =
387
- cells.filter(c => c.type === 'healthy').length;
388
- document.getElementById('cancerCount').textContent =
389
- cells.filter(c => c.type === 'cancer').length;
390
- }
391
-
392
- function animate() {
393
- ctx.clearRect(0, 0, canvas.width, canvas.height);
394
-
395
- frameCount++;
396
- if(frameCount % 60 === 0) {
397
- generation++;
398
- reproduceCells();
399
- updateStatus();
400
- saveGenerationData();
401
  }
402
-
403
- cells.forEach(cell => cell.update());
404
- checkCollisions();
405
- cells.forEach(cell => cell.draw());
406
-
407
- animationId = requestAnimationFrame(animate);
408
  }
409
 
410
  document.getElementById('start').addEventListener('click', () => {
411
- if(!animationId) animate();
 
 
 
412
  });
413
 
414
  document.getElementById('reset').addEventListener('click', () => {
@@ -433,7 +476,10 @@
433
  cells.forEach(cell => cell.draw());
434
  });
435
 
436
- // Initialize simulation
 
 
 
437
  document.getElementById('reset').click();
438
  </script>
439
  </body>
 
107
  <div class="rules">
108
  <h2>Simulation Rules</h2>
109
  <ul>
110
+ <li>Cancer cells die when surrounded by 3+ cells within <span id="deathProxDisplay">20</span>px</li>
111
+ <li>Healthy cells die when near 2+ cancer cells within 30px</li>
112
  <li>Cancer cells convert isolated healthy cells (no nearby healthy)</li>
113
  <li>Cells reproduce based on type-specific rates</li>
114
+ <li>Cancer cells move 50% faster than healthy cells</li>
115
  <li>Neural networks control movement decisions</li>
 
116
  </ul>
117
  </div>
118
 
119
  <div class="controls">
120
  <h2>Simulation Parameters</h2>
121
  <div class="param-group">
122
+ <label>Death Proximity (px): <input type="number" id="deathProximity" value="20" min="1"></label>
123
+ <label>Healthy Death Threshold: <input type="number" id="healthyDeathThreshold" value="2" min="1"></label>
124
+ <label>Healthy Death Radius: <input type="number" id="healthyDeathRadius" value="30" min="1"></label>
125
+ <label>Hidden Dimension: <input type="number" id="hiddenDim" value="6" min="1"></label>
126
  <label>Initial Healthy: <input type="number" id="initialHealthy" value="15" min="1"></label>
127
  <label>Initial Cancer: <input type="number" id="initialCancer" value="8" min="1"></label>
128
  <label>Mutation Rate: <input type="number" id="mutationRate" value="0.1" step="0.01" min="0"></label>
 
177
  const cellRadius = 5;
178
  let populationChart = null;
179
  let generationsData = [];
180
+ let currentHiddenDim = 6;
181
+ const targetFPS = 60;
182
+ let lastFrame = 0;
183
 
184
  function getNormal(mean = 0, std = 1) {
185
  let u, v, s;
 
282
  }
283
  }
284
 
285
+ function checkCollisions() {
286
+ const deathProximity = parseInt(document.getElementById('deathProximity').value);
287
+ const healthyDeathThreshold = parseInt(document.getElementById('healthyDeathThreshold').value);
288
+ const healthyDeathRadius = parseInt(document.getElementById('healthyDeathRadius').value);
289
+
290
+ const cellsCopy = [...cells];
291
+ const cellsToRemove = new Set();
292
+ const cellsToConvert = new Set();
293
+
294
+ cellsCopy.forEach((cell) => {
295
+ if(cell.type === 'healthy') {
296
+ const nearbyCancer = cellsCopy.filter(c =>
297
+ c.type === 'cancer' &&
298
+ Math.hypot(c.x - cell.x, c.y - cell.y) < healthyDeathRadius
299
+ );
300
+ if(nearbyCancer.length >= healthyDeathThreshold) {
301
+ cellsToRemove.add(cell);
302
+ }
303
+ }
304
+
305
+ if(cell.type === 'cancer') {
306
+ const neighbors = cellsCopy.filter(c =>
307
+ c !== cell &&
308
+ Math.hypot(c.x - cell.x, c.y - cell.y) < deathProximity
309
+ );
310
+ if(neighbors.length >= 3) {
311
+ cellsToRemove.add(cell);
312
+ }
313
+
314
+ cellsCopy.forEach((other) => {
315
+ if(other.type === 'healthy' &&
316
+ Math.hypot(cell.x - other.x, cell.y - other.y) < cellRadius * 2 &&
317
+ !cellsToConvert.has(other)) {
318
+ const healthyNeighbors = cellsCopy.filter(c =>
319
+ c !== other &&
320
+ c.type === 'healthy' &&
321
+ Math.hypot(c.x - other.x, c.y - other.y) < 60
322
+ );
323
+ if(healthyNeighbors.length === 0) {
324
+ cellsToConvert.add(other);
325
+ }
326
+ }
327
+ });
328
+ }
329
+ });
330
+
331
+ cells = cells.filter(cell => !cellsToRemove.has(cell));
332
+ cellsToConvert.forEach(cell => cell.type = 'cancer');
333
+ }
334
+
335
+ function reproduceCells() {
336
+ const healthyReproRate = parseInt(document.getElementById('healthyRepro').value);
337
+ const healthyCells = cells.filter(c => c.type === 'healthy');
338
+ const healthyCandidates = [...healthyCells].sort(() => Math.random() - 0.5).slice(0, healthyReproRate);
339
+ healthyCandidates.forEach(cell => cells.push(new Cell('healthy', cell)));
340
+
341
+ const cancerReproRate = parseInt(document.getElementById('cancerRepro').value);
342
+ const cancerCells = cells.filter(c => c.type === 'cancer');
343
+ const cancerCandidates = [...cancerCells].sort(() => Math.random() - 0.5).slice(0, cancerReproRate);
344
+ cancerCandidates.forEach(cell => cells.push(new Cell('cancer', cell)));
345
+ }
346
+
347
+ function updateStatus() {
348
+ document.getElementById('genCount').textContent = generation;
349
+ document.getElementById('healthyCount').textContent =
350
+ cells.filter(c => c.type === 'healthy').length;
351
+ document.getElementById('cancerCount').textContent =
352
+ cells.filter(c => c.type === 'cancer').length;
353
+ }
354
+
355
+ function saveGenerationData() {
356
+ if(generation % 10 === 0) {
357
+ const healthy = cells.filter(c => c.type === 'healthy').length;
358
+ const cancer = cells.filter(c => c.type === 'cancer').length;
359
+ const speeds = cells.map(c => c.speed);
360
+ const avgSpeed = speeds.reduce((a,b) => a + b, 0) / speeds.length || 0;
361
+
362
+ generationsData.push({
363
+ generation,
364
+ healthy,
365
+ cancer,
366
+ avgSpeed
367
+ });
368
+
369
+ if(generationsData.length > 20) generationsData.shift();
370
+ updateChart();
371
+ updateTable();
372
+ }
373
+ }
374
+
375
  function updateChart() {
376
  const ctx = document.getElementById('populationChart').getContext('2d');
377
 
 
418
  `).join('');
419
  }
420
 
421
+ function safeAnimate(timestamp) {
422
+ try {
423
+ const delta = timestamp - lastFrame;
424
+
425
+ if (delta >= 1000/targetFPS) {
426
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
427
+
428
+ frameCount++;
429
+ if(frameCount % 60 === 0) {
430
+ generation++;
431
+ reproduceCells();
432
+ updateStatus();
433
+ saveGenerationData();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  }
435
 
436
+ cells.forEach(cell => cell.update());
437
+ checkCollisions();
438
+ cells.forEach(cell => cell.draw());
439
+
440
+ lastFrame = timestamp;
 
 
 
 
 
 
 
441
  }
442
+ animationId = requestAnimationFrame(safeAnimate);
443
+ } catch (error) {
444
+ console.error('Simulation error:', error);
445
+ document.getElementById('status').innerHTML += ' [PAUSED DUE TO ERROR]';
446
+ cancelAnimationFrame(animationId);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  }
 
 
 
 
 
 
448
  }
449
 
450
  document.getElementById('start').addEventListener('click', () => {
451
+ if(!animationId) {
452
+ lastFrame = performance.now();
453
+ animationId = requestAnimationFrame(safeAnimate);
454
+ }
455
  });
456
 
457
  document.getElementById('reset').addEventListener('click', () => {
 
476
  cells.forEach(cell => cell.draw());
477
  });
478
 
479
+ document.getElementById('deathProximity').addEventListener('input', function() {
480
+ document.getElementById('deathProxDisplay').textContent = this.value;
481
+ });
482
+
483
  document.getElementById('reset').click();
484
  </script>
485
  </body>