From ae6da2e0d9bdf7fbf05bddeaa1c29466fe804b1a Mon Sep 17 00:00:00 2001
From: Mathom Johnson <156151500+MathomJohnson@users.noreply.github.com>
Date: Sun, 15 Jun 2025 22:16:06 -0500
Subject: [PATCH 1/2] Add stats modal with generation graph
---
helpers/SimulationManager.js | 7 +++-
index.html | 4 +++
main.js | 67 ++++++++++++++++++++++++++++++++++--
style.css | 27 +++++++++++++++
4 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/helpers/SimulationManager.js b/helpers/SimulationManager.js
index 6744d6d..c62c84e 100644
--- a/helpers/SimulationManager.js
+++ b/helpers/SimulationManager.js
@@ -2,13 +2,14 @@ import { Car } from "../classes/Car.js";
import { NeuralNetwork } from "../classes/NeuralNetwork.js";
export class SimulationManager {
- constructor(canvas, obstacleManager, populationSize, eliteCount, speed, mutationRate) {
+ constructor(canvas, obstacleManager, populationSize, eliteCount, speed, mutationRate, onGenerationComplete = () => {}) {
this.canvas = canvas;
this.obstacleManager = obstacleManager;
this.populationSize = populationSize;
this.eliteCount = eliteCount;
this.speed = speed;
this.mutationRate = mutationRate;
+ this.onGenerationComplete = onGenerationComplete;
this.generation = 1; // keep track of generations
this.CAR_WIDTH = 30;
@@ -137,6 +138,10 @@ export class SimulationManager {
console.log(`Distance: ${Math.floor(this.distanceTraveled)}`);
elites[0].brain.printNetwork();
+ if (this.onGenerationComplete) {
+ this.onGenerationComplete(this.generation, this.distanceTraveled);
+ }
+
const newCars = [];
const nextGen = this.generation + 1;
diff --git a/index.html b/index.html
index febe057..f1b9782 100644
--- a/index.html
+++ b/index.html
@@ -6,6 +6,10 @@
+
+
+
+
diff --git a/main.js b/main.js
index 561775a..8c91975 100644
--- a/main.js
+++ b/main.js
@@ -12,6 +12,62 @@ canvas.width = 600;
canvas.height = 800;
const ctx = canvas.getContext("2d");
+const statsBtn = document.getElementById('stats-btn');
+const statsModal = document.getElementById('stats-modal');
+const statsCanvas = document.getElementById('stats-canvas');
+const statsCtx = statsCanvas.getContext('2d');
+const statsData = [];
+
+function drawStatsGraph() {
+ statsCtx.clearRect(0, 0, statsCanvas.width, statsCanvas.height);
+ if (statsData.length === 0) return;
+
+ const padding = 30;
+ const maxGen = Math.max(...statsData.map(d => d.generation));
+ const maxDist = Math.max(...statsData.map(d => d.distance));
+
+ statsCtx.strokeStyle = '#fff';
+ statsCtx.beginPath();
+ statsCtx.moveTo(padding, padding);
+ statsCtx.lineTo(padding, statsCanvas.height - padding);
+ statsCtx.lineTo(statsCanvas.width - padding, statsCanvas.height - padding);
+ statsCtx.stroke();
+
+ statsCtx.fillStyle = '#fff';
+ statsCtx.font = '12px monospace';
+ statsCtx.textAlign = 'center';
+ statsCtx.fillText('Generation', statsCanvas.width / 2, statsCanvas.height - 10);
+ statsCtx.save();
+ statsCtx.translate(10, statsCanvas.height / 2);
+ statsCtx.rotate(-Math.PI / 2);
+ statsCtx.fillText('Distance', 0, 0);
+ statsCtx.restore();
+
+ statsCtx.strokeStyle = 'cyan';
+ statsCtx.beginPath();
+ statsData.forEach((d, i) => {
+ const x = padding + ((d.generation - 1) / Math.max(1, maxGen - 1)) * (statsCanvas.width - 2 * padding);
+ const y = (statsCanvas.height - padding) - (d.distance / Math.max(1, maxDist)) * (statsCanvas.height - 2 * padding);
+ if (i === 0) statsCtx.moveTo(x, y); else statsCtx.lineTo(x, y);
+ });
+ statsCtx.stroke();
+}
+
+function handleGenerationComplete(gen, dist) {
+ statsData.push({ generation: gen, distance: dist });
+ drawStatsGraph();
+}
+
+statsBtn.addEventListener('click', () => {
+ const visible = statsModal.style.display === 'flex';
+ statsModal.style.display = visible ? 'none' : 'flex';
+ if (!visible) drawStatsGraph();
+});
+
+statsModal.addEventListener('click', (e) => {
+ if (e.target === statsModal) statsModal.style.display = 'none';
+});
+
// Initialize road lines
initRoadLines(canvas.height);
@@ -22,8 +78,15 @@ const POPULATION_SIZE = 75;
const PARENT_COUNT = 10;
const MUTATION_RATE = 0.2;
-const simManager = new SimulationManager(canvas, new ObstacleManager(canvas, NUM_OBSTACLES, SPEED),
- POPULATION_SIZE, PARENT_COUNT, SPEED, MUTATION_RATE);
+const simManager = new SimulationManager(
+ canvas,
+ new ObstacleManager(canvas, NUM_OBSTACLES, SPEED),
+ POPULATION_SIZE,
+ PARENT_COUNT,
+ SPEED,
+ MUTATION_RATE,
+ handleGenerationComplete
+);
// Animation loop
function animate() {
diff --git a/style.css b/style.css
index e593ee5..77ce957 100644
--- a/style.css
+++ b/style.css
@@ -2,6 +2,7 @@ body {
margin: 0;
background: #111;
overflow: hidden;
+ position: relative;
}
canvas {
@@ -9,4 +10,30 @@ body {
margin: 0 auto;
background: #222;
}
+
+#stats-btn {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ padding: 6px 12px;
+ z-index: 2;
+}
+
+#stats-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: none;
+ background: rgba(0, 0, 0, 0.6);
+ align-items: center;
+ justify-content: center;
+ z-index: 3;
+}
+
+#stats-modal canvas {
+ background: #333;
+ border: 1px solid #555;
+}
\ No newline at end of file
From a34a72ea5118cc2833d3f7b6571ea51d5674ece3 Mon Sep 17 00:00:00 2001
From: Mathom Johnson <156151500+MathomJohnson@users.noreply.github.com>
Date: Sun, 15 Jun 2025 22:28:11 -0500
Subject: [PATCH 2/2] Fix stats overlay layout
---
index.html | 10 ++++++----
style.css | 21 ++++++++++++++-------
2 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/index.html b/index.html
index f1b9782..87f04ab 100644
--- a/index.html
+++ b/index.html
@@ -6,11 +6,13 @@
-
-