Machine Coding Problem

Bike Sharing System

macoAllmarketplaceiot-locktrip-lifecycle
Commonly Asked By:LimeBirdLyft

Functional Scope (In-Scope)

  • Physical IoT Unlock Cycles: Validates bike states atomically, triggers virtual hardware lock controllers, and tracks trip beginnings.
  • Hybrid Dock/Dockless Parking Validators: Enforces returns within either physical station slots or virtual geofenced circles.
  • Adaptive Per-Minute Billing Engine: Calculates trip cost base rates, running duration minutes, and caps charges at a daily maximum.
  • Stateful Trip Management: Restricts users to one active bike booking at a time and logs completed coordinates.

Explicit Boundaries (Out-of-Scope)

  • Dynamic Maintenance Dispatching: Focuses on on-trip state changes rather than rebalancing truck routing algorithms.
  • Real-time GPS Tracking Channels: Excludes handling WebSockets or telemetry data feeds to focus on start/end coordinate validation.

Production reference implementations demonstrating IoT lock integrations, geofenced boundaries, and duration billing calculators in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

enum BikeStatus {
    AVAILABLE, IN_USE, MAINTENANCE
}

enum TripStatus {
    ACTIVE, COMPLETED, UNPAID
}

class Point {
    private final double lat;
    private final double lng;

    public Point(double lat, double lng) {
        this.lat = lat;
        this.lng = lng;
    }

    public double getLat() { return lat; }
    public double getLng() { return lng; }

    public double distanceTo(Point other) {
        double R = 6371000.0; // Earth's radius in meters
        double latDist = Math.toRadians(other.lat - this.lat);
        double lngDist = Math.toRadians(other.lng - this.lng);
        double a = Math.sin(latDist / 2) * Math.sin(latDist / 2)
                + Math.cos(Math.toRadians(this.lat)) * Math.cos(Math.toRadians(other.lat))
                * Math.sin(lngDist / 2) * Math.sin(lngDist / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
    }
}

class Bike {
    private final String bikeId;
    private final AtomicReference<BikeStatus> status = new AtomicReference<>(BikeStatus.AVAILABLE);
    private volatile Point location;
    private volatile String currentDockId = null;

    public Bike(String bikeId, Point initialLocation) {
        this.bikeId = bikeId;
        this.location = initialLocation;
    }

    public String getBikeId() { return bikeId; }
    public BikeStatus getStatus() { return status.get(); }
    public boolean compareAndSetStatus(BikeStatus expect, BikeStatus update) {
        return status.compareAndSet(expect, update);
    }
    public void setStatus(BikeStatus s) { status.set(s); }
    public Point getLocation() { return location; }
    public void setLocation(Point location) { this.location = location; }
    public String getCurrentDockId() { return currentDockId; }
    public void setCurrentDockId(String currentDockId) { this.currentDockId = currentDockId; }
}

class Dock {
    private final String dockId;
    private final Point location;
    private final int capacity;
    private int occupiedSlots = 0;

    public Dock(String dockId, Point location, int capacity) {
        this.dockId = dockId;
        this.location = location;
        this.capacity = capacity;
    }

    public synchronized boolean hasAvailableSlots() {
        return occupiedSlots < capacity;
    }

    public synchronized boolean incrementOccupied() {
        if (occupiedSlots < capacity) {
            occupiedSlots++;
            return true;
        }
        return false;
    }

    public synchronized void decrementOccupied() {
        if (occupiedSlots > 0) {
            occupiedSlots--;
        }
    }

    public String getDockId() { return dockId; }
    public Point getLocation() { return location; }
    public int getCapacity() { return capacity; }
    public synchronized int getOccupiedSlots() { return occupiedSlots; }
}

class ParkingZone {
    private final String zoneId;
    private final Point center;
    private final double radiusMeters;

    public ParkingZone(String zoneId, Point center, double radiusMeters) {
        this.zoneId = zoneId;
        this.center = center;
        this.radiusMeters = radiusMeters;
    }

