Machine Coding Problem

Parking Lot

maco30maco60macoAllutilityinheritanceenumsstrategy-pattern
Commonly Asked By:AmazonGoogleMicrosoftUberWalmart

Functional Scope (In-Scope)

  • Multi-Floor Parking: The system should support multiple floors with diverse spot types (Motorcycle, Compact, Large).
  • Spot Allocation: Match incoming vehicles automatically to their correct spot type (e.g., Cars in Compact/Large, Trucks only in Large).
  • Ticket Management: Generate a unique, timestamped ticket at entry gates and collect it at exit gates.
  • Fee Calculation: Pluggable hourly or flat-rate dynamic pricing based on total parked duration.
  • Payment Processing: Support cards, cash, or mobile payments with mock verification logic.

Explicit Boundaries (Out-of-Scope)

  • No Real Database Integration: All storage is managed in-memory (singleton orchestrators, local maps) to focus purely on object models.
  • No UI Layer: Interacted with via a simple programmatic test harness or terminal controller.
  • Simplified Hardware Integrations: Barrier arm gates, ticket printers, and display boards are represented by stateless interface mocks.

Below is a clean, multi-language codebase blueprint showcasing our design patterns and thread-safety strategies in Java and Python:

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

enum VehicleType { MOTORCYCLE, CAR, TRUCK }
enum SpotType { MOTORCYCLE, COMPACT, LARGE }

abstract class Vehicle {
    private final String licensePlate;
    private final VehicleType type;

    protected Vehicle(String licensePlate, VehicleType type) {
        this.licensePlate = licensePlate;
        this.type = type;
    }
    public String getLicensePlate() { return licensePlate; }
    public VehicleType getType() { return type; }
}

class Motorcycle extends Vehicle {
    public Motorcycle(String licensePlate) { super(licensePlate, VehicleType.MOTORCYCLE); }
}

class Car extends Vehicle {
    public Car(String licensePlate) { super(licensePlate, VehicleType.CAR); }
}

class Truck extends Vehicle {
    public Truck(String licensePlate) { super(licensePlate, VehicleType.TRUCK); }
}

class ParkingSpot {
    private final String id;
    private final SpotType type;
    private Vehicle currentVehicle;
    private boolean isFree = true;

    public ParkingSpot(String id, SpotType type) {
        this.id = id;
        this.type = type;
    }

    public synchronized boolean isAvailable() { return isFree; }

    public synchronized boolean reserve(Vehicle vehicle) {
        if (!isFree) return false;
        this.currentVehicle = vehicle;
        this.isFree = false;
        return true;
    }

    public synchronized void release() {
        this.currentVehicle = null;
        this.isFree = true;
    }

    public String getId() { return id; }
    public SpotType getType() { return type; }
    public Vehicle getCurrentVehicle() { return currentVehicle; }
}

class ParkingFloor {
    private final String name;
    private final List<ParkingSpot> spots = new CopyOnWriteArrayList<>();

    public ParkingFloor(String name) {
        this.name = name;
    }

    public void addSpot(ParkingSpot spot) {
        spots.add(spot);
    }

    public List<ParkingSpot> getSpots() { return spots; }
    public String getName() { return name; }
}

interface PricingStrategy {
    double calculateFee(long durationMs);
}

class HourlyPricing implements PricingStrategy {
    private final double hourlyRate;
    public HourlyPricing(double rate) { this.hourlyRate = rate; }

    @Override
    public double calculateFee(long durationMs) {
        double hours = Math.ceil(durationMs / 3600000.0);
        return Math.max(1.0, hours) * hourlyRate;
    }
}

class Ticket {
    private static final AtomicInteger counter = new AtomicInteger(1);
    private final String ticketId;
    private final Vehicle vehicle;
    private final ParkingSpot spot;
    private final long entryTime;
    private long exitTime;
    private double fee;
    private boolean isPaid;

    public Ticket(Vehicle vehicle, ParkingSpot spot) {
        this.ticketId = "TKT-" + counter.getAndIncrement();
        this.vehicle = vehicle;
        this.spot = spot;
        this.entryTime = System.currentTimeMillis();
    }

    public String getTicketId() { return ticketId; }
    public Vehicle getVehicle() { return vehicle; }
    public ParkingSpot getSpot() { return spot; }
    public long getEntryTime() { return entryTime; }
    public long getExitTime() { return exitTime; }
    public void setExitTime(long exitTime) { this.exitTime = exitTime; }
    public double getFee() { return fee; }
    public void setFee(double fee) { this.fee = fee; }
    public boolean isPaid() { return isPaid; }
    public void pay() { this.isPaid = true; }
}

enum PaymentType { CASH, CARD, UPI }

abstract class Payment {
    private final String transactionId;
    protected double amount;

    protected Payment(double amount) {
        this.transactionId = "TXN-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
        this.amount = amount;
    }

    public abstract boolean process();
    public String getTransactionId() { return transactionId; }
}

