// MH edit
const rows = 15; // 20;
const cols = 15; // 30;
const cellSize = 40;
let grid = [];
let cars = [];
const numCars = 4;
let grass_image,road_image,house_image;
let car_images = []; // Array to hold car images
let carColors = [];
function preload() {
grass_image = loadImage('/uploads/nivedithavudayagiri/grass.png'); //Load grass image for non-road
road_image = loadImage('/uploads/nivedithavudayagiri/Road-2-way.png'); //Load road image
road_intersection_image = loadImage('/uploads/nivedithavudayagiri/Road-intersection.jpg'); //Load road image
house_image = loadImage('/uploads/nivedithavudayagiri/New-york-buildings-4.png'); // Load house image (destination spot for cars)
//Load car images
for (let i = 1; i <= numCars; i++) {
car_images.push(loadImage(`/uploads/dash1127/car${i}.png`));
carColors.push(color(random(255), random(255), random(255))); // Assigning random colors for each car trail
}
}
function setup() {
createCanvas(cols * cellSize, rows * cellSize);
//Initialise NYC Grid
grid = createNYCGrid(rows, cols);
//Generate random houses in non-road locations
housePositions = generateHouses(numCars);
//Initialise 4 cars
cars = createCars(numCars);
// Control speed of simulation (2 steps per second)
frameRate(2);
}
function draw() {
background(255);
drawGrid();
drawCars();
// Move cars step-by-step
moveCars();
}
// Set up the NYC grid layout
function createNYCGrid(rows, cols) {
let grid = [];
for (let r = 0; r < rows; r++) {
let row = [];
for (let c = 0; c < cols; c++) {
// Define streets every 3rd row and every 5th column for avenues
if (r % 3 === 0 || c % 5 === 0) {
row.push(0); // Road
} else {
row.push(1); // Building block
}
}
grid.push(row);
}
return grid;
}
// Generate positions for houses within the grid
function generateHouses(n) {
let houses = [];
while (houses.length < n) {
let r = floor(random(1, rows));
let c = floor(random(1, cols));
if (grid[r][c] === 1) { // Ensure it's a non-road building block
houses.push({ x: c, y: r });
grid[r][c] = 2; // Mark cell as house
}
}
return houses;
}
function createCars(n) {
let cars = [];
for (let i = 0; i < n; i++) {
let start = randomRoad();
let end = housePositions[i];
cars.push({
image: car_images[i], // Assign car image
color: carColors[i], // Assign unique color
position: start,
initial: start,
destination: end,
path: aStar(start, end), // Track the final path of the car
pathIndex: 0, // Track the current index in the path
hasReached: false, // Track if the car has reached its destination
angle: 0, // Initial angle
});
}
return cars;
}
// Get position of a random road location to set as start point for cars
function randomRoad() {
let r, c;
do {
r = floor(random(rows));
c = floor(random(cols));
} while (grid[r][c] === 1);
return { x: c, y: r };
}
function drawGrid() {
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
stroke(0);
noFill();
rect(c * cellSize, r * cellSize, cellSize, cellSize); // Draw grid
if (grid[r][c] === 1) {
image(grass_image, c * cellSize, r * cellSize, cellSize, cellSize); // Draw grass
} else if (grid[r][c] === 0) {
if (r % 3 === 0 && c % 5 === 0) {
image(road_intersection_image, c * cellSize, r * cellSize, cellSize, cellSize);
} else {
// Check if this cell should be an avenue (e.g., every third column is an avenue)
if (c % 5 === 0) {
push(); // Save current transformation state
translate(c * cellSize + cellSize / 2, r * cellSize + cellSize / 2); // Move origin to cell center
rotate(HALF_PI); // Rotate 90 degrees (HALF_PI radians)
image(road_image, -cellSize / 2, -cellSize / 2, cellSize, cellSize); // Draw rotated road image
pop(); // Restore previous transformation state
} else {
image(road_image, c * cellSize, r * cellSize, cellSize, cellSize); // Draw normal road
}
}
} else if (grid[r][c] === 2) {
image(house_image, c * cellSize, r * cellSize, cellSize, cellSize); // Draw house
}
}
}
}
function drawCars() {
cars.forEach(car => {
push();
translate(car.position.x * cellSize + cellSize / 2, car.position.y * cellSize + cellSize / 2);
rotate(car.angle);
image(car.image, -cellSize / 2 + 5, -cellSize / 2 + 5, cellSize - 10, cellSize - 10);
pop();
if (car.path.length > 0) {
stroke(car.color);
strokeWeight(2);
for (let i = 0; i < car.path.length - 1; i++) {
let startPos = car.path[i];
let endPos = car.path[i + 1];
line(startPos.x * cellSize + cellSize / 2, startPos.y * cellSize + cellSize / 2,
endPos.x * cellSize + cellSize / 2, endPos.y * cellSize + cellSize / 2);
}
}
fill(car.color);
rect(car.destination.x * cellSize + 10, car.destination.y * cellSize + 10, cellSize - 20, cellSize - 20);
});
}
// A* Pathfinding Algorithm
function aStar(start, end) {
let openSet = [];
let closedSet = [];
openSet.push({ ...start, g: 0, f: heuristic(start, end), parent: null });
while (openSet.length > 0) {
let current = openSet.sort((a, b) => a.f - b.f)[0]; // Get node with lowest f
// If the current position is the destination
if (current.x === end.x && current.y === end.y) {
console.log(`Path found from ${start.x},${start.y} to ${end.x},${end.y}`);
return reconstructPath(current);
}
openSet = openSet.filter(node => node !== current);
closedSet.push(current);
let neighbors = getNeighbors(current);
neighbors.forEach(neighbor => {
if (closedSet.find(n => n.x === neighbor.x && n.y === neighbor.y)) {
return;
}
let tentative_g = current.g + 1;
let inOpenSet = openSet.find(n => n.x === neighbor.x && n.y === neighbor.y);
if (!inOpenSet || tentative_g < neighbor.g) {
neighbor.g = tentative_g;
neighbor.h = heuristic(neighbor, end);
neighbor.f = neighbor.g + neighbor.h;
neighbor.parent = current;
if (!inOpenSet) {
openSet.push(neighbor);
}
}
});
}
console.log(`No path found from ${start.x},${start.y} to ${end.x},${end.y}`);
return []; // No path found
}
//Get neighbors function to get valid roads or houses
function getNeighbors(node) {
// Create a set to keep track of occupied positions by other cars
let occupiedPositions = new Set(cars.map(car => `${car.position.x},${car.position.y}`));
let neighbors = [];
let { x, y } = node;
// Define potential movement directions (left, right, up, down)
const directions = [
{ dx: -1, dy: 0 }, // Left
{ dx: 1, dy: 0 }, // Right
{ dx: 0, dy: -1 }, // Up
{ dx: 0, dy: 1 } // Down
];
directions.forEach(({ dx, dy }) => {
const newX = x + dx;
const newY = y + dy;
// Check if the adjacent cell is within the grid boundaries
if (newX >= 0 && newX < cols && newY >= 0 && newY < rows) {
const cellType = grid[newY][newX];
const positionKey = `${newX},${newY}`;
// Allow only road cells (0) as passable
if (cellType === 0) {
neighbors.push({ x: newX, y: newY });
}
// Check if the cell is a destination house and only add if it's the final destination
else if (cellType === 2 && housePositions.some(house => house.x === newX && house.y === newY)) {
neighbors.push({ x: newX, y: newY });
}
}
});
return neighbors;
}
function heuristic(a, b) {
return abs(a.x - b.x) + abs(a.y - b.y); // Manhattan distance
}
function reconstructPath(node) {
let path = [];
while (node) {
path.push({ x: node.x, y: node.y });
node = node.parent;
}
return path.reverse();
}
// Move cars with priority handling at intersections
function moveCars() {
cars.forEach((car, index) => {
if (car.path.length > 0 && car.pathIndex < car.path.length) {
let nextPosition = car.path[car.pathIndex];
let collision = cars.some((otherCar) =>
otherCar !== car && otherCar.position.x === nextPosition.x && otherCar.position.y === nextPosition.y
);
if (collision) {
console.log(`Collision detected! Checking priority for Car ${index + 1}.`);
let otherCar = cars.find((otherCar) =>
otherCar !== car && otherCar.position.x === nextPosition.x && otherCar.position.y === nextPosition.y
);
let carPriority = heuristic(car.position, car.destination);
let otherCarPriority = heuristic(otherCar.position, otherCar.destination);
if (carPriority < otherCarPriority || (carPriority == otherCarPriority && index < cars.indexOf(otherCar))) {
console.log(`Car ${index + 1} has priority. Path recomputing...`);
grid[nextPosition.y][nextPosition.x]=1;
car.path = aStar(car.position, car.destination);
car.pathIndex++;
car.pathIndex = 0; // Reset path index to start again
grid[nextPosition.y][nextPosition.x]=0;
} else {
console.log(`Car ${cars.indexOf(otherCar) + 1} has priority. Car ${index + 1} will reroute.`);
return;
}
} else {
let dx = nextPosition.x - car.position.x;
let dy = nextPosition.y - car.position.y;
car.angle = atan2(dy, dx);
car.position = nextPosition;
car.pathIndex++;
if (car.position.x === car.destination.x && car.position.y === car.destination.y) {
car.hasReached = true;
console.log(`Car ${index + 1} has reached destination from (${car.initial.x},${car.initial.y}) to (${car.destination.x},${car.destination.y})`);
}
}
}
});
}