    public boolean contains(Point point) {
        return center.distanceTo(point) <= radiusMeters;
    }

    public String getZoneId() { return zoneId; }
}

class Trip {
    private final String tripId;
    private final String userId;
    private final String bikeId;
    private final long startTimeMs;
    private final Point startLocation;
    private volatile long endTimeMs = 0L;
    private volatile Point endLocation = null;
    private volatile double fare = 0.0;
    private volatile TripStatus status = TripStatus.ACTIVE;

    public Trip(String tripId, String userId, String bikeId, Point startLocation) {
        this.tripId = tripId;
        this.userId = userId;
        this.bikeId = bikeId;
        this.startTimeMs = System.currentTimeMillis();
        this.startLocation = startLocation;
    }

    public void completeTrip(Point endLocation, double fare) {
        this.endTimeMs = System.currentTimeMillis();
        this.endLocation = endLocation;
        this.fare = fare;
        this.status = TripStatus.COMPLETED;
    }

    public String getTripId() { return tripId; }
    public String getUserId() { return userId; }
    public String getBikeId() { return bikeId; }
    public long getStartTimeMs() { return startTimeMs; }
    public Point getStartLocation() { return startLocation; }
    public long getEndTimeMs() { return endTimeMs; }
    public Point getEndLocation() { return endLocation; }
    public double getFare() { return fare; }
    public TripStatus getStatus() { return status; }
}

class BillingEngine {
    private static final double BASE_FEE = 2.0;
    private static final double RATE_PER_MINUTE = 0.15;
    private static final double DAILY_MAX_CAP = 20.0;

    public static double calculateFare(long startTimeMs, long endTimeMs) {
        long durationMs = endTimeMs - startTimeMs;
        if (durationMs < 0) durationMs = 0;
        
        double durationMinutes = durationMs / 60000.0;
        double charge = BASE_FEE + (durationMinutes * RATE_PER_MINUTE);
        return Math.min(charge, DAILY_MAX_CAP);
    }
}

class LockController {
    // Simulates an IoT Lock gateway unlock command
    public boolean sendUnlockSignal(String bikeId) {
        System.out.println("IOT SIGNAL -> Unlocking Bike: " + bikeId);
        return true; // Return successful hardware feedback
    }

    public boolean sendLockSignal(String bikeId) {
        System.out.println("IOT SIGNAL -> Locking Bike: " + bikeId);
        return true;
    }
}

class BikeSharingService {
    private final ConcurrentHashMap<String, Bike> bikes = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Dock> docks = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, ParkingZone> geofences = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Trip> activeTrips = new ConcurrentHashMap<>(); // userId -> Trip
    private final ConcurrentHashMap<String, List<Trip>> userHistory = new ConcurrentHashMap<>();
    
    private final LockController lockController = new LockController();

    public void registerBike(Bike bike) {
        bikes.put(bike.getBikeId(), bike);
    }

    public void registerDock(Dock dock) {
        docks.put(dock.getDockId(), dock);
    }

    public void registerParkingZone(ParkingZone zone) {
        geofences.put(zone.getZoneId(), zone);
    }

    // Dynamic Unlock Sequence
    public Trip unlockBike(String userId, String bikeId) {
        Bike bike = bikes.get(bikeId);
        if (bike == null) throw new IllegalArgumentException("Bike does not exist!");

        if (activeTrips.containsKey(userId)) {
            throw new IllegalStateException("User already has an active trip!");
        }

        // Atomically transitions bike state
        if (!bike.compareAndSetStatus(BikeStatus.AVAILABLE, BikeStatus.IN_USE)) {
            throw new IllegalStateException("Bike is not available for booking!");
        }

        // Dispatch hardware signal
        boolean hardwareSuccess = lockController.sendUnlockSignal(bikeId);
        if (!hardwareSuccess) {
            bike.setStatus(BikeStatus.AVAILABLE); // Rollback state
            throw new RuntimeException("Hardware failed to unlock bike!");
        }

        // Release occupied slot if bike was in a dock
        if (bike.getCurrentDockId() != null) {
            Dock dock = docks.get(bike.getCurrentDockId());
            if (dock != null) {
                dock.decrementOccupied();
            }
            bike.setCurrentDockId(null);
        }

        String tripId = UUID.randomUUID().toString();
        Trip trip = new Trip(tripId, userId, bikeId, bike.getLocation());
        activeTrips.put(userId, trip);

        System.out.println("TRIP STARTED -> User: " + userId + " | Trip: " + tripId + " | Bike: " + bikeId);
        return trip;
    }

