Skip to content

Instantly share code, notes, and snippets.

@dcoxall
Created May 21, 2024 07:58
Show Gist options
  • Save dcoxall/e5d63daeb1054634422db802c054965a to your computer and use it in GitHub Desktop.
Save dcoxall/e5d63daeb1054634422db802c054965a to your computer and use it in GitHub Desktop.
{
init: (elevators, floors) => {
// Stores the elevators that are currently idle
const idleElevators = new Set();
// Alternative floor model
const floorStates = floors.map((floor) => {
return { floorNum: floor.floorNum(), up: false, down: false, upIncoming: false, downIncoming: false };
});
const setElevatorIndicators = (elevator, direction = null) => {
const dir = direction || elevator.destinationDirection();
if (dir == "up") {
elevator.goingUpIndicator(true);
elevator.goingDownIndicator(false);
} else if (dir == "down") {
elevator.goingUpIndicator(false);
elevator.goingDownIndicator(true);
} else {
elevator.goingUpIndicator(true);
elevator.goingDownIndicator(true);
}
};
// When someone requests an elevator this function is called
const onCallElevator = (floor, direction) => {
// Locate the nearest idle elevator
const floorNum = floor.floorNum();
const idles = Array.from(idleElevators.values());
const [elevator, _] = idles.reduce(([previousElevator, previousDistance], elevator) => {
const currentDistance = Math.abs(elevator.currentFloor() - floorNum);
return currentDistance < previousDistance ?
[elevator, currentDistance] :
[previousElevator, previousDistance];
}, [null, Infinity]);
// Once one is found we mark it as no-longer idle and call it to the floor
if (elevator) {
idleElevators.delete(elevator);
elevator.goToFloor(floorNum);
setElevatorIndicators(elevator);
floorStates[floorNum][direction] = true;
floorStates[floorNum][`${direction}Incoming`] = true;
} else {
floorStates[floorNum][direction] = true;
floorStates[floorNum][`${direction}Incoming`] = false;
}
};
const locateNearestPerson = (elevator) => {
const waiting = floorStates.reduce((acc, floorState) => {
if ((floorState.up && !floorState.upIncoming) || (floorState.down && !floorState.downIncoming)) {
return [...acc, floorState.floorNum];
}
return acc;
}, []);
const elevatorFloorNum = elevator.currentFloor();
const [floorNum, _] = waiting.reduce(([floorNum, distance], currentFloorNum) => {
const currentDistance = Math.abs(elevatorFloorNum - currentFloorNum);
if (distance === Infinity || currentDistance < distance)
return [currentFloorNum, currentDistance];
return [floorNum, distance];
}, [null, Infinity]);
return floorNum;
};
// When someone selects a destination we need to make sure that we visit them in order
// and so we can't simply append to the queue. It is better to append and sort the queue
// based on the direction of travel.
const onDestinationPress = (elevator, floorNum) => {
let destinationQueue = [...elevator.destinationQueue, floorNum];
// If we are going up then we should sort smallest to largest
if (elevator.goingUpIndicator()) {
destinationQueue.sort((a, b) => a - b);
floorStates[floorNum].upIncoming = true;
} else if (elevator.goingDownIndicator()) {
destinationQueue.sort((a, b) => b - a);
floorStates[floorNum].downIncoming = true;
}
elevator.destinationQueue = destinationQueue;
elevator.checkDestinationQueue();
};
const onIdle = (elevator) => {
const floorNum = locateNearestPerson(elevator);
if (floorNum === null) {
idleElevators.add(elevator);
return;
}
elevator.goToFloor(floorNum);
setElevatorIndicators(elevator);
const direction = elevator.destinationDirection();
floorStates[floorNum][`${direction}Incoming`] = true;
};
const onStop = (elevator, floorNum) => {
// First we let others know that we are no longer incoming as we have arrived
// and also clear any recorded passengers at this floor based on our current
// travel direction
if (elevator.goingUpIndicator()) {
floorStates[floorNum].upIncoming = false;
floorStates[floorNum].up = false;
}
if (elevator.goingDownIndicator()) {
floorStates[floorNum].downIncoming = false;
floorStates[floorNum].down = false;
}
// Now we need to determine if we are changing direction. This would
// happen if we have no more destinations.
if (elevator.destinationQueue.length === 0) {
// We now check if this floor has people going up or down
if (floorStates[floorNum].up) {
setElevatorIndicators(elevator, "up");
floorStates[floorNum].up = false;
return // early return so we don't take both up and down
}
if (floorStates[floorNum].down) {
setElevatorIndicators(elevator, "down");
floorStates[floorNum].down = false;
return
}
}
// Finally if we are on the top or bottom floor we need to change direction
if (floorNum === 0)
setElevatorIndicators(elevator, "up");
if (floorNum === floorStates.at(-1).floorNum)
setElevatorIndicators(elevator, "down");
};
const onPassingFloor = (elevator, floorNum, direction) => {
// We can only stop if we are not at capacity
if (elevator.loadFactor() > 0.7)
return;
// Otherwise we can check this floor for passengers
if (floorStates[floorNum][direction] && !floorStates[floorNum][`${direction}Incoming`]) {
elevator.goToFloor(floorNum, true);
// If we are planning to stop we tell others that we are incoming
floorStates[floorNum][`${direction}Incoming`] = true;
return;
}
};
floors.forEach(floor => {
floor.on("up_button_pressed", onCallElevator.bind(this, floor, "up"));
floor.on("down_button_pressed", onCallElevator.bind(this, floor, "down"));
});
elevators.forEach(elevator => {
elevator.on("floor_button_pressed", onDestinationPress.bind(this, elevator));
elevator.on("idle", onIdle.bind(this, elevator));
elevator.on("stopped_at_floor", onStop.bind(this, elevator));
elevator.on("passing_floor", onPassingFloor.bind(this, elevator));
});
},
update: (dt, elevators, floors) => {
// We normally don't need to do anything here
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment