Code viewer for World: Tutorial 1.2 (clone by Sin...
import React, { useState, useEffect, useCallback } from 'react';
import { Camera } from 'lucide-react';

// A* Pathfinding Algorithm (Previous implementation remains the same)
class AStar {
  constructor(grid) {
    this.grid = grid;
  }

  // [Previous AStar implementation unchanged]
}

// Main Traffic Simulation Component
const TrafficSimulation = () => {
  const GRID_SIZE = 15;
  const [grid, setGrid] = useState([]);
  const [cars, setCars] = useState([]);
  const [time, setTime] = useState(0);

  // Create street-like grid with a more structured layout
  const createStreetGrid = useCallback(() => {
    const newGrid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
    
    // Create a more structured street grid
    for (let i = 0; i < GRID_SIZE; i++) {
      for (let j = 0; j < GRID_SIZE; j++) {
        // Create streets at regular intervals
        if (i % 4 === 0 || j % 4 === 0) {
          newGrid[i][j] = 0; // Street
        } else {
          // Randomly block some cells to simulate buildings
          newGrid[i][j] = Math.random() < 0.2 ? 1 : 0;
        }
      }
    }
    return newGrid;
  }, []);

  // Initialize simulation
  const initSimulation = useCallback(() => {
    const newGrid = createStreetGrid();
    setGrid(newGrid);

    // Create 4 cars with random start and end positions
    const newCars = Array(4).fill().map((_, index) => {
      let start, end;
      do {
        start = {
          x: Math.floor(Math.random() * GRID_SIZE),
          y: Math.floor(Math.random() * GRID_SIZE)
        };
        end = {
          x: Math.floor(Math.random() * GRID_SIZE),
          y: Math.floor(Math.random() * GRID_SIZE)
        };
      } while (
        newGrid[start.x][start.y] === 1 || 
        newGrid[end.x][end.y] === 1 || 
        start.x === end.x && start.y === end.y
      );

      return {
        id: index,
        position: start,
        destination: end,
        path: []
      };
    });

    setCars(newCars);
  }, [createStreetGrid]);

  // Calculate paths for all cars
  const calculatePaths = useCallback((currentCars) => {
    const astar = new AStar(grid);
    const occupiedCells = new Set();

    // Mark current car positions as occupied
    currentCars.forEach(car => {
      occupiedCells.add(`${car.position.x},${car.position.y}`);
    });

    return currentCars.map(car => {
      // If car has reached destination, return as is
      if (car.position.x === car.destination.x && 
          car.position.y === car.destination.y) {
        return car;
      }

      // Calculate path avoiding occupied cells
      const path = astar.findPath(car.position, car.destination, occupiedCells);
      
      return {
        ...car,
        path: path || []
      };
    });
  }, [grid]);

  // Move cars one step
  const moveCars = useCallback(() => {
    const updatedCars = calculatePaths(cars).map(car => {
      // If path exists and not at destination, move first step
      if (car.path && car.path.length > 1) {
        return {
          ...car,
          position: car.path[1],
          path: car.path.slice(1)
        };
      }
      return car;
    });

    setCars(updatedCars);
    setTime(t => t + 1);
  }, [calculatePaths, cars]);

  // Initial setup
  useEffect(() => {
    initSimulation();
  }, [initSimulation]);

  // Simulation step every second
  useEffect(() => {
    const timer = setInterval(moveCars, 1000);
    return () => clearInterval(timer);
  }, [moveCars]);

  // Render grid cells
  const renderGridCell = useCallback((x, y, cell) => {
    return (
      <div 
        key={`${x}-${y}`}
        className={`
          w-[30px] h-[30px] border 
          ${cell === 1 ? 'bg-gray-300' : 'bg-white'}
          relative
        `}
      >
        {cars.map(car => 
          car.position.x === x && car.position.y === y ? (
            <div 
              key={`car-${car.id}`}
              className="absolute inset-0 flex items-center justify-center"
            >
              <Camera 
                size={20} 
                color={['red', 'blue', 'green', 'purple'][car.id]} 
              />
            </div>
          ) : null
        )}
      </div>
    );
  }, [cars]);

  // Render car paths
  const renderCarPaths = useCallback(() => {
    return cars.flatMap(car => 
      (car.path || []).map((point, index) => (
        <div 
          key={`${car.id}-path-${index}`}
          className="absolute w-[30px] h-[30px] border border-blue-200 opacity-50"
          style={{
            left: `${point.x * 30}px`,
            top: `${point.y * 30}px`
          }}
        />
      ))
    );
  }, [cars]);

  return (
    <div className="flex flex-col items-center p-4">
      <h1 className="text-2xl font-bold mb-4">Traffic Simulation</h1>
      <div className="text-lg mb-2">Time Steps: {time}</div>
      <div 
        className="grid gap-1 relative"
        style={{
          gridTemplateColumns: `repeat(${GRID_SIZE}, 30px)`,
          gridTemplateRows: `repeat(${GRID_SIZE}, 30px)`
        }}
      >
        {grid.map((row, x) => 
          row.map((cell, y) => renderGridCell(x, y, cell))
        )}
        {renderCarPaths()}
      </div>
      <div className="mt-4">
        <button 
          onClick={initSimulation} 
          className="bg-blue-500 text-white px-4 py-2 rounded"
        >
          Restart Simulation
        </button>
      </div>
    </div>
  );
};

export default TrafficSimulation;