Code viewer for World: A star (clone by test2) (c...

// Cloned by Bharath@2024 on 6 Nov 2024 from World "A star (clone by test2) (clone by Rasinkton Fernando)" by Rasinkton Fernando 
// Please leave this clone trail here.
 
//####  Car start and end marked with the same car color


const diagonal = false;  // No diagonal movement for a street-like setup

const cw = 900;
const ch = 600;
const numCars = 4;

let cols, rows;
let w, h;
const grid = [];
const cars = [];

const wallAmount = 1.0;
const backcolor = 'black';
const wallcolor = 'yellow';

// Colors for each car
const carColors = ['blue', 'green', 'red', 'cyan'];

class Spot {
    constructor(i, j) {
        this.i = i;
        this.j = j;
        this.f = 0;
        this.g = 0;
        this.h = 0;
        this.neighbors = [];
        this.previous = undefined;

        // Introduce random street placement by setting probabilities for rows and columns
        const isStreetRow = random(1) < 0.3;  // 30% chance of an open street row
        const isStreetCol = random(1) < 0.6;  // 30% chance of an open street column
        this.wall = !(isStreetRow || isStreetCol);  // Only walls if neither row nor column is open
    }

    show(col) {
        fill(this.wall ? wallcolor : col || backcolor);
        noStroke();
        rect(this.i * w, this.j * h, w, h);
    }

    addNeighbors(grid) {
        let { i, j } = this;
        if (i < cols - 1) this.neighbors.push(grid[i + 1][j]);
        if (i > 0) this.neighbors.push(grid[i - 1][j]);
        if (j < rows - 1) this.neighbors.push(grid[i][j + 1]);
        if (j > 0) this.neighbors.push(grid[i][j - 1]);
    }
}

class Car {
    constructor(start, end, color) {
        this.start = start;
        this.end = end;
        this.path = [];
        this.current = start;
        this.color = color;
        this.recalculatePath();
    }

    recalculatePath() {
        let openSet = [this.start];
        let closedSet = [];
        this.path = [];

        while (openSet.length > 0) {
            let winner = openSet.reduce((best, spot, idx) => spot.f < openSet[best].f ? idx : best, 0);
            let current = openSet[winner];

            if (current === this.end) {
                let temp = current;
                this.path = [];
                while (temp) {
                    this.path.push(temp);
                    temp = temp.previous;
                }
                this.path.reverse();
                return;
            }

            openSet = openSet.filter((spot) => spot !== current);
            closedSet.push(current);

            current.neighbors.forEach((neighbor) => {
                if (!closedSet.includes(neighbor) && !neighbor.wall) {
                    let tempG = current.g + 1;
                    if (!openSet.includes(neighbor)) {
                        openSet.push(neighbor);
                    } else if (tempG >= neighbor.g) {
                        return;
                    }
                    neighbor.g = tempG;
                    neighbor.h = heuristic(neighbor, this.end);
                    neighbor.f = neighbor.g + neighbor.h;
                    neighbor.previous = current;
                }
            });
        }
    }

    move() {
        if (this.path.length > 1) {
            let nextSpot = this.path[1];
            let blockingCar = cars.find(otherCar => otherCar !== this && otherCar.current === nextSpot);

            if (!blockingCar) {
                this.current = nextSpot;
                this.path.shift();
            } else {
                this.recalculatePath();
            }
        }
    }

    drawPath() {
        noFill();
        stroke(this.color);
        strokeWeight(3);
        beginShape();
        this.path.forEach(spot => vertex(spot.i * w + w / 2, spot.j * h + h / 2));
        endShape();
    }

    show() {
        // Draw start point as a white square
        fill('this.color');
        //noStroke();
        stroke(this.color);
        strokeWeight(3);
        rect(this.start.i * w, this.start.j * h, w, h);

        // Draw end point as a white circle
        fill('white');
        ellipse(this.end.i * w + w / 2, this.end.j * h + h / 2, w * 0.8, h * 0.8);

        // Draw the car at its current position
        fill(this.color);
        stroke(this.color);
        strokeWeight(3);
        //triangle(this.current.i * w, this.current.j * h, w, h)
        rect(this.current.i * w, this.current.j * h, w, h);
    }
}

function heuristic(a, b) {
    return abs(a.i - b.i) + abs(a.j - b.j);
}

function setup() {
    frameRate(2);
    createCanvas(cw, ch);
    cols = floor(width / 20);
    rows = floor(height / 20);
    w = width / cols;
    h = height / rows;

    for (let i = 0; i < cols; i++) {
        grid[i] = new Array(rows);
        for (let j = 0; j < rows; j++) {
            grid[i][j] = new Spot(i, j);
        }
    }

    for (let i = 0; i < cols; i++) {
        for (let j = 0; j < rows; j++) {
            grid[i][j].addNeighbors(grid);
        }
    }

    for (let i = 0; i < numCars; i++) {
        let start, end;
        do {
            start = grid[floor(random(cols))][floor(random(rows))];
        } while (start.wall);
        
        do {
            end = grid[floor(random(cols))][floor(random(rows))];
        } while (end.wall || end === start);

        start.wall = end.wall = false;
        
        let color = carColors[i % carColors.length];
        cars.push(new Car(start, end, color));
    }
}

function draw() {
    background(backcolor);
    grid.flat().forEach((spot) => spot.show());

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