Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions helpers/ObstacleManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
23 changes: 23 additions & 0 deletions helpers/SimulationManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
8 changes: 8 additions & 0 deletions helpers/StatisticsManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
30 changes: 29 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@
</head>
<body>
<canvas id="canvas"></canvas>

<!-- Statistics Button -->
<button id="statsButton" class="stats-button">📊 Stats</button>

<!-- Start/Pause Button -->
<button id="toggleSim" class="control-button">Start</button>

<!-- Reset Button -->
<button id="resetSim" class="control-button">Reset</button>

<!-- Statistics Modal -->
<div id="statsModal" class="modal">
Expand All @@ -23,6 +29,28 @@ <h2>Generation Statistics</h2>
</div>
</div>
</div>

<!-- Configuration Modal -->
<div id="configModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Simulation Setup</h2>
</div>
<div class="modal-body">
<label>
Obstacles:
<input type="number" id="obstacleCount" min="1" max="20" value="10" />
</label>
<br />
<label>
Cars:
<input type="number" id="carCount" min="1" max="200" value="100" />
</label>
<br />
<button id="startSim" class="control-button">Start Simulation</button>
</div>
</div>
</div>

<script type="module" src="main.js"></script>
</body>
Expand Down
110 changes: 84 additions & 26 deletions main.js
Original file line number Diff line number Diff line change
@@ -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';
40 changes: 40 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}