Skip to content

Instantly share code, notes, and snippets.

@amok
Created June 15, 2021 13:12
Show Gist options
  • Select an option

  • Save amok/da3359ab3afec3c2cd1ee0e2a7e6be9f to your computer and use it in GitHub Desktop.

Select an option

Save amok/da3359ab3afec3c2cd1ee0e2a7e6be9f to your computer and use it in GitHub Desktop.
const DIRECTION_DOWN = -1;
const DIRECTION_NONE = 0;
const DIRECTION_UP = 1;
function HardwareElevator() {}
HardwareElevator.prototype = {
moveUp: function() { console.log("up") },
moveDown: function() { console.log("down") },
stopAndOpenDoors: function() { console.log("stop and open") },
getCurrentFloor: function() {},
getCurrentDirection: function() {}
};
function Elevator() {
this.hw = new HardwareElevator();
this.hw.on("doorsClosed", _bind(this.onDoorsClosed, this));
this.hw.on("beforeFloor", _bind(this.onBeforeFloor, this));
this.hw.on("floorButtonPressed", _bind(this.onFloorButtonPressed, this));
this.hw.on("cabinButtonPressed", _bind(this.onCabinButtonPressed, this));
}
Elevator.prototype = {
onDoorsClosed: function() {
this.moveElevatorOnIfNecessary();
},
onBeforeFloor: function() {
this.stopElevatorOnThisFloorIfNecessary();
},
onFloorButtonPressed: function(floor, direction) {
this.enqueueFloor(floor, direction);
},
onCabinButtonPressed: function(floor) {
this.enqueueFloor(floor);
}
//////////////////////////////////////////////////////////////////////////////
stopElevatorOnThisFloorIfNecessary() {
const floor = this.hw.getCurrentFloor() + 1;
if (!this.getRequestedFloors().includes(floor)) {
return;
}
this.stopElevatorFloorIfNecessary(floor);
}
stopElevatorFloorIfNecessary(floor) {
const command = this[floor];
const isSameDirection = command === this.hw.getCurrentDirection();
const shouldVisitFloor = command === DIRECTION_NONE;
const isElevatorStopped = this.hw.getCurrentDirection() === DIRECTION_NONE;
if (isElevatorStopped) {
// open doors and "unrequest" the floor
// we assume that this.hw.stopAndOpenDoors is idempotent and if
// we try to "stop" already stopped elevator it doesn't explode :)
this.hw.stopAndOpenDoors();
delete this[floor];
} else if (shouldVisitFloor || isSameDirection) {
this.lastElevatorDirection = this.hw.getCurrentDirection();
this.hw.stopAndOpenDoors();
delete this[floor];
} else if (!isSameDirection) {
// pick up on the way back
this[floor] = DIRECTION_NONE;
}
}
getRequestedFloors() {
return Object.keys(this).filter(isFinite).filter(floor => {
return [DIRECTION_NONE, DIRECTION_DOWN, DIRECTION_UP].includes(this[floor]);
}).map(it => parseInt(it, 10));
}
moveElevatorOnIfNecessary() {
const requestedFloors = this.getRequestedFloors();
if (!requestedFloors.length) {
// nowhere to move
return;
}
if (this.hw.getCurrentDirection() !== DIRECTION_NONE) {
// we're already moving
return;
}
const currentElevatorFloor = this.hw.getCurrentFloor();
const lastElevatorDirection = this.lastElevatorDirection;
if (requestedFloors.find(floor => floor === currentElevatorFloor)) {
// we're pressing up/down/cabin button being on the "currrent floor"
this.stopElevatorFloorIfNecessary(floor);
} else if (lastElevatorDirection === DIRECTION_DOWN && requestedFloors.find(floor => floor < currentElevatorFloor)) {
// we're moving down and there's a requested floor in the direction we move to
this.hw.moveDown();
} else if (lastElevatorDirection === DIRECTION_UP && requestedFloors.find(floor => floor > currentElevatorFloor)) {
// we're moving up and there's a requested floor in the direction we move to
this.hw.moveUp();
} else if (lastElevatorDirection === DIRECTION_UP) {
// we've been moving up, but only lower floors are left to visit
this.hw.moveDown();
} else if (lastElevatorDirection === DIRECTION_DOWN) {
// we've been moving down, but only upper floors are left to visit
this.hw.moveUp();
}
}
enqueueFloor(floor, direction) {
// we write directly to "this" for simplicity
this[floor] = direction || DIRECTION_NONE;
this.moveElevatorOnIfNecessary();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment