Skip to content

Instantly share code, notes, and snippets.

@mck-
Created March 22, 2016 23:30
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mck-/db264d115ac69a2fef77 to your computer and use it in GitHub Desktop.
Save mck-/db264d115ac69a2fef77 to your computer and use it in GitHub Desktop.
Routific solution to Local Motion challenge
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