    // Dynamic Return & End Trip Sequence
    public void lockBike(String userId, Point returnLocation) {
        Trip trip = activeTrips.get(userId);
        if (trip == null) {
            throw new IllegalStateException("No active trip found for user!");
        }

        String bikeId = trip.getBikeId();
        Bike bike = bikes.get(bikeId);
        if (bike == null) return;

        // Verify IoT lock feedback
        boolean hardwareSuccess = lockController.sendLockSignal(bikeId);
        if (!hardwareSuccess) {
            throw new RuntimeException("Hardware failed to confirm lock status!");
        }

        // Parking zone verification (Dock or Dockless geofence)
        boolean validParking = false;
        String dockIdMatched = null;

        // 1. Check if parked at a dock
        for (Dock dock : docks.values()) {
            if (dock.getLocation().distanceTo(returnLocation) <= 15.0) { // Within 15 meters
                if (dock.incrementOccupied()) {
                    validParking = true;
                    dockIdMatched = dock.getDockId();
                    break;
                }
            }
        }

        // 2. Check if parked inside a dockless geofence zone
        if (!validParking) {
            for (ParkingZone zone : geofences.values()) {
                if (zone.contains(returnLocation)) {
                    validParking = true;
                    break;
                }
            }
        }

        if (!validParking) {
            System.out.println("OUT-OF-BOUNDS WARNING -> User parked outside permitted zone. Applying surcharge penalty!");
            // Surcharges could be applied to fare calculations here
        }

        // Finalize state updates
        long now = System.currentTimeMillis();
        double fare = BillingEngine.calculateFare(trip.getStartTimeMs(), now);

        trip.completeTrip(returnLocation, fare);
        bike.setLocation(returnLocation);
        bike.setCurrentDockId(dockIdMatched);
        bike.setStatus(BikeStatus.AVAILABLE);

        activeTrips.remove(userId);
        userHistory.computeIfAbsent(userId, k -> new CopyOnWriteArrayList<>()).add(trip);

        System.out.println("TRIP CONCLUDED -> User: " + userId + " | Fare: $" + fare + " | Parked Dock: " + (dockIdMatched != null ? dockIdMatched : "Dockless Zone"));
    }

    public List<Trip> getUserHistory(String userId) {
        return userHistory.getOrDefault(userId, Collections.emptyList());
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("=== JAVA BIKE SHARING SYSTEM SIMULATION ===");
        BikeSharingService service = new BikeSharingService();

        Point startLoc = new Point(40.7128, -74.0060);
        Bike bike1 = new Bike("bike-101", startLoc);
        service.registerBike(bike1);

        Dock dock1 = new Dock("dock-001", new Point(40.7129, -74.0061), 5);
        dock1.incrementOccupied(); // bike1 is in this dock initially
        bike1.setCurrentDockId("dock-001");
        service.registerDock(dock1);

        ParkingZone zone1 = new ParkingZone("zone-south", new Point(40.7100, -74.0090), 100.0);
        service.registerParkingZone(zone1);

        // Unlock
        Trip trip = service.unlockBike("user-1", "bike-101");
        
        Thread.sleep(100); // simulate a tiny delay
        
        // Return location (near the parking zone)
        Point endLoc = new Point(40.7102, -74.0091);
        service.lockBike("user-1", endLoc);

        System.out.println("User trip count: " + service.getUserHistory("user-1").size());
        System.out.println("=== END OF JAVA SIMULATION ===");
    }
}