Code viewer for World: Tutorial 18.7 (clone by Si...
function a_star(grid, start, goal) {
    let openList = [];
    let closedList = new Set();
    let cameFrom = {};
    let gScore = {};
    let fScore = {};
    
    // Initialize start node
    openList.push(start);
    gScore[start] = 0;
    fScore[start] = heuristic(start, goal);
    
    while (openList.length > 0) {
        let current = lowestFScore(openList, fScore);
        
        if (current === goal) {
            return reconstructPath(cameFrom, current);
        }
        
        openList = openList.filter(n => n !== current);
        closedList.add(current);
        
        let neighbors = getNeighbors(grid, current);
        for (let neighbor of neighbors) {
            if (closedList.has(neighbor)) continue;
            
            let tentativeGScore = gScore[current] + 1;
            
            if (!openList.includes(neighbor)) {
                openList.push(neighbor);
            }
            if (tentativeGScore >= gScore[neighbor]) continue;
            
            cameFrom[neighbor] = current;
            gScore[neighbor] = tentativeGScore;
            fScore[neighbor] = gScore[neighbor] + heuristic(neighbor, goal);
        }
    }
    
    return null;  // No path found
}

function lowestFScore(openList, fScore) {
    let lowest = openList[0];
    for (let node of openList) {
        if (fScore[node] < fScore[lowest]) {
            lowest = node;
        }
    }
    return lowest;
}

function heuristic(a, b) {
    return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);  // Manhattan distance
}

function getNeighbors(grid, node) {
    let neighbors = [];
    let [row, col] = node;
    
    let moves = [
        [0, 1], [1, 0], [0, -1], [-1, 0]  // Right, Down, Left, Up
    ];
    
    for (let move of moves) {
        let newRow = row + move[0];
        let newCol = col + move[1];
        if (newRow >= 0 && newRow < grid.length && newCol >= 0 && newCol < grid[0].length && grid[newRow][newCol] === 0) {
            neighbors.push([newRow, newCol]);
        }
    }
    
    return neighbors;
}

function reconstructPath(cameFrom, current) {
    let path = [current];
    while (current in cameFrom) {
        current = cameFrom[current];
        path.push(current);
    }
    return path.reverse();
}
class Car {
    constructor(start, goal, grid) {
        this.position = start;
        this.goal = goal;
        this.grid = grid;
        this.path = a_star(grid, start, goal);
        this.index = 0;
    }
    
    move() {
        if (this.path && this.index < this.path.length) {
            this.position = this.path[this.index];
            this.index++;
        }
    }
    
    render() {
        // Render the car on the canvas
        let [x, y] = this.position;
        drawCar(x, y);  // Replace with actual Ancient Brain rendering logic
    }
}
// Define the grid (0 = road, 1 = obstacle)
let grid = [
    [0, 0, 0, 1, 0],
    [0, 1, 0, 1, 0],
    [0, 1, 0, 0, 0],
    [0, 0, 0, 1, 0],
    [0, 1, 0, 1, 0],
];

// Initialize cars with start and goal positions
let car1 = new Car([0, 0], [4, 4], grid);
let car2 = new Car([4, 0], [0, 4], grid);
let cars = [car1, car2];

// Render function for Ancient Brain
function startBrain() {
    // Render grid and cars
    for (let row = 0; row < grid.length; row++) {
        for (let col = 0; col < grid[0].length; col++) {
            drawCell(row, col, grid[row][col]);
        }
    }
    
    for (let car of cars) {
        car.render();
        car.move();
    }
}

// Custom draw functions
function drawCell(row, col, type) {
    if (type === 1) {
        // Draw obstacle
        ctx.fillStyle = 'black';
    } else {
        // Draw road
        ctx.fillStyle = 'gray';
    }
    ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
}

function drawCar(x, y) {
    ctx.fillStyle = 'blue';
    ctx.fillRect(y * cellSize + 5, x * cellSize + 5, cellSize - 10, cellSize - 10);
}
function checkCollision(car, cars) {
    for (let otherCar of cars) {
        if (car !== otherCar && car.position.toString() === otherCar.position.toString()) {
            return true;
        }
    }
    return false;
}

function updateCars() {
    for (let car of cars) {
        if (!checkCollision(car, cars)) {
            car.move();
        }
    }
}