eval({ init: function(elevators, floors) { // Get elevator/building info let elevatorCount = elevators.length; let floorCount = floors.length; let topFloor = floorCount - 1; let bottomFloor = 0; console.clear(); console.log("Starting (" + floorCount + " floors; " + elevatorCount + " elevator" + ((floorCount == 1) ? "s" : "") +")..."); // Direction constants const UP = 0; const DOWN = 1; const IDLE = 2; // Data structure for floor requests let requests = [new Set(), new Set()]; // ---- // Functions // ---- // getNextFloor chooses the best destination for an elevator // when given the index of the elevator, along with all // destinations, requests, and rest floors. function getNextFloor(elevators, elevatorIndex, destinations, requests, restFloors) { let elevator = elevators[elevatorIndex]; let floorNum = restFloors[1]; if (requests[UP].size == 0 && requests[DOWN].size == 0 && destinations.size == 0) { // No people; send to rest floor } else if (destinations.size == 0) { // No destinations; send to pick up passengers try { floorNum = getClosestFloor(elevator.currentFloor(), new Set([...requests[UP], ...requests[DOWN]])); } catch (e) { console.log("ERROR: Requests set was empty"); } } else { // Prioritize dropping off current passengers try { floorNum = getClosestFloor(elevator.currentFloor(), destinations); } catch (e) { console.log("ERROR: Destinations set was empty"); } } return floorNum; } // getClosestFloor takes a floor, a list of floors, an optional // list of ignored floors, and an optional direction. It // returns the closest floor from that list in the given // direction that is not part of the ignored floors list. function getClosestFloor(currentFloor, floorList, ignoredFloors = new Set(), direction = IDLE) { let closestFloor = currentFloor; let distance = floorCount; let newDistance = distance; floorList.forEach(function(floorNum) { switch (direction) { case UP: newDistance = floorNum - currentFloor; break; case DOWN: newDistance = currentFloor - floorNum; break; case IDLE: newDistance = Math.abs(floorNum - currentFloor); } if (newDistance < distance && newDistance > 0 && !(ignoredFloors.has(floorNum))) { closestFloor = floorNum; distance = newDistance; } }); if (closestFloor != currentFloor) { return closestFloor; } else { throw new Error("No floor in selected direction"); } } // getClosestElevator takes a floor number and returns the // index of the closest elevator. function getClosestElevator(floorNum) { let closestElevator = 0; let distance = floorCount; let newDistance = distance; elevators.forEach(function (elevator, elevatorIndex) { newDistance = Math.abs(elevator.currentFloor() - floorNum); if (newDistance < distance) { distance = newDistance; closestElevator = elevatorIndex; } }); //console.log("Closest is elevator[" + closestElevator + "]") return closestElevator; } // getDirection get the direction (up or down) that an elevator // will need to travel to get to a specified floor function getDirection(currentFloor, floorNum) { let distance = currentFloor - floorNum; if (distance > 0 || currentFloor == topFloor) { return DOWN; } else if (distance < 0 || currentFloor == 0) { return UP; } else { return IDLE; } } // setDirection changes the indicator lights on a specified // elevator to match the given direction. function setDirection(elevator, direction) { switch (direction) { case UP: elevator.goingUpIndicator(true); elevator.goingDownIndicator(false); break; case DOWN: elevator.goingUpIndicator(false); elevator.goingDownIndicator(true); break; default: elevator.goingUpIndicator(true); elevator.goingDownIndicator(true); } } // ---- // Floors // ---- floors.forEach(function (floor) { floor.on("up_button_pressed", function() { //console.log("> Up request on floor[" + floor.level + "]"); requests[UP].add(floor.level); }); floor.on("down_button_pressed", function() { //console.log("> Down request on floor[" + floor.level + "]"); requests[DOWN].add(floor.level); }); }); // ---- // Elevators // ---- elevators.forEach(function (elevator, elevatorIndex) { // Elevator info and variables let destinations = new Set(); const restFloors = [ elevatorIndex * Math.floor(floorCount / (elevatorCount + 1)), (elevatorIndex + 1) * Math.floor(floorCount / (elevatorCount + 1)), (elevatorIndex + 2) * Math.floor(floorCount / (elevatorCount + 1)) ]; // Next primary stop let nextFloor = restFloors[0]; // Start setDirection(elevator, UP); // Starts on the ground floor, must be going up console.log("> Elevator[" + elevatorIndex + "] rest floors are " + restFloors[0] + ", " + restFloors[1] + ", and " + restFloors[2] + ". Starting at " + restFloors[0] + "."); elevator.goToFloor(nextFloor); // Nearest rest floor // Idle elevator.on("idle", function() { //console.log("> Elevator[" + elevatorIndex + "] is idle") nextFloor = getNextFloor(elevators, elevatorIndex, destinations, requests, restFloors); elevator.goToFloor(nextFloor); }); // Passing elevator.on("passing_floor", function(floorNum, direction) { if (destinations.has(floorNum) || (elevator.loadFactor() < 1 && elevator.destinationDirection() == "up" && requests[UP].has(floorNum)) || (elevator.loadFactor() < 1 && elevator.destinationDirection() == "down" && requests[DOWN].has(floorNum))) { elevator.goToFloor(floorNum, true); } }); // Buttons elevator.on("floor_button_pressed", function(floorNum) { //console.log("> Pressed floor[" + floorNum + "] on elevator[" + elevatorIndex + "]"); destinations.add(floorNum); }); // Stopped elevator.on("stopped_at_floor", function(floorNum) { //console.log("> elevator[" + elevatorIndex + "] stopped at floor[" + floorNum + "]"); setDirection(elevator, getDirection(elevator.currentFloor(), nextFloor)); destinations.delete(floorNum); if (elevator.goingUpIndicator() == true) { requests[UP].delete(floorNum); } if (elevator.goingDownIndicator() == true) { requests[DOWN].delete(floorNum); } }); }); }, update: function(dt, elevators, floors) {} });