Created
March 22, 2016 23:30
-
-
Save mck-/db264d115ac69a2fef77 to your computer and use it in GitHub Desktop.
Routific solution to Local Motion challenge
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
var drivingSchedule = {}; | |
var turnNumber = 0; | |
var MIN_VISITS_PER_VEHICLE = 1; // to keep everyone busy | |
var OPTIMIZE_EVERY_X_TURNS = 5; | |
var turn = function(vehicles, people, buildings) { | |
'use strict'; | |
if(turnNumber % OPTIMIZE_EVERY_X_TURNS === 0 && turnNumber < 1000) { | |
// Parse data to be suitable for Routific's API | |
let inputData = createRoutificInputData(vehicles, people, buildings); | |
// Call Routific API | |
let solution = callRoutificAPI(inputData); | |
// Given the solution, dispatch the routes to the drivers | |
dispatchSolution(vehicles, solution); | |
} | |
// Execute on the drivingSchedule | |
driveAndPick(vehicles, people, buildings); | |
turnNumber++; | |
}; | |
// Helpers | |
var createRoutificInputData = function(vehicles, people, buildings) { | |
'use strict'; | |
let fleet = _createFleet(vehicles); | |
let allPeople = __getAllPeople(vehicles, people, buildings); | |
let visits = _createVisits(vehicles, allPeople, buildings); | |
let matrix = _createMatrix(vehicles, allPeople, buildings); | |
return { | |
visits: visits, | |
fleet: fleet, | |
matrix: matrix, | |
options: { | |
min_visits_per_vehicle: MIN_VISITS_PER_VEHICLE | |
} | |
}; | |
}; | |
var callRoutificAPI = function(inputData) { | |
'use strict'; | |
const ROUTIFIC_API_URL = 'https://api.routific.com/v1/pdp'; | |
const API_KEY = '<< API Token >>'; | |
let request = new XMLHttpRequest(); | |
request.open('POST', ROUTIFIC_API_URL, false); | |
request.setRequestHeader('Content-Type', 'application/json'); | |
request.setRequestHeader('Authorization', API_KEY); | |
request.send(JSON.stringify(inputData)); | |
return JSON.parse(request.responseText); | |
}; | |
var dispatchSolution = function(vehicles, routificResponse) { | |
'use strict'; | |
let solution = routificResponse.solution; | |
for(let driver in solution) { | |
let route = solution[driver].slice(1); // Remove first location on route (i.e. driver) | |
// Remove the visits on the front of the route that have time (i.e. already on driver) | |
let sliceCount = 0; | |
for(let i in route) { | |
let vehicle = __getVehicle(vehicles, driver); | |
if(route[i].type === "pickup" && __isOnCarP(vehicle, route[i].location_id)) { | |
sliceCount++; | |
} | |
} | |
route = route.slice(sliceCount); | |
drivingSchedule[driver] = route; | |
} | |
}; | |
var driveAndPick = function(vehicles, people, buildings) { | |
'use strict'; | |
for(let i in vehicles) { | |
let veh = vehicles[i]; | |
let route = drivingSchedule[veh.name]; | |
let nextDest = route[0]; | |
if(!nextDest) { | |
continue; | |
} | |
let building = __getBuilding(buildings, nextDest.location_name); | |
veh.moveTo(building); | |
// We are at this location now, so let's pick up the right passengers | |
if(__sameLocP(veh, building)) { | |
var passengers = __getPassengers(route, people); | |
for(let i in passengers) { | |
if(passengers[i] !== "Not found?!") { | |
if(__stillWaiting(passengers[i], buildings)) { | |
veh.pick(passengers[i]); | |
} else { | |
console.log(passengers[i].name + ' already started walking! ' + passengers[i].time); | |
} | |
} | |
} | |
// Update route | |
route.splice(0,passengers.length); | |
} | |
}; | |
}; | |
// UTILS | |
var _createVisits = function(vehicles, people, buildings) { | |
'use strict'; | |
let visits = {}; | |
for(let i in people) { | |
let p = people[i]; | |
visits[p.name] = { | |
load: 1, | |
pickup: { location: { name: p.origin }, duration: 0 , start: '0:00', end: __convertTurnsToTime(p.time) }, | |
dropoff: { location: { name: p.destination }, duration: 0 , start: '0:00', end: __convertTurnsToTime(p.time) }, | |
type: p.vehicle | |
}; | |
} | |
return visits; | |
}; | |
var _createFleet = function(vehicles) { | |
'use strict'; | |
let fleet = {}; | |
for(let i in vehicles) { | |
let v = vehicles[i]; | |
fleet[v.name] = { | |
start_location: { id: v.name }, | |
type: v.name, | |
capacity: 4 | |
}; | |
}; | |
return fleet; | |
}; | |
var _createMatrix = function(vehicles, people, buildings) { | |
'use strict'; | |
let allNodes = {}; | |
let matrix = {}; | |
for(let v in vehicles){ | |
allNodes[vehicles[v].name] = vehicles[v]; | |
}; | |
for(let p in people){ | |
allNodes[people[p].name + '_pickup'] = people[p]; | |
allNodes[people[p].name + '_dropoff'] = __getBuilding(buildings, people[p].destination); | |
}; | |
for(let from in allNodes){ | |
matrix[from] = {}; | |
for(let to in allNodes) { | |
if(from !== to) { | |
matrix[from][to] = __getDistance(allNodes[from], allNodes[to]); | |
}; | |
}; | |
}; | |
return matrix; | |
}; | |
var __getDistance = function(objA, objB) { | |
'use strict'; | |
let xDist = objA.x - objB.x; | |
let yDist = objA.y - objB.y; | |
let distance = Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); | |
return distance * 60; // Convert each turn to a minute | |
}; | |
var __getBuilding = function(buildings, name) { | |
'use strict'; | |
for(let i in buildings) { | |
if(buildings[i].name === name) { | |
return buildings[i]; | |
} | |
} | |
return "Not found?!"; | |
}; | |
var __getPerson = function(people, name) { | |
'use strict'; | |
for(let i in people) { | |
if(people[i].name === name) { | |
return people[i]; | |
} | |
} | |
return "Not found?!"; | |
}; | |
var __getVehicle = function(vehicles, driver) { | |
'use strict'; | |
for(let i in vehicles) { | |
if(vehicles[i].name === driver) { | |
return vehicles[i]; | |
} | |
} | |
return "Not found?!"; | |
}; | |
var __convertTurnsToTime = function(time) { | |
'use strict'; | |
// When time is negative, just return something large so you dump the passenger | |
if(time < 0) { | |
return '8:00'; | |
} | |
let hours = Math.floor(time / 60); | |
let min = time % 60; | |
let timeString = hours + ':'; | |
if(min < 10) { | |
timeString += '0'; | |
}; | |
timeString += min; | |
return timeString; | |
}; | |
var __sameLocP = function(locA, locB) { | |
return (locA.x === locB.x) && (locA.y === locB.y); | |
}; | |
var __getPassengers = function(route, people) { | |
'use strict'; | |
let location = route[0]; | |
let passengers = []; // get at least first one | |
for(let i in route) { | |
if(route[i].location_name !== location.location_name) { | |
break; | |
} else { | |
passengers.push(__getPerson(people, route[i].location_id)); | |
}; | |
}; | |
return passengers; | |
}; | |
var __getAllPeople = function(vehicles, people, buildings) { | |
'use strict'; | |
let allPassengers = []; | |
for(let i in vehicles) { | |
let veh = vehicles[i]; | |
// manually update all passenger's x/y coords, since the engine doesn't do this! | |
for(let j in veh.peoples) { | |
let person = veh.peoples[j]; | |
person.x = veh.x; | |
person.y = veh.y; | |
} | |
allPassengers = allPassengers.concat(veh.peoples); | |
}; | |
// Filter out people that started walking already | |
for(let i in people) { | |
if(__stillWaiting(people[i], buildings)) { | |
allPassengers.push(people[i]); | |
} else { | |
console.log(people[i].name + ' started walking already with time: ' + people[i].time); | |
} | |
} | |
// allPassengers = allPassengers.concat(people); | |
return allPassengers; | |
}; | |
var __stillWaiting = function(person, buildings) { | |
'use strict'; | |
let origin = __getBuilding(buildings, person.origin); | |
return person.x === origin.x && person.y === origin.y; | |
}; | |
var __isOnCarP = function(vehicle, personName) { | |
'use strict'; | |
for(let p in vehicle.peoples) { | |
if(personName === vehicle.peoples[p].name) { | |
return true; | |
} | |
}; | |
return false; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment