Created
May 21, 2024 07:58
-
-
Save dcoxall/e5d63daeb1054634422db802c054965a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
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