Skip to content

Instantly share code, notes, and snippets.

@cconger
Last active January 13, 2016 07:52
Show Gist options
  • Save cconger/9546dde605b9616858bb to your computer and use it in GitHub Desktop.
Save cconger/9546dde605b9616858bb to your computer and use it in GitHub Desktop.
A solution to Elevator Saga (http://play.elevatorsaga.com/)
{
DIRECTION: {
UP: 'up',
DOWN: 'down'
},
pendingFloors: [],
idleVators: [],
printPendingState: function() {
var upFloors = [];
var downFloors = [];
this.pendingFloors.forEach((floor, idx) => {
if (floor.up) { upFloors.push(idx); }
if (floor.down) { downFloors.push(idx); }
});
console.log("Floors needing an up elevator :", upFloors);
console.log("Floors needing a down elevator :", downFloors);
},
init: function(elevators, floors) {
elevators.forEach((elevator, elevatorId) => {
elevator.stopRequest = new Array(floors.length);
var setDirection = (direction) => {
elevator.direction = direction;
if (direction === this.DIRECTION.UP) {
elevator.goingUpIndicator(true);
elevator.goingDownIndicator(false);
return;
}
if (direction === this.DIRECTION.DOWN) {
elevator.goingUpIndicator(false);
elevator.goingDownIndicator(true);
return;
}
elevator.goingUpIndicator(true);
elevator.goingDownIndicator(true);
};
var anyRequests = () => {
return this.pendingFloors.reduce((prev, current) => {
return prev || current.down || current.up;
}, false);
};
var removeFloorFromQueue = (floorNum) => {
elevator.destinationQueue = elevator.destinationQueue.filter((floor) => {
return floor !== floorNum;
});
};
var highestDownFloor = () => {
for (var i = floors.length - 1; i >= 0; i--) {
if (this.pendingFloors[i].down) {
return i;
}
}
};
var lowestUpFloor = () => {
for (var i = 0; i < floors.length; i++) {
if (this.pendingFloors[i].up) {
return i;
}
}
};
setDirection(this.DIRECTION.UP);
var self = this;
elevator.initialize = function() {
//console.log(this.currentFloor(), self.pendingFloors);
var highDown = highestDownFloor();
var lowUp = lowestUpFloor();
var highDistance = Math.abs(highDown - this.currentFloor());
var lowDistance = Math.abs(lowUp - this.currentFloor());
console.log({
highDown,
lowUp,
highDistance,
lowDistance
});
//TODO: Improvment, have the elevator take people down when its resetting to the bottom
// or take people up when its resetting to the top.
if (lowUp === undefined && highDown !== undefined || (highDown !== undefined && lowUp !== undefined && highDistance < lowDistance)) {
console.log("ELEVATOR", elevatorId, "resetting (down) to ", highDown);
setDirection(self.DIRECTION.DOWN);
this.goToFloor(highDown);
} else if (lowUp !== undefined) {
console.log("ELEVATOR", elevatorId, "resetting (up) to ", lowUp);
setDirection(self.DIRECTION.UP);
this.goToFloor(lowUp);
} else {
self.idleVators.push(this);
}
};
elevator.on('idle', () => {
// I'm Idle
console.log("ELEVATOR", elevatorId, "IDLE");
elevator.initialize();
});
elevator.on('floor_button_pressed', (floorNum) => {
elevator.stopRequest[floorNum] = true;
if (elevator.direction === this.DIRECTION.UP) {
if (elevator.targetFloor === undefined || floorNum > elevator.targetFloor) {
removeFloorFromQueue(elevator.targetFloor);
elevator.targetFloor = floorNum;
elevator.goToFloor(elevator.targetFloor);
}
} else {
if (elevator.targetFloor === undefined || floorNum < elevator.targetFloor) {
removeFloorFromQueue(elevator.targetFloor);
elevator.targetFloor = floorNum;
elevator.goToFloor(elevator.targetFloor);
}
}
console.log('ELEVATOR', elevatorId, 'floor_button_pressed:', floorNum);
console.log(' TargetFloor:', elevator.targetFloor);
});
elevator.on('passing_floor', (floorNum) => {
if (elevator.destinationQueue.includes(floorNum) || elevator.stopRequest[floorNum]) {
elevator.goToFloor(floorNum, true);
}
// If we have room and there are people going in the direction we're going.
if (elevator.loadFactor() < 0.5) {
if (this.pendingFloors[floorNum][elevator.direction]) {
elevator.goToFloor(floorNum, true);
}
}
});
elevator.on('stopped_at_floor', (floorNum) => {
//this.printPendingState();
// We took care of a direction on this floor.
elevator.stopRequest[floorNum] = false;
if (elevator.targetFloor === floorNum) {
elevator.targetFloor = undefined;
}
// THIS IS HIGH RISK SINCE WE MIGHT NOT HAVE GOT EVERYONE.
this.pendingFloors[floorNum][elevator.direction] = false;
removeFloorFromQueue(floorNum);
elevator.checkDestinationQueue();
});
});
floors.forEach((floor) => {
var floorNum = floor.floorNum();
this.pendingFloors[floorNum] = {up: false, down: false};
floor.on('up_button_pressed', () => {
console.log('up_button_pressed on floor', floorNum);
this.pendingFloors[floorNum].up = true;
this.printPendingState();
var idle = this.idleVators.pop();
if (idle) { idle.initialize()}
});
floor.on('down_button_pressed', () => {
console.log('down_button_pressed on floor', floorNum);
this.pendingFloors[floorNum].down = true;
this.printPendingState();
var idle = this.idleVators.pop();
if (idle) { idle.initialize(); }
});
});
},
update: function(dt, elevators, floors) {
// We normally don't need to do anything here
}
}
{
init: function(elevators, floors) {
var floorCount = floors.length;
var elevatorCount = elevators.length;
elevators.forEach((elevator, idx) => {
//var home = Math.floor((floorCount / (elevatorCount + 1)) * (idx + 1));
var self = this;
elevator.dispatch = function(floor) {
self.markBusy(this);
this.goToFloor(floor);
};
var recalibrateDestination = () => {
// Sort by target distance.
// Closest one is the next target.
// If we're not full and there is a requestedFloor between me and there...
// Stop there too.
/*
elevator.destinationQueue = elevator.destinationQueue.sort((floorA, floorB) => {
var currentFloor = elevator.currentFloor();
var distA = Math.abs(floorA - currentFloor);
var distB = Math.abs(floorB - currentFloor);
return distB - distA;
});
*/
var nextDestination = elevator.destinationQueue[0];
if (nextDestination > elevator.currentFloor()) {
// Going up
//elevator.goingDownIndicator(false);
//elevator.goingUpIndicator(true);
} else {
// Going down
//elevator.goingDownIndicator(true);
//elevator.goingUpIndicator(false);
}
elevator.checkDestinationQueue();
};
elevator.on('idle', () => {
var newTarget = this.getOldestRequest();
if (newTarget) {
elevator.goToFloor(newTarget);
this.clearedFloor(newTarget);
recalibrateDestination()
} else {
this.markIdle(elevator);
elevator.goingDownIndicator(true);
elevator.goingUpIndicator(true);
}
});
elevator.on('floor_button_pressed', (floorPressed) => {
// Add target to destinations.
elevator.goToFloor(floorPressed);
//try pre-emptively clearing floors so multiples don't go there.
this.clearedFloor(floorPressed);
recalibrateDestination();
});
elevator.on('passing_floor', (floorNum, direction) => {
if (elevator.loadFactor() < .5 && this.floorNeedsPickup(floorNum)) {
elevator.goToFloor(floorNum, true);
}
var indexInQueue = elevator.destinationQueue.indexOf(floorNum);
if (indexInQueue !== -1) {
elevator.goToFloor(floorNum, true);
elevator.checkDestinationQueue();
}
});
elevator.on('stopped_at_floor', (floorNum) => {
this.clearedFloor(floorNum);
//recalibrateDestination();
});
});
floors.forEach((floor) => {
// When button is pushed, add yourself to the requestedPickup list.
var queuePickup = () => {
this.requestPickup(floor.floorNum());
};
floor.on('up_button_pressed', queuePickup);
floor.on('down_button_pressed', queuePickup);
});
},
idleVators: [],
markIdle: function(elevator) {
if (this.idleVators.indexOf(elevator) === -1) {
this.idleVators.push(elevator);
}
},
markBusy: function(elevator) {
var elevatorIdx = this.idleVators.indexOf(elevator);
if (elevatorIdx >= 0) {
this.idleVators.splice(elevatorIdx, 1);
}
},
pickupFloors: [],
requestPickup: function(floor) {
if (this.idleVators.length > 0) {
this.idleVators[0].dispatch(floor);
} else if (this.pickupFloors.indexOf(floor) === -1) {
// This floor hasn't been requested yet.
this.pickupFloors.push(floor);
}
},
getOldestRequest: function() {
if (this.pickupFloors.length > 0) {
return this.pickupFloors.splice(0,1);
} else {
return null;
}
},
clearedFloor: function(floor) {
var floorIdx = this.pickupFloors.indexOf(floor);
if (floorIdx >= 0) {
this.pickupFloors.splice(floorIdx, 1);
}
},
floorNeedsPickup: function(floor) {
return this.pickupFloors.indexOf(floor) !== -1;
},
update: function(dt, elevators, floors) {
// We normally don't need to do anything here
}
}
{
init: function(elevators, floors) {
var floorCount = floors.length;
var elevatorCount = elevators.length;
elevators.forEach((elevator, idx) => {
//var home = Math.floor((floorCount / (elevatorCount + 1)) * (idx + 1));
var self = this;
elevator.dispatch = function(floor) {
self.markBusy(this);
this.goToFloor(floor);
};
var recalibrateDestination = () => {
// Sort by target distance.
// Closest one is the next target.
// If we're not full and there is a requestedFloor between me and there...
// Stop there too.
elevator.destinationQueue = elevator.destinationQueue.sort((floorA, floorB) => {
var currentFloor = elevator.currentFloor();
var distA = Math.abs(floorA - currentFloor);
var distB = Math.abs(floorB - currentFloor);
return distA - distB;
});
var nextDestination = elevator.destinationQueue[0];
if (nextDestination > elevator.currentFloor()) {
// Going up
//elevator.goingDownIndicator(false);
//elevator.goingUpIndicator(true);
} else {
// Going down
//elevator.goingDownIndicator(true);
//elevator.goingUpIndicator(false);
}
elevator.checkDestinationQueue();
};
elevator.on('idle', () => {
var newTarget = this.getOldestRequest();
if (newTarget) {
elevator.goToFloor(newTarget);
this.clearedFloor(newTarget);
recalibrateDestination()
} else {
this.markIdle(elevator);
elevator.goingDownIndicator(true);
elevator.goingUpIndicator(true);
}
});
elevator.on('floor_button_pressed', (floorPressed) => {
// Add target to destinations.
elevator.goToFloor(floorPressed);
//try pre-emptively clearing floors so multiples don't go there.
this.clearedFloor(floorPressed);
recalibrateDestination();
});
elevator.on('passing_floor', (floorNum, direction) => {
if (elevator.loadFactor() < .5 && this.floorNeedsPickup(floorNum)) {
elevator.goToFloor(floorNum, true);
}
});
elevator.on('stopped_at_floor', (floorNum) => {
this.clearedFloor(floorNum);
//recalibrateDestination();
});
});
floors.forEach((floor) => {
// When button is pushed, add yourself to the requestedPickup list.
var queuePickup = () => {
this.requestPickup(floor.floorNum());
};
floor.on('up_button_pressed', queuePickup);
floor.on('down_button_pressed', queuePickup);
});
},
idleVators: [],
markIdle: function(elevator) {
if (this.idleVators.indexOf(elevator) === -1) {
this.idleVators.push(elevator);
}
},
markBusy: function(elevator) {
var elevatorIdx = this.idleVators.indexOf(elevator);
if (elevatorIdx >= 0) {
this.idleVators.splice(elevatorIdx, 1);
}
},
pickupFloors: [],
requestPickup: function(floor) {
if (this.idleVators.length > 0) {
this.idleVators[0].dispatch(floor);
} else if (this.pickupFloors.indexOf(floor) === -1) {
// This floor hasn't been requested yet.
this.pickupFloors.push(floor);
}
},
getOldestRequest: function() {
if (this.pickupFloors.length > 0) {
return this.pickupFloors.splice(0,1);
} else {
return null;
}
},
clearedFloor: function(floor) {
var floorIdx = this.pickupFloors.indexOf(floor);
if (floorIdx >= 0) {
this.pickupFloors.splice(floorIdx, 1);
}
},
floorNeedsPickup: function(floor) {
return this.pickupFloors.indexOf(floor) !== -1;
},
update: function(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