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 @@ - -
- +
+ +
+ +
+
- diff --git a/style.css b/style.css index 77ce957..b066183 100644 --- a/style.css +++ b/style.css @@ -1,9 +1,16 @@ + body { - margin: 0; - background: #111; - overflow: hidden; - position: relative; - } + margin: 0; + background: #111; + overflow: hidden; + display: flex; + justify-content: center; +} + +#game-container { + position: relative; + width: fit-content; +} canvas { display: block; @@ -20,7 +27,7 @@ body { } #stats-modal { - position: fixed; + position: absolute; top: 0; left: 0; width: 100%; @@ -36,4 +43,4 @@ body { background: #333; border: 1px solid #555; } - \ No newline at end of file +