import React, { useState, useEffect, useCallback } from 'react';
import { Camera } from 'lucide-react';
// A* Pathfinding Algorithm (Previous implementation remains the same)
class AStar {
constructor(grid) {
this.grid = grid;
}
// [Previous AStar implementation unchanged]
}
// Main Traffic Simulation Component
const TrafficSimulation = () => {
const GRID_SIZE = 15;
const [grid, setGrid] = useState([]);
const [cars, setCars] = useState([]);
const [time, setTime] = useState(0);
// Create street-like grid with a more structured layout
const createStreetGrid = useCallback(() => {
const newGrid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
// Create a more structured street grid
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
// Create streets at regular intervals
if (i % 4 === 0 || j % 4 === 0) {
newGrid[i][j] = 0; // Street
} else {
// Randomly block some cells to simulate buildings
newGrid[i][j] = Math.random() < 0.2 ? 1 : 0;
}
}
}
return newGrid;
}, []);
// Initialize simulation
const initSimulation = useCallback(() => {
const newGrid = createStreetGrid();
setGrid(newGrid);
// Create 4 cars with random start and end positions
const newCars = Array(4).fill().map((_, index) => {
let start, end;
do {
start = {
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE)
};
end = {
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE)
};
} while (
newGrid[start.x][start.y] === 1 ||
newGrid[end.x][end.y] === 1 ||
start.x === end.x && start.y === end.y
);
return {
id: index,
position: start,
destination: end,
path: []
};
});
setCars(newCars);
}, [createStreetGrid]);
// Calculate paths for all cars
const calculatePaths = useCallback((currentCars) => {
const astar = new AStar(grid);
const occupiedCells = new Set();
// Mark current car positions as occupied
currentCars.forEach(car => {
occupiedCells.add(`${car.position.x},${car.position.y}`);
});
return currentCars.map(car => {
// If car has reached destination, return as is
if (car.position.x === car.destination.x &&
car.position.y === car.destination.y) {
return car;
}
// Calculate path avoiding occupied cells
const path = astar.findPath(car.position, car.destination, occupiedCells);
return {
...car,
path: path || []
};
});
}, [grid]);
// Move cars one step
const moveCars = useCallback(() => {
const updatedCars = calculatePaths(cars).map(car => {
// If path exists and not at destination, move first step
if (car.path && car.path.length > 1) {
return {
...car,
position: car.path[1],
path: car.path.slice(1)
};
}
return car;
});
setCars(updatedCars);
setTime(t => t + 1);
}, [calculatePaths, cars]);
// Initial setup
useEffect(() => {
initSimulation();
}, [initSimulation]);
// Simulation step every second
useEffect(() => {
const timer = setInterval(moveCars, 1000);
return () => clearInterval(timer);
}, [moveCars]);
// Render grid cells
const renderGridCell = useCallback((x, y, cell) => {
return (
<div
key={`${x}-${y}`}
className={`
w-[30px] h-[30px] border
${cell === 1 ? 'bg-gray-300' : 'bg-white'}
relative
`}
>
{cars.map(car =>
car.position.x === x && car.position.y === y ? (
<div
key={`car-${car.id}`}
className="absolute inset-0 flex items-center justify-center"
>
<Camera
size={20}
color={['red', 'blue', 'green', 'purple'][car.id]}
/>
</div>
) : null
)}
</div>
);
}, [cars]);
// Render car paths
const renderCarPaths = useCallback(() => {
return cars.flatMap(car =>
(car.path || []).map((point, index) => (
<div
key={`${car.id}-path-${index}`}
className="absolute w-[30px] h-[30px] border border-blue-200 opacity-50"
style={{
left: `${point.x * 30}px`,
top: `${point.y * 30}px`
}}
/>
))
);
}, [cars]);
return (
<div className="flex flex-col items-center p-4">
<h1 className="text-2xl font-bold mb-4">Traffic Simulation</h1>
<div className="text-lg mb-2">Time Steps: {time}</div>
<div
className="grid gap-1 relative"
style={{
gridTemplateColumns: `repeat(${GRID_SIZE}, 30px)`,
gridTemplateRows: `repeat(${GRID_SIZE}, 30px)`
}}
>
{grid.map((row, x) =>
row.map((cell, y) => renderGridCell(x, y, cell))
)}
{renderCarPaths()}
</div>
<div className="mt-4">
<button
onClick={initSimulation}
className="bg-blue-500 text-white px-4 py-2 rounded"
>
Restart Simulation
</button>
</div>
</div>
);
};
export default TrafficSimulation;