class CashPayment extends Payment {
    public CashPayment(double amount) { super(amount); }
    @Override
    public boolean process() {
        System.out.printf("[Cash] Processed payment of $%.2f via transaction: %s\n", amount, getTransactionId());
        return true;
    }
}

class CardPayment extends Payment {
    public CardPayment(double amount) { super(amount); }
    @Override
    public boolean process() {
        System.out.printf("[Card] Processed payment of $%.2f via transaction: %s\n", amount, getTransactionId());
        return true;
    }
}

class UPIPayment extends Payment {
    public UPIPayment(double amount) { super(amount); }
    @Override
    public boolean process() {
        System.out.printf("[UPI] Processed payment of $%.2f via transaction: %s\n", amount, getTransactionId());
        return true;
    }
}

class PaymentFactory {
    public static Payment createPayment(PaymentType type, double amount) {
        switch (type) {
            case CASH: return new CashPayment(amount);
            case CARD: return new CardPayment(amount);
            case UPI: return new UPIPayment(amount);
            default: throw new IllegalArgumentException("Unknown payment type: " + type);
        }
    }
}

class ParkingLot {
    private static volatile ParkingLot instance;
    private final String name;
    private final List<ParkingFloor> floors = new CopyOnWriteArrayList<>();
    private final ConcurrentHashMap<String, Ticket> activeTickets = new ConcurrentHashMap<>();
    private PricingStrategy pricingStrategy = new HourlyPricing(5.0);

    private ParkingLot(String name) {
        this.name = name;
    }

    public static ParkingLot getInstance(String name) {
        if (instance == null) {
            synchronized (ParkingLot.class) {
                if (instance == null) {
                    instance = new ParkingLot(name);
                }
            }
        }
        return instance;
    }

    public void addFloor(ParkingFloor floor) {
        floors.add(floor);
    }

    public void setPricingStrategy(PricingStrategy strategy) {
        this.pricingStrategy = strategy;
    }

    private boolean isSpotCompatible(VehicleType vehicleType, SpotType spotType) {
        switch (vehicleType) {
            case MOTORCYCLE: return spotType == SpotType.MOTORCYCLE || spotType == SpotType.COMPACT || spotType == SpotType.LARGE;
            case CAR: return spotType == SpotType.COMPACT || spotType == SpotType.LARGE;
            case TRUCK: return spotType == SpotType.LARGE;
            default: return false;
        }
    }

    public synchronized Ticket parkVehicle(Vehicle vehicle) {
        for (ParkingFloor floor : floors) {
            for (ParkingSpot spot : floor.getSpots()) {
                if (spot.isAvailable() && isSpotCompatible(vehicle.getType(), spot.getType())) {
                    if (spot.reserve(vehicle)) {
                        Ticket ticket = new Ticket(vehicle, spot);
                        activeTickets.put(ticket.getTicketId(), ticket);
                        System.out.printf("[ParkingSuccess] %s (%s) parked at spot %s on floor %s. Ticket: %s\n",
                                vehicle.getType(), vehicle.getLicensePlate(), spot.getId(), floor.getName(), ticket.getTicketId());
                        return ticket;
                    }
                }
            }
        }
        System.out.printf("[ParkingFailed] No compatible spot available for %s (%s)\n", vehicle.getType(), vehicle.getLicensePlate());
        return null;
    }

    public synchronized boolean checkoutVehicle(String ticketId, PaymentType paymentType) {
        Ticket ticket = activeTickets.get(ticketId);
        if (ticket == null) {
            System.out.println("[CheckoutError] Ticket ID " + ticketId + " not found.");
            return false;
        }

        ticket.setExitTime(System.currentTimeMillis() + 7200000); // Mocks 2 hours parking
        long duration = ticket.getExitTime() - ticket.getEntryTime();
        double fee = pricingStrategy.calculateFee(duration);
        ticket.setFee(fee);

        Payment payment = PaymentFactory.createPayment(paymentType, fee);
        if (payment.process()) {
            ticket.pay();
            ticket.getSpot().release();
            activeTickets.remove(ticketId);
            System.out.printf("[CheckoutSuccess] Ticket %s resolved. Fee: $%.2f. Spot %s is vacant.\n",
                    ticketId, fee, ticket.getSpot().getId());
            return true;
        }
        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        ParkingLot lot = ParkingLot.getInstance("Grand Arena");
        ParkingFloor f1 = new ParkingFloor("Floor 1");
        f1.addSpot(new ParkingSpot("F1-M1", SpotType.MOTORCYCLE));
        f1.addSpot(new ParkingSpot("F1-C1", SpotType.COMPACT));
        f1.addSpot(new ParkingSpot("F1-L1", SpotType.LARGE));
        lot.addFloor(f1);

        Vehicle car1 = new Car("KA-01-AB-1234");
        Vehicle moto1 = new Motorcycle("KA-01-XYZ-567");

        Ticket t1 = lot.parkVehicle(car1);
        Ticket t2 = lot.parkVehicle(moto1);

        lot.checkoutVehicle(t1.getTicketId(), PaymentType.UPI);
        lot.checkoutVehicle(t2.getTicketId(), PaymentType.CARD);
    }
}