Skip to content

Instantly share code, notes, and snippets.

@moshaad7
Created February 6, 2025 23:47
Show Gist options
  • Save moshaad7/21c56443b2ec793b262c1a8cae5f0726 to your computer and use it in GitHub Desktop.
Save moshaad7/21c56443b2ec793b262c1a8cae5f0726 to your computer and use it in GitHub Desktop.
LLD_PARKING_LOT
package main
import (
"fmt"
"sync"
"errors"
)
// --- Vehicle Types ---
// VehicleType indicates the type of a vehicle.
type VehicleType int
const (
Car VehicleType = iota
Bike
)
func (vt VehicleType) String() string {
if vt == Car {
return "Car"
}
return "Bike"
}
// Vehicle interface for all vehicles.
type Vehicle interface {
GetID() string
GetType() VehicleType
}
// CarVehicle represents a car.
type CarVehicle struct {
ID string
}
func (c *CarVehicle) GetID() string {
return c.ID
}
func (c *CarVehicle) GetType() VehicleType {
return Car
}
// BikeVehicle represents a bike.
type BikeVehicle struct {
ID string
}
func (b *BikeVehicle) GetID() string {
return b.ID
}
func (b *BikeVehicle) GetType() VehicleType {
return Bike
}
// --- Parking Spot and Floor ---
// ParkingSpot represents an individual parking spot.
type ParkingSpot struct {
SpotID string
Floor int // Floor index
SpotType VehicleType
Occupied bool
Distance int // Lower value means closer to entrance
}
// Floor contains multiple parking spots.
type Floor struct {
FloorNumber int
Spots []*ParkingSpot
mu sync.Mutex
}
// Utility function to create a floor with given number of car and bike spots.
func createFloor(floorNumber int, numCarSpots, numBikeSpots int) *Floor {
spots := []*ParkingSpot{}
// Create car spots.
for i := 0; i < numCarSpots; i++ {
spot := &ParkingSpot{
SpotID: fmt.Sprintf("F%d-C%d", floorNumber, i+1),
Floor: floorNumber,
SpotType: Car,
Occupied: false,
Distance: i + 1,
}
spots = append(spots, spot)
}
// Create bike spots.
for i := 0; i < numBikeSpots; i++ {
spot := &ParkingSpot{
SpotID: fmt.Sprintf("F%d-B%d", floorNumber, i+1),
Floor: floorNumber,
SpotType: Bike,
Occupied: false,
Distance: i + 1,
}
spots = append(spots, spot)
}
return &Floor{
FloorNumber: floorNumber,
Spots: spots,
}
}
// --- Strategy Pattern for Parking Allocation ---
// ParkingStrategy defines an interface for parking spot selection algorithms.
type ParkingStrategy interface {
FindSpot(lot *ParkingLot, vehicle Vehicle) (*ParkingSpot, error)
}
// NearestSpotStrategy selects the nearest available spot across all floors.
type NearestSpotStrategy struct{}
func (n *NearestSpotStrategy) FindSpot(lot *ParkingLot, vehicle Vehicle) (*ParkingSpot, error) {
var bestSpot *ParkingSpot
// Check each floor.
for _, floor := range lot.Floors {
floor.mu.Lock()
for _, spot := range floor.Spots {
if !spot.Occupied && spot.SpotType == vehicle.GetType() {
// Choose the spot with the smallest distance (and lower floor if tie).
if bestSpot == nil || spot.Distance < bestSpot.Distance ||
(spot.Distance == bestSpot.Distance && floor.FloorNumber < bestSpot.Floor) {
bestSpot = spot
}
}
}
floor.mu.Unlock()
}
if bestSpot == nil {
return nil, errors.New("no available spot")
}
return bestSpot, nil
}
// --- ParkingLot (Facade & Core Functionalities) ---
// ParkingLot represents the entire parking system.
type ParkingLot struct {
Floors []*Floor
vehicleSpotMap map[string]*ParkingSpot // vehicleID -> ParkingSpot
parkingStrategy ParkingStrategy
mu sync.RWMutex
}
// NewParkingLot creates a new ParkingLot with the given floors and parking strategy.
func NewParkingLot(floors []*Floor, strategy ParkingStrategy) *ParkingLot {
return &ParkingLot{
Floors: floors,
vehicleSpotMap: make(map[string]*ParkingSpot),
parkingStrategy: strategy,
}
}
// Park attempts to park a vehicle using the parking strategy.
func (lot *ParkingLot) Park(vehicle Vehicle) error {
lot.mu.Lock()
defer lot.mu.Unlock()
if _, exists := lot.vehicleSpotMap[vehicle.GetID()]; exists {
return errors.New("vehicle already parked")
}
spot, err := lot.parkingStrategy.FindSpot(lot, vehicle)
if err != nil {
return err
}
// Mark the spot as occupied.
floor := lot.Floors[spot.Floor]
floor.mu.Lock()
spot.Occupied = true
floor.mu.Unlock()
lot.vehicleSpotMap[vehicle.GetID()] = spot
fmt.Printf("Parked %s (%s) at spot %s on floor %d\n", vehicle.GetID(), vehicle.GetType(), spot.SpotID, spot.Floor)
return nil
}
// Unpark frees the parking spot for the given vehicle.
func (lot *ParkingLot) Unpark(vehicleID string) error {
lot.mu.Lock()
defer lot.mu.Unlock()
spot, exists := lot.vehicleSpotMap[vehicleID]
if !exists {
return errors.New("vehicle not found")
}
floor := lot.Floors[spot.Floor]
floor.mu.Lock()
spot.Occupied = false
floor.mu.Unlock()
delete(lot.vehicleSpotMap, vehicleID)
fmt.Printf("Unparked vehicle %s from spot %s on floor %d\n", vehicleID, spot.SpotID, spot.Floor)
return nil
}
// Search returns the parking spot for a given vehicle ID.
func (lot *ParkingLot) Search(vehicleID string) (*ParkingSpot, error) {
lot.mu.RLock()
defer lot.mu.RUnlock()
spot, exists := lot.vehicleSpotMap[vehicleID]
if !exists {
return nil, errors.New("vehicle not parked")
}
return spot, nil
}
// --- Main and Test Scenario ---
func main() {
// Create two floors.
floor1 := createFloor(0, 5, 5)
floor2 := createFloor(1, 5, 5)
floors := []*Floor{floor1, floor2}
// Instantiate the parking lot with the NearestSpotStrategy.
strategy := &NearestSpotStrategy{}
parkingLot := NewParkingLot(floors, strategy)
// Create some vehicles.
car1 := &CarVehicle{ID: "CAR123"}
bike1 := &BikeVehicle{ID: "BIKE456"}
car2 := &CarVehicle{ID: "CAR789"}
// Park vehicles asynchronously.
// In a real system, these calls might be invoked concurrently.
if err := parkingLot.Park(car1); err != nil {
fmt.Println("Error parking car1:", err)
}
if err := parkingLot.Park(bike1); err != nil {
fmt.Println("Error parking bike1:", err)
}
if err := parkingLot.Park(car2); err != nil {
fmt.Println("Error parking car2:", err)
}
// Search for a vehicle.
if spot, err := parkingLot.Search("CAR123"); err == nil {
fmt.Printf("Vehicle CAR123 is parked at spot %s on floor %d\n", spot.SpotID, spot.Floor)
} else {
fmt.Println(err)
}
// Unpark a vehicle.
if err := parkingLot.Unpark("BIKE456"); err != nil {
fmt.Println("Error unparking bike1:", err)
}
// Try parking another bike.
bike2 := &BikeVehicle{ID: "BIKE999"}
if err := parkingLot.Park(bike2); err != nil {
fmt.Println("Error parking bike2:", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment