Code viewer for World: A star (clone by Daniel Co...
diagonal = false;

const cw = 900;
const ch = 600;
const numCars = 4;
const carColors = ['red', 'blue', 'green', 'yellow'];

function randomIntAtoB(a, b){
    return Math.floor(Math.random() * (b - a + 1)) + a;
}

function randomFloatAtoB(a, b){
    return Math.random() * (b - a) + a;
}
var rando = 3;
var cols = 9 * rando;
var rows = 6 * rando;

const wallAmount = randomFloatAtoB(0.1, 0.5);

const backcolor = 'gray';
const wallcolor = 'lightgreen';

var grid = new Array(cols);
var w, h;
const cars = [];
var grid = [];  // Define grid in the global scope
var openSet = [];  // Define openSet in the global scope

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

    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]);
        if (diagonal)
        {
            if (i < cols - 1 && j < rows - 1) this.neighbors.push(grid[i + 1][j + 1]);
            if (i > 0 && j < rows - 1) this.neighbors.push(grid[i - 1][j + 1]);
            if (i < cols - 1 && j > 0) this.neighbors.push(grid[i + 1][j - 1]);
            if (i > 0 && j > 0) this.neighbors.push(grid[i - 1][j - 1]);
        
        }
    }
}

//Preset patterns for city layout
const preset1 = [
    [false, false, false, false, false, false, false, false, false],
    [false, true, true, false, true, false, true, true, false],
    [false, true, true, false, true, false, true, true, true],
    [false, false, false, false, false, false, false, false, false],
    [false, true, true, false, true, false, false, true, true],
    [false, true, true, false, true, true, false, true, false],
    [false, false, false, false, false, false, false, false, false],
    [false, true, true, false, true, false, true, true, true],
    [false, true, true, false, true, false, true, false, true]
  ];
  
  const preset2 = [
    [false, false, false, false, false, false, false, false, false],
    [false, true, false, true, true, true, true, false, true],
    [false, true, true, false, false, true, true, false, false],
    [false, false, true, true, false, false, false, false, false],
    [false, false, false, false, false, true, false, true, true],
    [false, true, true, false, true, true, false, true, true],
    [false, true, true, false, false, false, false, false, false],
    [false, false, false, true, true, false, true, false, true],
    [false, true, false, true, true, false, true, true, true]
  ];
  
  const preset3 = [
    [false, false, false, false, false, false, false, false, false],
    [false, true, false, false, true, true, false, true, false],
    [false, true, true, false, true, true, false, true, true],
    [false, false, false, false, false, false, false, false, false],
    [false, true, true, false, false, true, true, false, true],
    [false, true, true, true, false, true, true, false, true],
    [false, false, false, false, false, false, false, false, false],
    [false, true, true, false, true, true, true, true, false],
    [false, true, true, false, true, false, true, false, false]
  ];

  const presets = [preset1, preset2, preset3];

  //Apply preset to grid
  function applyPresetToGrid(grid, startX, startY, preset) {
    for (let i = 0; i < preset.length; i++) {
      for (let j = 0; j < preset[i].length; j++) {
        if (startX + i < grid.length && startY + j < grid[0].length) {
          grid[startX + i][startY + j].wall = preset[i][j];
        }
      }
    }
    return grid;
}
function createCityGrid(grid) {
    const presetSize = 9;
    for (let i = 0; i < grid.length; i += presetSize) {
      for (let j = 0; j < grid[0].length; j += presetSize) {
        const randomPreset = presets[Math.floor(Math.random() * presets.length)];
        grid = applyPresetToGrid(grid, i, j, randomPreset);
      }
    }       
    return grid;
}

// Define the heuristic function before the Car class
function heuristic(a, b) {
    return diagonal ? dist(a.i, a.j, b.i, b.j) : abs(a.i - b.i) + abs(a.j - b.j);
}

class Car {
    constructor(start, end, color) {
        this.start = start;  
        this.end = end;
        this.color = color;
        this.current = start;
        this.path = [];
        this.findPath(); // Call findPath once during initialization
    }

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

        while (openSet.length > 0) {
            let winner = openSet.reduce((best, cell, idx) => cell.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((cell) => cell !== 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;
                }
            });
        }
    }

    moveOneStep() {
        if (this.path.length > 1){
            this.current = this.path[1];
            this.path.shift();
        } 
    }

    drawPath(){
        noFill();
        stroke(this.color);
        strokeWeight(4);
        beginShape();
        for (let cell of this.path) {
            vertex(cell.i * w + w / 2, cell.j * h + h / 2);
        }
        endShape();
    }

    showPath() {
        fill(this.color); 
        noStroke();
        rect(this.current.i * w, this.current.j * h, w, h);
    }
}

// Setup function
function setup() {
    frameRate(2);

    // Create canvas and set width and height
    createCanvas(cw, ch);
    w = width / cols;
    h = height / rows;

    console.log("cols:", cols, "rows:", rows); // Add this line to debug

    // Initialize grid
    if (!Number.isInteger(cols) || cols <= 0) {
        console.error("Invalid array length for cols:", cols);
        return;
    }
    if (!Number.isInteger(rows) || rows <= 0) {
        console.error("Invalid array length for rows:", rows);
        return;
    }

    for (var i = 0; i < cols; i++) {
        grid[i] = new Array(rows);
    }

    for (var i = 0; i < cols; i++) {
        for (var j = 0; j < rows; j++) {
            grid[i][j] = new Cell(i, j);
        }
    }
    
    grid = createCityGrid(grid); // Fix this line to call the function
    grid[2][2].wall = true;

    for (var i = 0; i < cols; i++) {
        for (var j = 0; j < rows; j++) {
            grid[i][j].addNeighbors(grid);
        }
    }
    
    start = grid[0][0];
    end = grid[cols - 1][rows - 1];
    start.wall = false;
    end.wall = false;

    openSet.push(start);
    console.log('start search');

    // Initialize cars
    for (let i = 0; i < numCars; i++) {
        let start, end;
        let retryCount = 0;

        const maxRetries = 200;
        do{ 
            start = grid[floor(random(cols))][floor(random(rows))];
            retryCount++;

            if (retryCount > maxRetries) {
                console.log('Max retries reached');
                break;
            }

        }while (start.wall);

        retryCount = 0;

        do {
            end = grid[floor(random(cols))][floor(random(rows))];
            retryCount++;

            if (retryCount > maxRetries) {
                console.log('Max retries reached');
                break;
            }

        } while (end.wall || end === start);

        if (!start || !end) {
            console.error('Failed to assign start or end point for a car');
            continue; // Skip this iteration if either start or end is undefined
        }

        start.wall = false;
        end.wall = false;

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

// Draw function
function draw() {
    background(backcolor);

    grid.flat().forEach(cell => cell.show());

    cars.forEach((car, index) => {
        car.drawPath();

        let blockingCar = cars.find((otherCar, otherIndex) => otherIndex !== index && otherCar.current === car.path[1]);

        if (blockingCar) {
            car.findPath(); // Recalculate path if blocked
        } else {
            car.moveOneStep(); // Move the car one step
        }

        car.showPath();
    });
}