Skip to content

Instantly share code, notes, and snippets.

@moshaad7
Created February 6, 2025 21:40
Show Gist options
  • Save moshaad7/6d31c5d7660fa4c2680572b19279f0e2 to your computer and use it in GitHub Desktop.
Save moshaad7/6d31c5d7660fa4c2680572b19279f0e2 to your computer and use it in GitHub Desktop.
LLD_RIDE_SHARING
package main
import (
"errors"
"fmt"
"log"
"math"
"math/rand"
"sync"
"time"
)
// ======================== Core Interfaces ========================
type Rider interface {
ID() string
Location() Location
Notify(string)
}
type Driver interface {
ID() string
Location() Location
Status() DriverStatus
AssignRide(ride Ride)
}
type Ride interface {
ID() string
Status() RideStatus
CalculateFare() float64
}
type PricingStrategy interface {
CalculateFare(Ride) float64
}
// ======================== Base Types ========================
type Location struct {
Lat float64
Lng float64
}
type DriverStatus int
type RideStatus int
const (
Available DriverStatus = iota
InRide
Offline
)
const (
Requested RideStatus = iota
DriverAssigned
InProgress
Completed
Cancelled
)
// ======================== Implementations ========================
type RiderImpl struct {
id string
location Location
notifyCh chan string
}
func NewRider(id string, loc Location) *RiderImpl {
return &RiderImpl{
id: id,
location: loc,
notifyCh: make(chan string, 100),
}
}
func (r *RiderImpl) ID() string { return r.id }
func (r *RiderImpl) Location() Location { return r.location }
func (r *RiderImpl) Notify(msg string) {
select {
case r.notifyCh <- msg:
default:
log.Println("Notification queue full for rider", r.id)
}
}
type DriverImpl struct {
id string
location Location
status DriverStatus
mu sync.RWMutex
rideCh chan Ride
}
func NewDriver(id string, loc Location) *DriverImpl {
return &DriverImpl{
id: id,
location: loc,
status: Available,
rideCh: make(chan Ride, 5),
}
}
func (d *DriverImpl) ID() string { return d.id }
func (d *DriverImpl) Location() Location { return d.location }
func (d *DriverImpl) Status() DriverStatus {
d.mu.RLock()
defer d.mu.RUnlock()
return d.status
}
func (d *DriverImpl) AssignRide(ride Ride) {
d.mu.Lock()
defer d.mu.Unlock()
if d.status == Available {
d.status = InRide
d.rideCh <- ride
}
}
// ======================== Ride Implementation ========================
type RideImpl struct {
id string
rider Rider
driver Driver
startLocation Location
endLocation Location
status RideStatus
startTime time.Time
endTime time.Time
pricing PricingStrategy
mu sync.RWMutex
}
func NewRide(id string, rider Rider, start, end Location, strategy PricingStrategy) *RideImpl {
return &RideImpl{
id: id,
rider: rider,
startLocation: start,
endLocation: end,
status: Requested,
pricing: strategy,
}
}
func (r *RideImpl) ID() string { return r.id }
func (r *RideImpl) Status() RideStatus {
r.mu.RLock()
defer r.mu.RUnlock()
return r.status
}
func (r *RideImpl) CalculateFare() float64 {
return r.pricing.CalculateFare(r)
}
// ======================== Pricing Strategies ========================
type StandardPricing struct {
baseFare float64
perMileRate float64
perMinuteRate float64
}
func (s *StandardPricing) CalculateFare(ride Ride) float64 {
r := ride.(*RideImpl)
duration := r.endTime.Sub(r.startTime).Minutes()
distance := calculateDistance(r.startLocation, r.endLocation)
return s.baseFare + (distance * s.perMileRate) + (duration * s.perMinuteRate)
}
type SurgePricing struct {
*StandardPricing
multiplier float64
}
func (s *SurgePricing) CalculateFare(ride Ride) float64 {
return s.StandardPricing.CalculateFare(ride) * s.multiplier
}
// ======================== Ride Matching Service ========================
type RideMatcher struct {
drivers map[string]Driver
rideRequests chan *RideImpl
mu sync.RWMutex
strategy MatchingStrategy
}
type MatchingStrategy interface {
MatchDriver(ride *RideImpl, drivers []Driver) (Driver, error)
}
func NewRideMatcher(strategy MatchingStrategy) *RideMatcher {
return &RideMatcher{
drivers: make(map[string]Driver),
rideRequests: make(chan *RideImpl, 1000),
strategy: strategy,
}
}
func (rm *RideMatcher) AddDriver(driver Driver) {
rm.mu.Lock()
defer rm.mu.Unlock()
rm.drivers[driver.ID()] = driver
}
func (rm *RideMatcher) Start() {
go func() {
for ride := range rm.rideRequests {
rm.mu.RLock()
drivers := make([]Driver, 0, len(rm.drivers))
for _, d := range rm.drivers {
if d.Status() == Available {
drivers = append(drivers, d)
}
}
rm.mu.RUnlock()
if driver, err := rm.strategy.MatchDriver(ride, drivers); err == nil {
driver.AssignRide(ride)
ride.driver = driver
ride.rider.Notify(fmt.Sprintf("Driver %s accepted your ride", driver.ID()))
} else {
ride.rider.Notify("No drivers available")
}
}
}()
}
// ======================== Matching Strategies ========================
type NearestDriverStrategy struct{}
func (n *NearestDriverStrategy) MatchDriver(ride *RideImpl, drivers []Driver) (Driver, error) {
var nearestDriver Driver
minDistance := math.MaxFloat64
for _, d := range drivers {
dist := calculateDistance(ride.rider.Location(), d.Location())
if dist < minDistance {
minDistance = dist
nearestDriver = d
}
}
if nearestDriver == nil {
return nil, errors.New("no available drivers")
}
return nearestDriver, nil
}
// ======================== Helper Functions ========================
func calculateDistance(loc1, loc2 Location) float64 {
// Simplified haversine calculation
latDiff := loc2.Lat - loc1.Lat
lngDiff := loc2.Lng - loc1.Lng
return math.Sqrt(latDiff*latDiff + lngDiff*lngDiff)
}
// ======================== Main Service ========================
type RideSharingService struct {
matcher *RideMatcher
rides map[string]*RideImpl
mu sync.RWMutex
pricing PricingStrategy
}
func NewRideSharingService() *RideSharingService {
strategy := &NearestDriverStrategy{}
matcher := NewRideMatcher(strategy)
matcher.Start()
return &RideSharingService{
matcher: matcher,
rides: make(map[string]*RideImpl),
pricing: &StandardPricing{
baseFare: 2.50,
perMileRate: 1.75,
perMinuteRate: 0.25,
},
}
}
func (s *RideSharingService) RequestRide(rider Rider, start, end Location) (Ride, error) {
ride := NewRide(generateID(), rider, start, end, s.pricing)
s.mu.Lock()
s.rides[ride.ID()] = ride
s.mu.Unlock()
s.matcher.rideRequests <- ride
return ride, nil
}
func generateID() string {
return fmt.Sprintf("%d", rand.Intn(1000000))
}
// ======================== Main Execution ========================
func main() {
service := NewRideSharingService()
// Create riders and drivers
rider1 := NewRider("rider-1", Location{37.7749, -122.4194})
driver1 := NewDriver("driver-1", Location{37.7755, -122.4182})
driver2 := NewDriver("driver-2", Location{37.7760, -122.4175})
// Add drivers to the system
service.matcher.AddDriver(driver1)
service.matcher.AddDriver(driver2)
// Simulate ride request
ride, _ := service.RequestRide(rider1,
Location{37.7749, -122.4194},
Location{37.7850, -122.4003},
)
// Wait for matching
time.Sleep(1 * time.Second)
// Check ride status
if ride.Status() >= DriverAssigned {
fmt.Printf("Ride %s matched with driver %s\n",
ride.ID(),
ride.(*RideImpl).driver.ID(),
)
}
// Simulate ride completion
ride.(*RideImpl).status = Completed
fmt.Printf("Total fare: $%.2f\n", ride.CalculateFare())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment