Skip to content

Commit

Permalink
Merge pull request ashishps1#35 from mukulagarwal08/parking-lot-golang
Browse files Browse the repository at this point in the history
Golang Parking Lot
  • Loading branch information
ashishps1 authored Sep 21, 2024
2 parents c111790 + addef57 commit b1bccfd
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 0 deletions.
1 change: 1 addition & 0 deletions problems/parking-lot.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
## Implementations
#### [Java Implementation](../solutions/java/src/parkinglot/)
#### [Python Implementation](../solutions/python/parkinglot/)
#### [Golang Implementation](../solutions/golang/parking-lot/)

## Classes, Interfaces and Enumerations
1. The **ParkingLot** class follows the Singleton pattern to ensure only one instance of the parking lot exists. It maintains a list of levels and provides methods to park and unpark vehicles.
Expand Down
64 changes: 64 additions & 0 deletions solutions/golang/parking-lot/level.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"fmt"
"github.com/ashishps1/awesome-low-level-design/parking-lot/vehicles"
)

type Level struct {
Floor int
ParkingSpots map[vehicles.VehicleType]map[int]*ParkingSpot
}

// NewLevel returns a default level with 30 bike parking, 20 car parking, 10 truck parking
func NewLevel(floor int) *Level {
parkingSpots := make(map[vehicles.VehicleType]map[int]*ParkingSpot)

carSpots := make(map[int]*ParkingSpot)

for spotNo := 1; spotNo <= 20; spotNo++ {
carSpots[spotNo] = NewParkingSpot(vehicles.CAR, spotNo)
}

truckSpots := make(map[int]*ParkingSpot)

for spotNo := 1; spotNo <= 10; spotNo++ {
truckSpots[spotNo] = NewParkingSpot(vehicles.TRUCK, spotNo)
}

bikeSpots := make(map[int]*ParkingSpot)

for spotNo := 1; spotNo <= 30; spotNo++ {
bikeSpots[spotNo] = NewParkingSpot(vehicles.BIKE, spotNo)
}

parkingSpots[vehicles.CAR] = carSpots
parkingSpots[vehicles.TRUCK] = truckSpots
parkingSpots[vehicles.BIKE] = bikeSpots

return &Level{
Floor: floor,
ParkingSpots: parkingSpots,
}
}

func (l *Level) FindParkingSpot(v vehicles.Vehicle) *ParkingSpot {
for _, spot := range l.ParkingSpots[v.Type()] {
if spot.CurrentVehicle == nil {
return spot
}
}
return nil
}

func (l *Level) DisplayAvailability() {
for vtype, spotMap := range l.ParkingSpots {
count := 0
for _, spot := range spotMap {
if spot.CurrentVehicle == nil {
count++
}
}
fmt.Printf("%s: %d\n", vtype.String(), count)
}
}
54 changes: 54 additions & 0 deletions solutions/golang/parking-lot/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"fmt"
"github.com/ashishps1/awesome-low-level-design/parking-lot/vehicles"
"sync"
"time"
)

func main() {
var wg sync.WaitGroup

parkingLots := make(map[int]*ParkingLot)

// testing if singleton working in parking lot
for i := 1; i <= 10; i++ {
for j := 0; j < 5; j++ {
wg.Add(1)
go func(ind int) {
parkingLots[ind] = GetInstance(ind)
wg.Done()
}(i)
}
}

wg.Wait()
// test if park is thread safe
parkingLot := parkingLots[1]
parkingLot.AddLevel(0)
parkingLot.AddLevel(1)

parkingLot.DisplayAvailability()

// try to park 10 cars using different routines
for i := 1; i <= 10; i++ {
wg.Add(1)
go func(ind int) {
car := vehicles.NewCar(fmt.Sprintf("car-%d", ind))
parkingLot.ParkVehicle(car)
wg.Done()
}(i)
}
wg.Wait()
parkingLot.DisplayAvailability()

//unpark any one vehicle
ticket, _ := parkingLot.ParkVehicle(vehicles.NewTruck("truck-1"))

time.Sleep(10 * time.Second)
parkingLot.UnParkVehicle(ticket)
fmt.Printf("ticket = %+v\n", ticket)

fmt.Printf("bill for %s = %d", ticket.Vehicle.RegisNo(), ticket.TotalBill)
}
80 changes: 80 additions & 0 deletions solutions/golang/parking-lot/parkingLot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package main

import (
"errors"
"fmt"
"github.com/ashishps1/awesome-low-level-design/parking-lot/vehicles"
"sync"
)

var (
singletonMap map[int]*ParkingLot
once sync.Once
instanceLock sync.Mutex
parkLock sync.Mutex
)

type ParkingLot struct {
Name string
Address string // this could be another struct but keeping it simple for now
Levels []*Level
}

func GetInstance(id int) *ParkingLot {
once.Do(func() {
singletonMap = make(map[int]*ParkingLot)
})
if _, isPresent := singletonMap[id]; !isPresent {
instanceLock.Lock()
if _, isPresent = singletonMap[id]; !isPresent {
fmt.Printf("Creating new parking lot for id: %d\n", id)
pLot := ParkingLot{}
singletonMap[id] = &pLot
}
instanceLock.Unlock()
}
return singletonMap[id]
}

func (p *ParkingLot) ParkVehicle(vehicle vehicles.Vehicle) (*Ticket, error) {
// Find the best spot available
parkLock.Lock()
defer parkLock.Unlock()

fmt.Printf("Parking vehicle no: %s\n", vehicle.RegisNo())
var spot *ParkingSpot
for _, level := range p.Levels {
if spot = level.FindParkingSpot(vehicle); spot != nil {
break
} else {
return nil, errors.New("parking full")
}
}
// Mark spot as occupied
spot.CurrentVehicle = vehicle
// Generate ticket and return
ticket := NewTicket(spot, vehicle)

return ticket, nil
}

func (p *ParkingLot) DisplayAvailability() {
fmt.Println("Following no of spots available:")
for floor, level := range p.Levels {
fmt.Printf("level %d:\n", floor)
level.DisplayAvailability()
fmt.Println()
}
fmt.Println()
}

func (p *ParkingLot) UnParkVehicle(ticket *Ticket) {
// empty the spot
ticket.ParkingSpot.CurrentVehicle = nil
// calculate the amount
ticket.CalculateAmount()
}

func (p *ParkingLot) AddLevel(floor int) {
p.Levels = append(p.Levels, NewLevel(floor))
}
13 changes: 13 additions & 0 deletions solutions/golang/parking-lot/parkingSpot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import "github.com/ashishps1/awesome-low-level-design/parking-lot/vehicles"

type ParkingSpot struct {
SpotNo int
VehicleType vehicles.VehicleType
CurrentVehicle vehicles.Vehicle
}

func NewParkingSpot(vType vehicles.VehicleType, spotNo int) *ParkingSpot {
return &ParkingSpot{SpotNo: spotNo, VehicleType: vType}
}
30 changes: 30 additions & 0 deletions solutions/golang/parking-lot/ticket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

import (
"github.com/ashishps1/awesome-low-level-design/parking-lot/vehicles"
"time"
)

type Ticket struct {
EntryTime time.Time
ExitTime time.Time
ParkingSpot *ParkingSpot
Vehicle vehicles.Vehicle
TotalBill int
}

func NewTicket(spot *ParkingSpot, vehicle vehicles.Vehicle) *Ticket {
return &Ticket{
EntryTime: time.Now(),
ExitTime: time.Time{},
ParkingSpot: spot,
Vehicle: vehicle,
TotalBill: 0,
}
}

func (t *Ticket) CalculateAmount() {
t.ExitTime = time.Now()
timeSpent := int(t.ExitTime.Sub(t.EntryTime).Seconds())
t.TotalBill = timeSpent * t.Vehicle.Cost()
}
16 changes: 16 additions & 0 deletions solutions/golang/parking-lot/vehicles/bike.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package vehicles

type Bike struct {
vehicleBase
}

func NewBike(regNo string) *Bike {
return &Bike{vehicleBase{
RegistrationNo: regNo,
VehicleType: BIKE,
}}
}

func (c *Bike) Cost() int {
return 10
}
16 changes: 16 additions & 0 deletions solutions/golang/parking-lot/vehicles/car.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package vehicles

type Car struct {
vehicleBase
}

func NewCar(regNo string) *Car {
return &Car{vehicleBase{
RegistrationNo: regNo,
VehicleType: CAR,
}}
}

func (c *Car) Cost() int {
return 20
}
16 changes: 16 additions & 0 deletions solutions/golang/parking-lot/vehicles/truck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package vehicles

type Truck struct {
vehicleBase
}

func NewTruck(regNo string) *Truck {
return &Truck{vehicleBase{
RegistrationNo: regNo,
VehicleType: TRUCK,
}}
}

func (c *Truck) Cost() int {
return 30
}
20 changes: 20 additions & 0 deletions solutions/golang/parking-lot/vehicles/vehicle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package vehicles

type vehicleBase struct {
RegistrationNo string
VehicleType VehicleType
}

type Vehicle interface {
RegisNo() string
Type() VehicleType
Cost() int
}

func (v *vehicleBase) RegisNo() string {
return v.RegistrationNo
}

func (v *vehicleBase) Type() VehicleType {
return v.VehicleType
}
17 changes: 17 additions & 0 deletions solutions/golang/parking-lot/vehicles/vehicleType.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package vehicles

type VehicleType int

const (
CAR VehicleType = iota + 1
TRUCK
BIKE
)

func (v VehicleType) String() string {
return [...]string{"Car", "Truck", "Bike"}[v-1]
}

func (v VehicleType) EnumIndex() int {
return int(v)
}

0 comments on commit b1bccfd

Please sign in to comment.