Machine Coding Problem

Elevator System

maco30maco60macoAllinfrastructurestatestrategy-(dispatching)
Commonly Asked By:GoogleAmazonMicrosoftUber

Functional Scope (In-Scope)

  • N Floors & M Elevators: Support customizable scale, controlling multiple distinct elevator cabins traversing standard floor heights.
  • Dual-Request Ingestion: Process external hall calls (floor + direction button pressed outside) and internal cabin calls (target floor button pressed inside).
  • Minimized Wait Times: Swappable dispatch strategies to minimize elevator distance and passenger wait cycles.
  • Dynamic Stop Sorting (SCAN): Cabins should process floors efficiently on their path (e.g. stopping for floor 3 when moving from 0 to 5) before switching direction.

Explicit Boundaries (Out-of-Scope)

  • No Weight Sensors / Hardware Alarms: Omit physical cabin components (brakes, cable tension, overload warning lights).
  • No Interactive Floor Displays: UI feedback is aggregated as status prints in the controller event tick loop.

Clean reference designs showcasing operational patterns and thread-safety in Java and Python:

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

enum Direction { UP, DOWN, IDLE }

class Request {
    private final int targetFloor;
    private final Direction direction;

    public Request(int targetFloor, Direction direction) {
        this.targetFloor = targetFloor;
        this.direction = direction;
    }
    public int getTargetFloor() { return targetFloor; }
    public Direction getDirection() { return direction; }
}

class Elevator {
    private final int id;
    private int currentFloor = 0;
    private Direction direction = Direction.IDLE;
    private final TreeSet<Integer> upRequests = new TreeSet<>();
    private final TreeSet<Integer> downRequests = new TreeSet<>();

    public Elevator(int id) {
        this.id = id;
    }

    public int getId() { return id; }
    public synchronized int getCurrentFloor() { return currentFloor; }
    public synchronized Direction getDirection() { return direction; }

    public synchronized void addRequest(int floor) {
        if (floor > currentFloor) {
            upRequests.add(floor);
            if (direction == Direction.IDLE) {
                direction = Direction.UP;
            }
        } else if (floor < currentFloor) {
            downRequests.add(floor);
            if (direction == Direction.IDLE) {
                direction = Direction.DOWN;
            }
        } else {
            System.out.printf("[Elevator %d] Already at floor %d. Doors open.\n", id, floor);
        }
    }

    public synchronized void step() {
        if (direction == Direction.UP) {
            if (!upRequests.isEmpty()) {
                currentFloor++;
                System.out.printf("[Elevator %d] Moving UP: reached floor %d\n", id, currentFloor);
                if (upRequests.contains(currentFloor)) {
                    upRequests.remove(currentFloor);
                    System.out.printf("[Elevator %d] STOPPED at floor %d. Doors open/close.\n", id, currentFloor);
                }
                if (upRequests.isEmpty()) {
                    direction = downRequests.isEmpty() ? Direction.IDLE : Direction.DOWN;
                }
            } else {
                direction = downRequests.isEmpty() ? Direction.IDLE : Direction.DOWN;
            }
        } else if (direction == Direction.DOWN) {
            if (!downRequests.isEmpty()) {
                currentFloor--;
                System.out.printf("[Elevator %d] Moving DOWN: reached floor %d\n", id, currentFloor);
                if (downRequests.contains(currentFloor)) {
                    downRequests.remove(currentFloor);
                    System.out.printf("[Elevator %d] STOPPED at floor %d. Doors open/close.\n", id, currentFloor);
                }
                if (downRequests.isEmpty()) {
                    direction = upRequests.isEmpty() ? Direction.IDLE : Direction.UP;
                }
            } else {
                direction = upRequests.isEmpty() ? Direction.IDLE : Direction.UP;
            }
        }
    }

    public synchronized boolean hasRequests() {
        return !upRequests.isEmpty() || !downRequests.isEmpty();
    }
}

interface DispatchStrategy {
    Elevator selectElevator(List<Elevator> elevators, Request request);
}

class OptimalDispatchStrategy implements DispatchStrategy {
    @Override
    public Elevator selectElevator(List<Elevator> elevators, Request request) {
        Elevator best = null;
        int minCost = Integer.MAX_VALUE;

        for (Elevator e : elevators) {
            int cost = calculateCost(e, request);
            if (cost < minCost) {
                minCost = cost;
                best = e;
            }
        }
        return best != null ? best : elevators.get(0);
    }

    private int calculateCost(Elevator e, Request r) {
        int currentFloor = e.getCurrentFloor();
        Direction dir = e.getDirection();
        int target = r.getTargetFloor();

        if (dir == Direction.IDLE) {
            return Math.abs(currentFloor - target);
        } else if (dir == Direction.UP && target >= currentFloor && r.getDirection() == Direction.UP) {
            return target - currentFloor;
        } else if (dir == Direction.DOWN && target <= currentFloor && r.getDirection() == Direction.DOWN) {
            return currentFloor - target;
        } else {
            return Math.abs(currentFloor - target) + 100;
        }
    }
}

class ElevatorController {
    private final List<Elevator> elevators;
    private final DispatchStrategy strategy;

    public ElevatorController(int elevatorCount, DispatchStrategy strategy) {
        this.elevators = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= elevatorCount; i++) {
            elevators.add(new Elevator(i));
        }
        this.strategy = strategy;
    }

    public void requestElevator(int floor, Direction direction) {
        Request req = new Request(floor, direction);
        Elevator selected = strategy.selectElevator(elevators, req);
        System.out.printf("[Controller] Dispatched Elevator %d to floor %d (%s)\n", selected.getId(), floor, direction);
        selected.addRequest(floor);
    }

    public void step() {
        for (Elevator e : elevators) {
            e.step();
        }
    }

    public boolean hasActiveRequests() {
        for (Elevator e : elevators) {
            if (e.hasRequests()) return true;
        }
        return false;
    }

    public List<Elevator> getElevators() { return elevators; }
}

public class Main {
    public static void main(String[] args) {
        ElevatorController controller = new ElevatorController(2, new OptimalDispatchStrategy());
        controller.requestElevator(3, Direction.UP);
        controller.requestElevator(7, Direction.DOWN);

        int ticks = 0;
        while (controller.hasActiveRequests() && ticks < 15) {
            System.out.printf("--- Tick %d ---\n", ++ticks);
            controller.step();
        }
    }
}