Last active
September 23, 2015 16:22
-
-
Save SinisterMinister/899e1c1b40220319a9bb 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: function (elevators, floors) { | |
var DS = this.api.instances.DispatchServiceInstance = new this.api.services.DispatchService(this.api, floors), | |
ES = this.api.instances.ElevatorServiceInstance = new this.api.services.ElevatorService(this.api, elevators); | |
DS.on("new_task", function () { | |
console.info("New task", this); | |
// if (ES.idleElevatorsAvailable()) | |
// ES.processTask(DS.nextTask()); | |
}); | |
ES.on("idle_elevator_available", function () { | |
console.info("Elevator idle", this); | |
// var task = DS.nextTask(); | |
// if (task === undefined) | |
// return; | |
// ES.processTask(task); | |
}); | |
}, | |
update: function (dt, elevators, floors) { | |
var DS = this.api.instances.DispatchServiceInstance, | |
ES = this.api.instances.ElevatorServiceInstance; | |
// GC ALL THE THINGS | |
DS.gcTasks(); | |
// Delegate the tasks as idle elevators become available | |
if (ES.idleElevatorsAvailable()) | |
ES.processTask(DS.nextTask()); | |
}, | |
api: { | |
models: { | |
Task: function (floorNum, direction) { | |
this.floorNum = floorNum; | |
this.direction = direction; | |
this.timestamp = Date.now(); | |
} | |
}, | |
instances: { | |
DispatchServiceInstance: null, | |
ElevatorServiceInstance: null | |
}, | |
services: { | |
DispatchService: (function () { | |
var DispatchService = function (api, floors) { | |
this._floors = floors; | |
this._api = api; | |
this._taskQueue = []; | |
this._eventBindings = {}; | |
this._setupBindings(); | |
}; | |
DispatchService.prototype._setupBindings = function() { | |
console.info("Binding dispatch events"); | |
var self = this; | |
this._floors.forEach(function (floor) { | |
floor.handled = { | |
up: false, | |
down: false | |
}; | |
floor.on("up_button_pressed", self._getUpButtonHandler()); | |
floor.on("down_button_pressed", self._getDownButtonHandler()); | |
}); | |
}; | |
/********************************************************* | |
* * | |
* Floor Event Handlers * | |
* * | |
*********************************************************/ | |
DispatchService.prototype._getUpButtonHandler = function() { | |
var self = this; | |
return function () { | |
self.queueTask(this.floorNum(), "up"); | |
}; | |
}; | |
DispatchService.prototype._getDownButtonHandler = function() { | |
var self = this; | |
return function () { | |
self.queueTask(this.floorNum(), "down"); | |
}; | |
}; | |
/********************************************************* | |
* * | |
* Public API * | |
* * | |
*********************************************************/ | |
DispatchService.prototype.queueTask = function(floorNum, direction) { | |
// Create a new task | |
var task = new this._api.models.Task(floorNum, direction); | |
// Push the task into the queue | |
this._taskQueue.push(task); | |
// Trigger an event | |
this.trigger("new_task", task); | |
}; | |
DispatchService.prototype.hasPassengersWaiting = function(floorNum, direction) { | |
var floor = this.getFloor(floorNum); | |
return (floor && floor.buttonStates[direction] !== ""); | |
}; | |
DispatchService.prototype.markFloorAsHandled = function(floorNum, direction) { | |
var floor = this.getFloor(floorNum); | |
if (floor) | |
floor.handled[direction] = true; | |
}; | |
DispatchService.prototype.markFloorAsUnhandled = function(floorNum, direction) { | |
var floor = this.getFloor(floorNum); | |
if (floor) | |
floor.handled[direction] = false; | |
}; | |
DispatchService.prototype.isFloorHandled = function(floorNum, direction) { | |
var floor = this.getFloor(floorNum); | |
return (floor && floor.handled[direction]); | |
}; | |
DispatchService.prototype.getFloor = function(floorNum) { | |
var floor = null; | |
for (var i = this._floors.length - 1; i >= 0; i--) { | |
if (this._floors[i].floorNum() === floorNum) { | |
floor = this._floors[i]; | |
break; | |
} | |
}; | |
return floor; | |
}; | |
DispatchService.prototype.getFloorCount = function() { | |
return this._floors.length; | |
}; | |
DispatchService.prototype.getTopFloorNum = function() { | |
return this.getFloorCount() - 1; | |
}; | |
DispatchService.prototype.nextTask = function() { | |
if (this._taskQueue.length === 0) console.warn("Task queue is empty"); | |
return this._taskQueue.shift(); | |
}; | |
DispatchService.prototype.findTasks = function(floorNum, direction) { | |
var tasks = []; | |
this._taskQueue.forEach(function (task) { | |
if (task.floorNum === floorNum && task.direction === direction) | |
tasks.push(task); | |
}); | |
return tasks; | |
}; | |
DispatchService.prototype.clearTasks = function(floorNum, direction) { | |
var tasks = this.findTasks(floorNum, direction), | |
self = this; | |
tasks.forEach(function (task) { | |
self.clearTask(task); | |
}); | |
}; | |
DispatchService.prototype.clearTask = function(task) { | |
if (this._taskQueue.indexOf(task) > -1) | |
this._taskQueue.splice(this._taskQueue.indexOf(task), 1); | |
}; | |
DispatchService.prototype.gcTasks = function() { | |
var DS = this; | |
this._taskQueue.forEach(function (task) { | |
// Check to see if the floor is still waiting for pickup | |
if (DS.getFloor(task.floorNum).buttonStates[task.direction] === "") { | |
console.warn("Clearing task", task, "Button State:", DS.getFloor(task.floorNum).buttonStates[task.direction]); | |
DS.clearTask(task); | |
} | |
}); | |
}; | |
DispatchService.prototype.trigger = function (event, context) { | |
var self = this, | |
context = context || self; | |
// Get the callbacks for the event | |
if (this._eventBindings[event] !== undefined) { | |
this._eventBindings[event].forEach(function (callback) { | |
callback.call(context); | |
}); | |
} | |
}; | |
DispatchService.prototype.on = function (event, callback) { | |
// Setup the container | |
this._eventBindings[event] = this._eventBindings[event] || []; | |
// Add the callback | |
this._eventBindings[event].push(callback); | |
}; | |
return DispatchService; | |
})(), | |
ElevatorService: (function () { | |
var ElevatorService = function (api, elevators) { | |
this._api = api; | |
this._elevators = elevators; | |
this._idleElevators = []; | |
this._eventBindings = {}; | |
this._setupBindings(); | |
}; | |
ElevatorService.prototype._setupBindings = function() { | |
console.info("Binding elevator events"); | |
var self = this; | |
this._elevators.forEach(function (elevator, idx) { | |
elevator.service = self; | |
elevator.ignoreStops = false; | |
elevator.currentTask = null; | |
elevator.id = idx | |
elevator.on("idle", self._getIdleHandler()); | |
elevator.on("floor_button_pressed", self._getFloorButtonHandler()); | |
elevator.on("stopped_at_floor", self._getFloorStopHandler()); | |
elevator.on("passing_floor", self._getFloorPassHandler()); | |
}); | |
}; | |
/********************************************************* | |
* * | |
* Elevator Event Handlers * | |
* * | |
*********************************************************/ | |
ElevatorService.prototype._getIdleHandler = function() { | |
var ES = this, | |
DS = this._api.instances.DispatchServiceInstance; | |
return function () { | |
ES._idleElevators.push(this); | |
ES.trigger("idle_elevator_available", this); | |
if (DS.getFloorCount() > 6 && ES.getElevatorCount() > 3 && this.currentFloor() !== ES.getIdlePosition(this)) { | |
ES.sendElevatorToFloor(this, ES.getIdlePosition(this), false, true); | |
this.destinationQueue.splice(0); | |
} | |
}; | |
}; | |
ElevatorService.prototype._getFloorButtonHandler = function() { | |
var self = this, | |
DS = this._api.instances.DispatchServiceInstance; | |
return function (floorNum) { | |
console.info("Passenger has requested elevator", this.id, "to go to floor", floorNum); | |
self.sendElevatorToFloor(this, floorNum); | |
}; | |
}; | |
ElevatorService.prototype._getFloorPassHandler = function() { | |
var self = this, | |
DS = self._api.instances.DispatchServiceInstance; | |
return function (floorNum, direction) { | |
console.info("Elevator", this.id, "destination queue", this.destinationQueue); | |
if ( ! self.isButtonPressed(this, floorNum)) { | |
if (this.ignoreStops) { | |
console.info("Elevator", this.id, "is skipping floor", floorNum, "as it is ignoring all stops"); | |
return; | |
} | |
if (this.loadFactor() > 0.6) { | |
console.info("Elevator", this.id, "is skipping floor", floorNum, "as it is probably full", this.loadFactor()); | |
return; | |
} | |
if ( ! DS.hasPassengersWaiting(floorNum, direction)) { | |
console.info("Elevator", this.id, "is skipping floor", floorNum, "as there are no passengers waiting"); | |
return; | |
} | |
if (DS.isFloorHandled(floorNum, direction)) { | |
console.info("Elevator", this.id, "is skipping floor", floorNum, "as it has been marked as handled"); | |
return; | |
} | |
} | |
// People are waiting so lets stop | |
self.sendElevatorToFloor(this, floorNum); | |
// Mark the elevator as handled | |
DS.markFloorAsHandled(floorNum, direction); | |
console.info("Elevator", this.id, "is marking floor", floorNum, "as handled"); | |
}; | |
}; | |
ElevatorService.prototype._getFloorStopHandler = function() { | |
var self = this, | |
DS = self._api.instances.DispatchServiceInstance; | |
return function (floorNum) { | |
console.info("Elevator", this.id, "has stopped at floor", floorNum); | |
DS.markFloorAsUnhandled(floorNum, self.getNextDirection(this)); | |
self.clearElevatorTasks(floorNum, self.getNextDirection(this)); | |
self.setDirectionIndicator(this, self.getNextDirection(this)); | |
if (this.currentTask !== null && this.currentTask.floorNum === floorNum) { | |
this.currentTask = null; | |
} | |
this.ignoreStops = false; | |
}; | |
}; | |
/********************************************************* | |
* * | |
* Public API * | |
* * | |
*********************************************************/ | |
ElevatorService.prototype.trigger = function (event, context) { | |
var self = this, | |
context = context || self; | |
// Get the callbacks for the event | |
if (this._eventBindings[event] !== undefined) { | |
this._eventBindings[event].forEach(function (callback) { | |
callback.call(context); | |
}); | |
} | |
}; | |
ElevatorService.prototype.on = function (event, callback) { | |
// Setup the container | |
this._eventBindings[event] = this._eventBindings[event] || []; | |
// Add the callback | |
this._eventBindings[event].push(callback); | |
}; | |
ElevatorService.prototype.sendElevatorToFloor = function(elevator, floorNum, force, remainIdle) { | |
// Remove from idle elevators | |
if ( ! remainIdle && this._idleElevators.indexOf(elevator) > -1) | |
this._idleElevators.splice(this._idleElevators.indexOf(elevator), 1); | |
if (force && this.getElevatorCount() > 1 ) { | |
elevator.ignoreStops = true; | |
return elevator.goToFloor(floorNum, true); | |
} | |
if (elevator.destinationQueue.indexOf(floorNum) > -1) | |
return; | |
// Set the elevator direction indicator | |
this.setDirectionIndicator(elevator, this.getDirectionToFloor(elevator, floorNum)); | |
elevator.goToFloor(floorNum); | |
elevator.destinationQueue.sort(function (a,b){return a-b;}); | |
if (this.getDirectionToFloor(elevator, floorNum) === "down") | |
elevator.destinationQueue.reverse(); | |
elevator.checkDestinationQueue(); | |
}; | |
ElevatorService.prototype.getIdlePosition = function(elevator) { | |
if (elevator.id === 0) | |
return 0; | |
if (elevator.id === this.getElevatorCount() - 1) | |
return this._api.instances.DispatchServiceInstance.getTopFloorNum(); | |
var floorCount = this._api.instances.DispatchServiceInstance.getFloorCount() - 2, | |
elevatorCount = this.getElevatorCount() - 2; | |
return Math.floor(floorCount / elevatorCount) * elevator.id; | |
}; | |
ElevatorService.prototype.setDirectionIndicator = function(elevator, direction) { | |
elevator.goingDownIndicator(false); | |
elevator.goingUpIndicator(false); | |
if (direction == "up") | |
elevator.goingUpIndicator(true); | |
if (direction == "down") | |
elevator.goingDownIndicator(true); | |
}; | |
ElevatorService.prototype.getDirectionToFloor = function(elevator, floorNum) { | |
if (elevator.currentFloor() < floorNum) | |
return "up"; | |
else | |
return "down"; | |
}; | |
ElevatorService.prototype.getClosestIdleElevator = function(floorNum) { | |
var currentBest = null; | |
// Find the best idle elevator | |
for (var i = this._idleElevators.length - 1; i >= 0; i--) { | |
if (currentBest === null) | |
currentBest = this._idleElevators[i]; | |
else { | |
if (this.getElevatorDistance(this._idleElevators[i], floorNum) < this.getElevatorDistance(currentBest, floorNum)) { | |
// Found a better elevator | |
currentBest = this._idleElevators[i]; | |
} | |
} | |
}; | |
return currentBest; | |
}; | |
ElevatorService.prototype.isButtonPressed = function(elevator, floorNum) { | |
var floors = elevator.getPressedFloors(); | |
for (var i = floors.length - 1; i >= 0; i--) { | |
if (floors[i] === floorNum) | |
return true; | |
}; | |
return false; | |
}; | |
ElevatorService.prototype.getElevatorCount = function() { | |
return this._elevators.length; | |
}; | |
ElevatorService.prototype.getElevatorDistance = function (elevator, floorNum) { | |
return Math.abs(elevator.currentFloor() - floorNum); | |
}; | |
ElevatorService.prototype.idleElevatorsAvailable = function() { | |
return this._idleElevators.length > 0; | |
}; | |
ElevatorService.prototype.processTask = function(task) { | |
if (task === undefined) | |
return; | |
var elevator = this.getClosestIdleElevator(task.floorNum); | |
if (elevator === null) { | |
console.error("Could not find an elevator to service the task", this._idleElevators); | |
return false; | |
} | |
console.info("Delegating task", task, "to elevator", elevator.id); | |
elevator.currentTask = task; | |
this.sendElevatorToFloor(elevator, task.floorNum, this.getElevatorCount() > 2); | |
this._api.instances.DispatchServiceInstance.markFloorAsHandled(task.floorNum, this.getDirectionToFloor(elevator, task.floorNum)); | |
return true; | |
}; | |
ElevatorService.prototype.clearElevatorTasks = function(floorNum, direction) { | |
var ES = this; | |
this._elevators.forEach(function (elevator) { | |
if (elevator.currentTask !== null && elevator.currentTask.floorNum === floorNum && elevator.currentTask.direction === direction) { | |
// Clear the task | |
elevator.currentTask = null; | |
// Reset the queue to only what's currently pressed | |
var floors = elevator.getPressedFloors(), | |
closest = null; | |
// Find the closest | |
floors.forEach(function (floor) { | |
if (closest === null || ES.getElevatorDistance(elevator, closest) > ES.getElevatorDistance(elevator, floor)) | |
closest = floor; | |
}); | |
if (closest !== null) { | |
// Sort the floors | |
floors.sort(function (a,b) {return a-b;}) | |
// Reverse the sort based on direction | |
if (ES.getDirectionToFloor(elevator, closest) === "down") | |
floors.reverse(); | |
// Set the new destination queue | |
elevator.destinationQueue = floors; | |
elevator.checkDestinationQueue(); | |
} | |
// Stop the elevator if necessary | |
if (elevator.destinationQueue.length === 0) { | |
console.warn("Elevator", elevator.id, "going idle as nothing is left to do."); | |
elevator.trigger("idle"); | |
} | |
} | |
}); | |
}; | |
ElevatorService.prototype.getNextDirection = function(elevator) { | |
var DS = this._api.instances.DispatchServiceInstance; | |
if (elevator.currentTask !== null && elevator.currentTask.floorNum === elevator.currentFloor()) | |
return elevator.currentTask.direction; | |
if (elevator.destinationQueue.length > 0) | |
return this.getDirectionToFloor(elevator, elevator.destinationQueue[0]); | |
if (elevator.currentFloor() === 0) | |
return "up"; | |
if (elevator.currentFloor() === DS.getTopFloorNum()) | |
return "down"; | |
if (DS.hasPassengersWaiting(elevator.currentFloor(), "up")) | |
return "up"; | |
if (DS.hasPassengersWaiting(elevator.currentFloor(), "down")) | |
return "down"; | |
return "stopped"; | |
}; | |
return ElevatorService; | |
})() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment