Code viewer for World: A star (clone by Singiredd...
!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2D Traffic World with A* Pathfinding</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
    <script src="sketch.js"></script>
    <style>
        body {
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background: linear-gradient(135deg, #a8ede1, #fed6e3);
            font-family: 'Arial', sans-serif;
        }

        h1 {
            color: #333;
            margin-bottom: 20px;
            text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
        }

        button {
            margin-bottom: 20px;
            padding: 10px 20px;
            font-size: 18px;
            background-color: #FF6F61; /* Coral */
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }

        button:hover {
            background-color: #FF4C3B; /* Darker Coral */
        }

        canvas {
            border: 2px solid #333; /* Dark border around the canvas */
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
        }

        @media (max-width: 600px) {
            button {
                padding: 8px 16px;
                font-size: 16px;
            }
        }
    </style>
</head>
<body>
    <h1>2D Traffic Simulation with A* Pathfinding</h1>
    <button id="startButton">Start Simulation</button>
    <div id="canvasContainer"></div>
</body>
</html>
let cols = 20; // Number of columns in the grid
let rows = 20; // Number of rows in the grid
let grid = []; // 2D array for the grid structure
let cellSize = 35; // Size of each grid cell
let cars = []; // Array to hold car objects
let simulationRunning = false; // Flag to indicate if simulation is active

// Array of colors for the cars
const carColors = ["#FF5733", "#33FF57", "#3357FF", "#FF33A8"]; 

// Setting up the canvas and initializing the grid and cars
function setup() {
    let canvas = createCanvas(cols * cellSize, rows * cellSize);
    canvas.parent("canvasContainer");
    initializeGrid();
    placeCars(4); // Placing 4 cars
    drawGrid();

    // Adding event listener to start the simulation
    let button = select('#startButton');
    button.mousePressed(startSimulation);
}

// Initializing the grid with streets and blocks for a city-like layout
function initializeGrid() {
    for (let i = 0; i < cols; i++) {
        grid[i] = [];
        for (let j = 0; j < rows; j++) {
            // Define streets: every second row and column is a street
            if (i % 2 === 0 || j % 2 === 0) {
                grid[i][j] = { x: i, y: j, isStreet: true }; // Street cell
            } else {
                grid[i][j] = { x: i, y: j, isStreet: false }; // Block cell
            }
        }
    }
}

// Drawing the grid with enhanced street and block design
function drawGrid() {
    for (let i = 0; i < cols; i++) {
        for (let j = 0; j < rows; j++) {
            if (grid[i][j].isStreet) {
                fill(220); // Light gray color for streets
                stroke(180); // Darker gray for street edges
            } else {
                fill(50); // Dark gray color for blocks
                stroke(100); // Slightly lighter edge for blocks
            }
            rect(i * cellSize, j * cellSize, cellSize, cellSize);
            
            // Drawing intersections with a special marker
            if (grid[i][j].isStreet && i % 2 === 0 && j % 2 === 0) {
                fill(255, 255, 100); // Yellow for intersections
                ellipse((i + 0.5) * cellSize, (j + 0.5) * cellSize, cellSize * 0.3, cellSize * 0.3);
            }
        }
    }
}

// Car class defining position, destination, path, and color
class Car {
    constructor(x, y, destination, color) {
        this.x = x;
        this.y = y;
        this.destination = destination;
        this.path = [];
        this.color = color;
        this.waitCounter = 0; // Counter to handle waiting when blocked
        this.findPath(); // Initial path calculation
    }

    // Method to find a path using A* algorithm
    findPath() {
        this.path = aStarPath(this.x, this.y, this.destination.x, this.destination.y);
    }

    // Method to move the car along the calculated path
    move() {
        if (this.path.length > 0) {
            let nextStep = this.path[0];

            // Check if the next step is blocked by another car
            if (!isCarBlocking(this, nextStep.x, nextStep.y)) {
                // Move the car to the next step if not blocked
                this.path.shift();
                this.x = nextStep.x;
                this.y = nextStep.y;
                this.waitCounter = 0; // Reset wait counter as the car moved
            } else {
                // Increment wait counter if the car is blocked
                this.waitCounter++;

                // After waiting for 5 frames, recalculate path
                if (this.waitCounter > 5) {
                    this.findPath(); // Recalculate path
                    this.waitCounter = 0; // Reset wait counter after recalculating
                }
            }
        }
    }

    // Method to display the car on the grid
    display() {
        fill(this.color); // Set the car's color
        noStroke(); // Remove car border for a cleaner look
        ellipse((this.x + 0.5) * cellSize, (this.y + 0.5) * cellSize, cellSize * 0.6, cellSize * 0.6); // Draw the car as a circle
    }
}

// Placing a set number of cars with random start and destination points
function placeCars(numCars) {
    for (let i = 0; i < numCars; i++) {
        let startX, startY, destX, destY;
        do {
            startX = floor(random(cols));
            startY = floor(random(rows));
            destX = floor(random(cols));
            destY = floor(random(rows));
        } while (!grid[startX][startY].isStreet || !grid[destX][destY].isStreet || (startX === destX && startY === destY));

        let car = new Car(startX, startY, { x: destX, y: destY }, carColors[i % carColors.length]);
        car.findPath();
        cars.push(car);
    }
}

// A* algorithm to find the optimal path
function aStarPath(startX, startY, goalX, goalY) {
    let openSet = [{ x: startX, y: startY, g: 0, h: heuristic(startX, startY, goalX, goalY) }];
    let cameFrom = {};
    let closedSet = new Set();

    while (openSet.length > 0) {
        openSet.sort((a, b) => (a.g + a.h) - (b.g + b.h));
        let current = openSet.shift();

        if (current.x === goalX && current.y === goalY) {
            return reconstructPath(cameFrom, current);
        }

        closedSet.add(`${current.x},${current.y}`);
        let neighbors = getNeighbors(current);

        for (let neighbor of neighbors) {
            if (closedSet.has(`${neighbor.x},${neighbor.y}`) || !grid[neighbor.x][neighbor.y].isStreet) {
                continue;
            }

            let gScore = current.g + 1;
            let hScore = heuristic(neighbor.x, neighbor.y, goalX, goalY);
            let openNode = openSet.find(n => n.x === neighbor.x && n.y === neighbor.y);

            if (!openNode || gScore < openNode.g) {
                cameFrom[`${neighbor.x},${neighbor.y}`] = current;
                if (!openNode) {
                    openSet.push({ x: neighbor.x, y: neighbor.y, g: gScore, h: hScore });
                }
            }
        }
    }
    return [];
}

// Reconstructing the path from goal to start using cameFrom data
function reconstructPath(cameFrom, current) {
    let totalPath = [current];
    while (`${current.x},${current.y}` in cameFrom) {
        current = cameFrom[`${current.x},${current.y}`];
        totalPath.push(current);
    }
    return totalPath.reverse();
}

// Manhattan distance heuristic for A* algorithm
function heuristic(x1, y1, x2, y2) {
    return abs(x1 - x2) + abs(y1 - y2);
}

// Getting neighbors for a given node
function getNeighbors(node) {
    let directions = [
        { x: 1, y: 0 }, { x: -1, y: 0 },
        { x: 0, y: 1 }, { x: 0, y: -1 }
    ];
    return directions
        .map(dir => ({ x: node.x + dir.x, y: node.y + dir.y }))
        .filter(n => n.x >= 0 && n.x < cols && n.y >= 0 && n.y < rows);
}

// Checking if another car is blocking the path
function isCarBlocking(car, x, y) {
    return cars.some(otherCar => otherCar !== car && otherCar.x === x && otherCar.y === y);
}

// Starting the simulation
function startSimulation() {
    simulationRunning = true;
    frameRate(2);
    loop();
}

// Main draw loop
function draw() {
    if (!simulationRunning) return;
    background(240); // Light background color
    drawGrid();

    cars.forEach(car => {
        car.move();
        car.display();
    });
}