diff --git a/helpers/ObstacleManager.js b/helpers/ObstacleManager.js index 5548fdf..511a8c0 100644 --- a/helpers/ObstacleManager.js +++ b/helpers/ObstacleManager.js @@ -80,4 +80,13 @@ export class ObstacleManager { // Add back the walls this.obstacles.push(...walls); } + + /** + * Update the number of obstacles and rebuild them. + * @param {number} count - New obstacle count + */ + setCount(count) { + this.count = count; + this.initializeObstacles(); + } } diff --git a/helpers/SimulationManager.js b/helpers/SimulationManager.js index 8ba9575..abb5e54 100644 --- a/helpers/SimulationManager.js +++ b/helpers/SimulationManager.js @@ -220,6 +220,29 @@ export class SimulationManager { this.obstacleManager.reset(); } + /** + * Fully reset the simulation to generation 1 with new parameters. + * @param {number} populationSize - Number of cars in the new generation. + * @param {number} obstacleCount - Number of obstacles to spawn. + */ + reset(populationSize, obstacleCount) { + this.populationSize = populationSize; + if (typeof this.obstacleManager.setCount === 'function') { + this.obstacleManager.setCount(obstacleCount); + } + + this.cars = []; + this.deadCars = []; + this.memoryQueue = []; + this.generationStats = []; + this.processedCars.clear(); + this.maxFitness = 0; + this.distanceTraveled = 0; + this.generation = 1; + + this.spawnInitialPopulation(); + } + getAliveCars() { return this.cars.filter(car => car.alive); } diff --git a/helpers/StatisticsManager.js b/helpers/StatisticsManager.js index 34c9325..b8d31d7 100644 --- a/helpers/StatisticsManager.js +++ b/helpers/StatisticsManager.js @@ -10,6 +10,14 @@ export class StatisticsManager { this.setupEventListeners(); } + /** + * Replace the underlying simulation manager instance. + * @param {SimulationManager} manager + */ + setSimulationManager(manager) { + this.simulationManager = manager; + } + setupEventListeners() { // Open modal this.statsButton.addEventListener('click', () => { diff --git a/index.html b/index.html index 6661fc3..5cb8bf3 100644 --- a/index.html +++ b/index.html @@ -7,9 +7,15 @@ - + + + + + + + + + + diff --git a/main.js b/main.js index 2ce00ca..ab77a65 100644 --- a/main.js +++ b/main.js @@ -1,45 +1,103 @@ import { SimulationManager } from './helpers/SimulationManager.js'; import { StatisticsManager } from './helpers/StatisticsManager.js'; -import { - initRoadLines, - updateRoadLines, - drawRoadLines, - } from './helpers/roadLines.js'; +import { initRoadLines, updateRoadLines, drawRoadLines } from './helpers/roadLines.js'; import { ObstacleManager } from './helpers/ObstacleManager.js'; // Get canvas and context -const canvas = document.getElementById("canvas"); +const canvas = document.getElementById('canvas'); canvas.width = 600; canvas.height = 800; -const ctx = canvas.getContext("2d"); +const ctx = canvas.getContext('2d'); -// Initialize road lines +// Initialize road lines once initRoadLines(canvas.height); -// Define constants +// Constants const SPEED = 1; -const NUM_OBSTACLES = 10; -const POPULATION_SIZE = 100; 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); +let simManager = null; +let statsManager = null; +let running = false; +let animationId = null; -// Initialize statistics manager -const statsManager = new StatisticsManager(simManager); +function initSimulation(obstacleCount, carCount) { + const obstacleManager = new ObstacleManager(canvas, obstacleCount, SPEED); + simManager = new SimulationManager( + canvas, + obstacleManager, + carCount, + PARENT_COUNT, + SPEED, + MUTATION_RATE + ); + + if (statsManager) { + statsManager.setSimulationManager(simManager); + } else { + statsManager = new StatisticsManager(simManager); + } +} -// Animation loop function animate() { - ctx.clearRect(0, 0, canvas.width, canvas.height); - - updateRoadLines(SPEED, canvas.height); - drawRoadLines(ctx, canvas.width); - - simManager.update(); - simManager.draw(ctx); - - requestAnimationFrame(animate); + ctx.clearRect(0, 0, canvas.width, canvas.height); + + updateRoadLines(SPEED, canvas.height); + drawRoadLines(ctx, canvas.width); + + simManager.update(); + simManager.draw(ctx); + + if (running) { + animationId = requestAnimationFrame(animate); + } +} + +function startLoop() { + if (!running) { + running = true; + document.getElementById('toggleSim').textContent = 'Pause'; + animationId = requestAnimationFrame(animate); + } } -animate(); +function pauseLoop() { + if (running) { + running = false; + document.getElementById('toggleSim').textContent = 'Start'; + cancelAnimationFrame(animationId); + } +} + +// UI wiring +document.getElementById('toggleSim').addEventListener('click', () => { + if (running) { + pauseLoop(); + } else { + startLoop(); + } +}); + +document.getElementById('resetSim').addEventListener('click', () => { + pauseLoop(); + document.getElementById('configModal').style.display = 'block'; +}); + +document.getElementById('startSim').addEventListener('click', () => { + const obstacleCount = parseInt(document.getElementById('obstacleCount').value, 10); + const carCount = parseInt(document.getElementById('carCount').value, 10); + + if (simManager) { + simManager.reset(carCount, obstacleCount); + statsManager.setSimulationManager(simManager); + } else { + initSimulation(obstacleCount, carCount); + } + + document.getElementById('configModal').style.display = 'none'; + startLoop(); +}); + +// Show configuration modal on first load +document.getElementById('configModal').style.display = 'block'; diff --git a/style.css b/style.css index c80d59b..05ddb5d 100644 --- a/style.css +++ b/style.css @@ -32,6 +32,33 @@ body { transform: scale(1.05); } + /* Control Buttons (Start/Pause and Reset) */ + .control-button { + position: fixed; + top: 20px; + left: 20px; + background: rgba(0, 0, 0, 0.7); + color: white; + border: 2px solid #444; + border-radius: 8px; + padding: 10px 15px; + font-size: 16px; + cursor: pointer; + z-index: 1000; + transition: all 0.3s ease; + margin-right: 10px; + } + + #resetSim { + left: 120px; + } + + .control-button:hover { + background: rgba(0, 0, 0, 0.9); + border-color: #666; + transform: scale(1.05); + } + /* Modal */ .modal { display: none; @@ -107,4 +134,17 @@ body { display: block; margin: 0 auto; } + + /* Config modal input layout */ + #configModal label { + color: white; + font-family: Arial, sans-serif; + display: block; + margin-bottom: 10px; + } + + #configModal input { + width: 100px; + margin-left: 10px; + } \ No newline at end of file