Last active
August 29, 2015 14:06
-
-
Save CodingNinja/86d84cec11bae0f4ec66 to your computer and use it in GitHub Desktop.
Mine output simulator
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
/** | |
* Dump Truck simulator | |
* | |
* Simple simulator that emulates a mining environment and allows | |
* one to plot the potential output of a mines operation. | |
* | |
* @author David Mann <[email protected]> | |
*/ | |
var currentTime = 1, runTime = ((60*1)*60), events = [], tonnageCarried = 0, maxSimultaneousDump = 1; | |
var Truck = function(ref, tonnage, kmPh, dumpTime) { | |
var that = this; | |
that.tonnage = tonnage; | |
that.kmPh = kmPh; | |
that.dumpTime = dumpTime; | |
that.full = false; | |
that.location = null; | |
that.busy = false; | |
that.markEmpty = function(loc) { | |
events.push({ | |
name: that.toString() + ' completed dumping', | |
location: loc.toString(), | |
truck: that.toString(), | |
time: currentTime | |
}); | |
that.full = false; | |
}; | |
that.tick = function() { | |
}; | |
that.isEmpty = function() { | |
return that.full === false; | |
}; | |
that.markFull = function(loc) { | |
events.push({ | |
name: that.toString() + ' full ' + loc.toString(), | |
truck: that.toString(), | |
location: loc.toString(), | |
time: currentTime | |
}); | |
tonnageCarried += that.tonnage; | |
return that.full = true; | |
}; | |
/** | |
* Calculate the time taken to travel x distance | |
* @param Number x The distance | |
* @return {[type]} [description] | |
*/ | |
that.timeForDistance = function(x) { | |
return Math.ceil((x / kmPh) * 60) * 60; | |
}; | |
/** | |
* Get the time it would take for that truck to travel to loc; | |
* @param Location loc The location | |
* @return {[type]} [description] | |
*/ | |
that.timeToLocation = function(loc) { | |
return Math.ceil((loc.getDistanceFromLocation(that.location) / kmPh) * 60) * 60; | |
}; | |
/** | |
* Get the time the truck would arrive at location | |
* @param {[type]} loc [description] | |
* @return {[type]} [description] | |
*/ | |
that.getArrivalTime = function(loc) { | |
// if(loc === that.location && that.empty) { | |
// return that.willArriveAt(); | |
// } | |
return that.timeToLocation(loc) + currentTime; | |
}; | |
/** | |
* Find out how long it will take to fill the this truck at location | |
* @param {[type]} location The location that will load the truck | |
* @return {[type]} [description] | |
*/ | |
that.timeToFill = function (location) { | |
if(location.isDumpSite()) { | |
return that.dumpTime; | |
} | |
return Math.ceil(that.tonnage / location.shovelCap) * location.shovelTime; | |
}; | |
that.toString = function() { | |
return ref; | |
}; | |
return that; | |
}; | |
var Location = function(ref, isDumpSite, shovelCap, shovelTime) { | |
var that = this; | |
that.ref = ref; | |
that.isDumpSite = isDumpSite; | |
that.shovelCap = shovelCap; | |
that.shovelTime = shovelTime; | |
that.enRoute = {}; | |
that.shovelWaiting = {}; | |
that.shovelBusy = false; | |
that.shovelNextFreeAt = null; | |
that.estimatedQueue = 0; | |
that.shovelWaitingCount = 0; | |
that.trucksInBays = 0; | |
that.distanceFromLocation = {}; | |
that.shovelInUse = {}; | |
that.tick = function() { | |
for(arrivalTime in that.enRoute) { | |
if(Number(currentTime) === Number(arrivalTime)) { | |
var arrivingNow = that.enRoute[arrivalTime]; | |
for (var i = arrivingNow.length - 1; i >= 0; i--) { | |
var truck = arrivingNow[i]; | |
events.push({ | |
name: truck.toString() + ' arrived at ' + (that.isDumpSite() ? 'dump': 'fill') + ' site "' + that.toString() + '"', | |
truck: truck.toString(), | |
location: that.toString(), | |
time: currentTime | |
}); | |
that.notifyShovel(truck); | |
}; | |
delete that.enRoute[arrivalTime]; | |
} | |
} | |
that.processShovel(); | |
that.shovelBusy = that.assertShovelBusy(); | |
}; | |
that.processShovel = function() { | |
if(!(typeof that.shovelInUse[currentTime] === 'undefined')) { | |
for (var i = that.shovelInUse[currentTime].length - 1; i >= 0; i--) { | |
var truckReady = that.shovelInUse[currentTime][i]; | |
delete that.shovelInUse[currentTime][i]; | |
that.trucksInBays--; | |
if(that.isDumpSite()) { | |
truckReady.markEmpty(that); | |
}else{ | |
truckReady.markFull(that); | |
} | |
truckReady.busy = false; | |
}; | |
} | |
if(!(typeof that.shovelWaiting[currentTime] === 'undefined')) { | |
for (var i = that.shovelWaiting[currentTime].length - 1; i >= 0; i--) { | |
var truckReady = that.shovelWaiting[currentTime][i]; | |
that.startShovel(truckReady, that.calculateShovelTime(truckReady) + currentTime); | |
that.shovelWaitingCount--; | |
delete that.shovelWaiting[currentTime][i]; | |
}; | |
} | |
}; | |
that.startShovel = function(truck, shovelTime) { | |
that.trucksInBays++; | |
events.push({ | |
name: that.toString() + ' shovel started to '+(that.isDumpSite() ? 'dump': 'fill')+' ' + truck.toString() + ' complete at ' + shovelTime, | |
truck: truck.toString(), | |
location: that.toString(), | |
time: currentTime | |
}); | |
if(typeof that.shovelInUse[shovelTime] === 'undefined') { | |
that.shovelInUse[shovelTime] = []; | |
} | |
that.shovelInUse[shovelTime].push(truck); | |
} | |
that.notifyShovel = function(truck) { | |
var shovelNextFreeAt = that.calculateShovelNextFreeAt(); | |
events.push({ | |
name: truck.toString() + ' waiting for free shovel at ' + that.toString() + '(Queue size: ' + that.shovelWaitingCount + ')', | |
truck: truck.toString(), | |
location: that.toString(), | |
time: currentTime | |
}); | |
if(typeof that.shovelWaiting[shovelNextFreeAt] === 'undefined') { | |
that.shovelWaiting[shovelNextFreeAt] = []; | |
} | |
that.shovelWaiting[shovelNextFreeAt].push(truck); | |
that.shovelWaitingCount++; | |
that.shovelNextFreeAt = shovelNextFreeAt + that.calculateShovelTime(truck); | |
}; | |
that.calculateShovelNextFreeAt = function() { | |
return Math.max(that.shovelNextFreeAt, currentTime) || (currentTime); | |
}; | |
that.assertShovelBusy = function() { | |
if(that.isDumpSite() && that.trucksInBays >= maxSimultaneousDump) { | |
return true; | |
}else{ | |
return that.trucksInBays > 0; | |
} | |
return that.calculateShovelNextFreeAt() <= currentTime;; | |
}; | |
that.calculateShovelTime = function(truck) { | |
if(that.isDumpSite()) { | |
return truck.dumpTime; | |
}else{ | |
return Math.ceil((truck.tonnage / that.shovelCap) * that.shovelTime); | |
} | |
}; | |
/** | |
* Record that a truck is on it's way here | |
* @param {[type]} truck [description] | |
* @return {[type]} [description] | |
*/ | |
that.notifyEnRoute = function(truck) { | |
var arrivalTime = truck.getArrivalTime(that); | |
if(typeof that.enRoute[arrivalTime] === 'undefined') { | |
that.enRoute[arrivalTime] = []; | |
} | |
truck.location = that; | |
truck.busy = true; | |
that.enRoute[arrivalTime].push(truck); | |
// Minus the current time so that the queue is just a queue time, not "end of queue" time | |
events.push({ | |
name: truck.toString() + ' enroute to ' + (that.isDumpSite() ? 'dump': 'fill') + ' site "' + that.toString() + '", arrive at ' + arrivalTime + ' and start ' + (that.isDumpSite() ? 'dumping': 'loading') + ' at ' + Math.max(that.estimatedQueue, arrivalTime), | |
truck: truck.toString(), | |
location: that.toString(), | |
time: currentTime, | |
}); | |
that.estimatedQueue = arrivalTime + (Math.max(that.estimatedQueue - currentTime, 0)) + truck.timeToFill(that); | |
}; | |
/** | |
* Is this a dump site? | |
* | |
* @return {Boolean} True if this location is used for dumping or false if used for filling | |
*/ | |
that.isDumpSite = function() { | |
return isDumpSite === true; | |
}; | |
/** | |
* Get the trucks en route to this location | |
* @return {[type]} [description] | |
*/ | |
that.getEnRoute = function(compareTime) { | |
var enRoute = []; | |
if(typeof compareTime === 'undefined') { | |
compareTime = currentTime; | |
} | |
for(arrivalTime in that.enRoute) { | |
if(arrivalTime >= compareTime) { | |
enRoute = enRoute.concat(that.enRoute[arrivalTime]); | |
} | |
} | |
return enRoute; | |
}; | |
/** | |
* Get the trucks en route to this location | |
* @return {[type]} [description] | |
*/ | |
that.getQueueTimeAt = function(arrivalTime) { | |
var enRoute = that.getEnRoute(arrivalTime); | |
return enRoute; | |
}; | |
/** | |
* Get the trucks en route to this location | |
* @return {[type]} [description] | |
*/ | |
that.getQueueTime = function(truck) { | |
return that.getQueueTimeAt(truck.getArrivalTime(that)); | |
}; | |
that.getDistanceFromLocation = function(loc) { | |
return that.distanceFromLocation[loc.ref]; | |
}; | |
that.setDistanceFromLocation = function(loc, distance) { | |
if(that.getDistanceFromLocation(loc) !== distance) { | |
that.distanceFromLocation[loc.ref] = distance; | |
loc.setDistanceFromLocation(that, distance); | |
} | |
}; | |
/** | |
* Return the name of the location | |
* @return {[type]} [description] | |
*/ | |
that.toString = function() { | |
return ref; | |
}; | |
return that; | |
}; | |
// Locations available | |
// Is dump site? | Shovel Cap | Shovel time | |
var loc1 = new Location('loc1', true), // Dump site | |
loc2 = new Location('loc2', true), // Dump site | |
loc3 = new Location('loc3', true), // Dump site | |
loc4 = new Location('loc4', false, 45, 60), // Fill site | |
loc5 = new Location('loc5', false, 45, 60), // Fill site | |
loc6 = new Location('loc6', false, 35, 60); // Fill site | |
// Set the distance between fill sites and dump sites | |
// The distance setter will set the inverse | |
// relationship so no we are not required to | |
// explicitly set distances on both locations | |
loc1.setDistanceFromLocation(loc4, 200); | |
loc1.setDistanceFromLocation(loc5, 400); | |
loc1.setDistanceFromLocation(loc6, 600); | |
loc2.setDistanceFromLocation(loc4, 100); | |
loc2.setDistanceFromLocation(loc5, 200); | |
loc2.setDistanceFromLocation(loc6, 300); | |
loc3.setDistanceFromLocation(loc4, 500); | |
loc3.setDistanceFromLocation(loc5, 1000); | |
loc3.setDistanceFromLocation(loc6, 1500); | |
//loc1.setDistanceFromLocation(loc3, 3); | |
// All locations grouped together | |
var locations = [ | |
loc1, | |
loc2, | |
loc3, | |
loc4, | |
loc5, | |
loc6 | |
]; | |
// Trucks available | |
// Tonnage Km/Ph Dump Time | |
var truck1 = new Truck('truck1', 200, 3500, 60), | |
truck2 = new Truck('truck2', 200, 3500, 60), | |
truck3 = new Truck('truck3', 200, 3500, 60), | |
truck4 = new Truck('truck4', 200, 3500, 60), | |
truck5 = new Truck('truck5', 200, 3500, 60), | |
truck6 = new Truck('truck6', 200, 3500, 60); | |
// Set both trucks at initial dump site | |
truck1.location = loc1; | |
truck2.location = loc2; | |
truck3.location = loc3; | |
truck4.location = loc1; | |
truck5.location = loc2; | |
truck6.location = loc3; | |
// All trucks grouped together | |
var trucks = [ | |
truck1, | |
truck2, | |
truck3, | |
truck4, | |
truck5, | |
truck6 | |
]; | |
// Take the current second and look at what needs to go where and send them off | |
function moveTrucks () { | |
var truckNum; | |
for(truckNum in trucks) { | |
trucks[truckNum].tick(); | |
} | |
for(locationNum in locations) { | |
locations[locationNum].tick(); | |
} | |
for(truckNum in trucks) { | |
var truck = trucks[truckNum]; | |
if(truck.busy) { | |
continue; | |
}else if(truck.isEmpty()) { | |
var locationTo = null, minQueueTime = null, locationNum; | |
for(locationNum in locations) { | |
var location = locations[locationNum], | |
queueTime = location.estimatedQueue - currentTime; | |
if(location.isDumpSite() || location.ref === truck.location.ref) { | |
continue; | |
} | |
if(minQueueTime === null || minQueueTime > queueTime) { | |
minQueueTime = queueTime; | |
locationTo = location; | |
} | |
} | |
if(locationTo) { | |
locationTo.notifyEnRoute(truck); | |
} | |
}else if(truck.isEmpty() === false) { | |
var locationTo = null, minQueueTime = null, locationNum; | |
for(locationNum in locations) { | |
var location = locations[locationNum], | |
queueTime = location.estimatedQueue - currentTime; | |
if(!location.isDumpSite() || location.ref === truck.location.ref) { | |
continue; | |
} | |
if(minQueueTime === null || minQueueTime > queueTime) { | |
minQueueTime = queueTime; | |
locationTo = location; | |
} | |
} | |
if(locationTo) { | |
locationTo.notifyEnRoute(truck); | |
} | |
} | |
} | |
} | |
while(currentTime < runTime) { | |
moveTrucks(); | |
currentTime++; | |
} | |
console.table(events); | |
console.log('Total tonnage: %d', tonnageCarried); | |
// setInterval(moveTrucks, 1000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment