let cols = 10;
let rows = 7;
let w, h;
let grid = [];
let cars = [];
const colors = ["red", "blue", "green", "yellow"];
let currentCarIndex = 0;
function setup() {
createCanvas(900, 600);
w = width / cols;
h = height / rows;
frameRate(7); // Slow down for testing
for (let i = 0; i < cols; i++) {
grid[i] = [];
for (let j = 0; j < rows; j++) {
grid[i][j] = new Spot(i, j);
grid[i][j].wall = !(i % 3 === 0 || j % 3 === 0);
}
}
grid[0][0].wall = true;
grid[0][rows - 1].wall = true;
grid[cols - 1][0].wall = true;
grid[cols - 1][rows - 1].wall = true;
for (let i = 0; i < colors.length; i++) {
let car = createCar(i);
let destination = createDestination(car);
car.destination = destination;
// Calculate the original path (A* for valid path)
let originalPath = findValidPath(car, destination);
console.log(`Car ${i} original path:`, originalPath); // Log the path
car.originalPath = originalPath;
car.hasReachedDestination = false;
cars.push(car);
}
}
function draw() {
background(255);
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
grid[i][j].show();
}
}
for (let car of cars) {
displayPath(car.originalPath, car.color);
car.show();
displayDestination(car.destination, car.color);
}
if (cars.length > 0) {
moveCar(currentCarIndex);
}
}
function Spot(i, j) {
this.i = i;
this.j = j;
this.wall = false;
this.show = function() {
fill(this.wall ? 0 : 255);
stroke(0);
rect(this.i * w, this.j * h, w, h);
};
}
function createCar(carIndex) {
let i, j;
do {
i = floor(random(cols));
j = floor(random(rows));
} while (grid[i][j].wall || isOccupied(i, j));
return new Car(i, j, colors[carIndex]);
}
function isOccupied(i, j) {
return cars.some(car => car.i === i && car.j === j);
}
function createDestination(car) {
let i, j;
do {
i = floor(random(cols));
j = floor(random(rows));
} while (grid[i][j].wall || (i === car.i && j === car.j) || isDestinationOccupied(i, j));
return { i, j };
}
function isDestinationOccupied(i, j) {
return cars.some(car => car.destination && car.destination.i === i && car.destination.j === j);
}
function Car(startI, startJ, carColor) {
this.i = startI;
this.j = startJ;
this.color = carColor;
this.destination = null;
this.originalPath = [];
this.allowedGridsAround = [];
this.blockedGridsAround = [];
this.hasReachedDestination = false;
this.show = function() {
fill(this.color);
noStroke();
ellipse((this.i + 0.5) * w, (this.j + 0.5) * h, w / 2, h / 2);
};
}
function moveCar(index) {
let car = cars[index];
if (!car.hasReachedDestination) {
let { allowedGridsAround, blockedGridsAround } = senseGridAroundCar(car);
car.allowedGridsAround = allowedGridsAround;
car.blockedGridsAround = blockedGridsAround;
let action = planMovement(car);
if (action === "Proceed as per the original plan.") {
let nextStep = car.originalPath[1];
if (nextStep) {
car.i = nextStep.i;
car.j = nextStep.j;
car.originalPath.shift();
console.log(`Car ${car.color} moved to (${car.i}, ${car.j})`);
}
// Check if the car has reached its destination
if (car.originalPath.length === 0 ||
(car.i === car.destination.i && car.j === car.destination.j)) {
car.hasReachedDestination = true;
console.log(`Car ${car.color} reached its destination.`);
// Mark the grid position as empty when the car reaches its destination
grid[car.i][car.j].wall = false;
// Remove the car from the game (from the cars array)
cars.splice(index, 1);
}
} else if (action === "Re-routing and revising the original plan.") {
console.log(`Car ${car.color} is re-routing...`);
car.originalPath = findValidPath(car, car.destination, true);
logPath(car, "Revised path");
} else {
console.log(`Car ${car.color}: ${action}`);
}
}
currentCarIndex = (currentCarIndex + 1) % cars.length;
if (cars.every(car => car.hasReachedDestination)) {
noLoop();
} else if (cars[currentCarIndex].hasReachedDestination) {
currentCarIndex = (currentCarIndex + 1) % cars.length;
}
}
function senseGridAroundCar(car) {
let allowedGridsAround = [];
let blockedGridsAround = [];
let carsAround = [];
let directions = [
{ di: -1, dj: 0 },
{ di: 1, dj: 0 },
{ di: 0, dj: -1 },
{ di: 0, dj: 1 },
];
for (let dir of directions) {
let ni = car.i + dir.di;
let nj = car.j + dir.dj;
if (ni >= 0 && ni < cols && nj >= 0 && nj < rows) {
if (grid[ni][nj].wall) {
blockedGridsAround.push({ i: ni, j: nj });
} else if (cars.some(c => c.i === ni && c.j === nj)) {
blockedGridsAround.push({ i: ni, j: nj });
carsAround.push({ i: ni, j: nj });
} else {
allowedGridsAround.push({ i: ni, j: nj });
}
}
}
return { allowedGridsAround, blockedGridsAround, carsAround };
}
function planMovement(car) {
let nextStep = car.originalPath[1];
if (!nextStep) return "Car has reached its destination.";
let inAllowedGrids = car.allowedGridsAround.some(grid => grid.i === nextStep.i && grid.j === nextStep.j);
if (inAllowedGrids) return "Proceed as per the original plan.";
let inBlockedGrids = car.blockedGridsAround.some(grid => grid.i === nextStep.i && grid.j === nextStep.j);
if (inBlockedGrids) {
let isIntersection = checkIntersection(nextStep.i, nextStep.j);
if (isIntersection) {
return "The car will wait and pass the turn.";
} else {
return "Re-routing and revising the original plan.";
}
}
return "Error: Grid is neither allowed nor blocked.";
}
function checkIntersection(i, j) {
let pathCount = 0;
let directions = [
{ di: -1, dj: 0 },
{ di: 1, dj: 0 },
{ di: 0, dj: -1 },
{ di: 0, dj: 1 },
];
for (let dir of directions) {
let ni = i + dir.di;
let nj = j + dir.dj;
if (ni >= 0 && ni < cols && nj >= 0 && nj < rows && !grid[ni][nj].wall) {
pathCount++;
}
}
return pathCount > 2;
}
function findValidPath(car, destination, secondShortest = false) {
let start = grid[car.i][car.j];
let end = grid[destination.i][destination.j];
if (start.wall || end.wall) return [];
let openSet = [];
let closedSet = new Set();
let cameFrom = {};
openSet.push({ node: start, g: 0, h: manhattanDistance(start.i, start.j, end.i, end.j), f: 0 });
cameFrom[`${start.i},${start.j}`] = null;
let directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
while (openSet.length > 0) {
openSet.sort((a, b) => a.f - b.f);
let current = openSet.shift();
if (current.node === end) {
let path = [];
let temp = current.node;
while (temp) {
path.push(temp);
temp = cameFrom[`${temp.i},${temp.j}`];
}
return path.reverse();
}
closedSet.add(current.node);
for (let [di, dj] of directions) {
let ni = current.node.i + di;
let nj = current.node.j + dj;
if (ni < 0 || nj < 0 || ni >= cols || nj >= rows || grid[ni][nj].wall) continue;
let neighbor = grid[ni][nj];
if (closedSet.has(neighbor)) continue;
if (isOccupied(ni, nj)) continue;
let tentativeG = current.g + 1;
let existingNode = openSet.find(node => node.node === neighbor);
if (secondShortest) {
let additionalPenalty = existingNode ? 1 : 0;
tentativeG += additionalPenalty;
}
if (!existingNode || tentativeG < existingNode.g) {
cameFrom[`${ni},${nj}`] = current.node;
let h = manhattanDistance(ni, nj, end.i, end.j);
let f = tentativeG + h;
if (!existingNode) openSet.push({ node: neighbor, g: tentativeG, h, f });
}
}
}
return [];
}
function manhattanDistance(i1, j1, i2, j2) {
return abs(i1 - i2) + abs(j1 - j2);
}
function logPath(car, label) {
console.log(`${label} for ${car.color}:`);
for (let spot of car.originalPath) {
console.log(`(${spot.i}, ${spot.j})`);
}
}
function displayPath(path, color) {
stroke(color);
noFill();
beginShape();
for (let spot of path) {
vertex(spot.i * w + w / 2, spot.j * h + h / 2);
}
endShape();
}
function displayDestination(destination, color) {
fill(color);
noStroke();
ellipse((destination.i + 0.5) * w, (destination.j + 0.5) * h, w / 3, h / 3);
}