Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Salesperson assignment submission #13

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions lib/calculates_route.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
class CalculatesRoute

def self.calculate(points)

remaining_points = points
remaining_points = points.clone
route = []
route << remaining_points.slice!(0)
until remaining_points == [] do
until remaining_points == [] do
next_point = shortest_distance(route.last, remaining_points)
route << remaining_points.slice!(remaining_points.index(next_point))
end
Expand All @@ -18,5 +17,17 @@ def self.shortest_distance(from, possible)
end
distances.sort{|a,b| a.fetch(:distance) <=> b.fetch(:distance)}.first.fetch(:point)
end

def self.distance(route)
total_distance = 0.0
route.each_cons(2) do |route_combo|
total_distance += Map.distance_between(route.first,route.last)
end
return total_distance
end

def self.time(route,speed)
(distance(route) / speed).to_f
end
end

1 change: 1 addition & 0 deletions lib/map.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'geocoder'

class Map

def self.search(terms)
Expand Down
5 changes: 3 additions & 2 deletions lib/place.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
require_relative "./map"

class Place
attr_accessor :name, :coordinates
attr_accessor :name, :coordinates, :starting_point

def self.build(name)
def self.build(name, starting_point = false)
results = Map.search(name)
Place.new.tap do |p|
p.name = name
p.coordinates = results.coordinates
p.starting_point ||= starting_point
end
end

Expand Down
28 changes: 25 additions & 3 deletions lib/sales_person.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
class SalesPerson

attr_reader :cities
MILES_PER_HOUR = 55
attr_reader :cities, :routed_cities, :unrouted_cities
def initialize
@cities = []
@unrouted_cities = []
@routed_cities = []
end

def schedule_city(city)
@cities << city unless @cities.include?(city)
[unrouted_cities, cities].each {|a| a << city} unless cities.include?(city)
starting_city = city if city.starting_point
cities.reject{|c| c.starting_point}
cities.unshift(starting_city) unless (cities.include?(starting_city) || starting_city.nil?)
end

def route
CalculatesRoute.calculate(cities)
@routed_cities = CalculatesRoute.calculate(cities)
end

def total_miles
CalculatesRoute.distance(routed_cities)
end

def total_travel_time
CalculatesRoute.time(routed_cities, MILES_PER_HOUR)
end

def formatted_total_travel_time
t = total_travel_time * 60 * 60
mm, ss = t.divmod(60)
hh, mm = mm.divmod(60)
dd, hh = hh.divmod(24)
"%d days, %d hours, %d minutes and %d seconds" % [dd, hh, mm, ss]
end
end
21 changes: 21 additions & 0 deletions sales_person_bm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'benchmark'

Dir["./lib/*.rb"].each {|file| require file }

def run_route(n)
salesperson = SalesPerson.new

all_cities = ["Abilene", "Alamo", "Alamo Heights", "Alice", "Allen", "Alpine", "Alvarado", "Alvin", "Amarillo", "Andrews", "Angleton", "Argyle", "Arlington", "Austin", "Azle", "Balch Springs", "Bastrop", "Bay City", "Baytown", "Beaumont", "Bellaire", "Belton", "Benbrook", "Big Spring", "Boerne", "Brenham", "Bridgeport", "Brownfield", "Brownsville", "Brownwood", "Bryan", "Bulverde", "Burkburnett", "Burleson", "Burnet", "Canyon", "Carrollton", "Carthage", "Cedar Hill", "Cedar Park", "Cleburne", "Clute", "College Station", "Colleyville", "Columbus", "Conroe", "Converse", "Coppell", "Copperas Cove", "Corinth", "Corpus Christi", "Corsicana", "Crockett", "Crowley", "Dallas", "Deer Park", "Del Rio", "Denison", "Denton", "Desoto", "Devine", "Dickinson", "Donna", "Dumas", "Duncanville", "Eagle Pass", "Edinburg", "El Campo", "El Paso", "Ennis", "Euless", "Farmers Branch", "Flower Mound", "Fort Stockton", "Fort Worth", "Fredericksburg", "Freeport", "Friendswood", "Frisco", "Gainesville", "Galveston", "Garden Ridge", "Garland", "Gatesville", "Georgetown", "Gonzales", "Granbury", "Grand Prairie", "Granite Shoals", "Grapevine", "Greenville", "Haltom City", "Hamilton", "Harker Heights", "Harlingen", "Hearne", "Henderson", "Hewitt", "Highland Park", "Highland Village", "Houston", "Humble", "Hunters Creek Village", "Huntsville", "Hurst", "Irving", "Jacksonville", "Jasper", "Jersey Village", "Katy", "Keller", "Kemah", "Kennedale", "Kerrville", "Killeen", "Kingsville", "Krum", "Kyle", "La Grange", "La Porte", "Lacy Lakeview", "Lago Vista", "Lake Jackson", "Lakeway", "Lamesa", "Lampasas", "Lancaster", "Laredo", "League City", "Leander", "Leon Valley", "Levelland", "Lewisville", "Littlefield", "Live Oak", "Lockhart", "Longview", "Lowry Crossing", "Lubbock", "Lucas", "Lufkin", "Mansfield", "Manvel", "Marble Falls", "Marshall", "McAllen", "McKinney", "Mesquite", "Midland", "Midlothian", "Mission", "Missouri City", "Mount Pleasant", "Muleshoe", "Murphy", "Nacogdoches", "Nassau Bay", "New Braunfels", "Newark", "Newton", "North Richland Hills", "Oak Ridge North", "Odessa", "Orange", "Overton", "Palacios", "Palestine", "Pampa", "Paris", "Pasadena", "Pearland", "Perryton", "Pflugerville", "Plainview", "Plano", "Port Aransas", "Port Arthur", "Port Lavaca", "Portland", "Richardson", "Richland Hills", "Ridge City", "Rio Grande City", "River Oaks", "Rockport", "Rockwall", "Rosenberg", "Round Rock", "Rowlett", "Royse City", "Rusk", "Sachse", "Saginaw", "San Angelo", "San Antonio", "San Benito", "San Marcos", "San Saba", "Santa Fe", "Schertz", "Seabrook", "Sealy", "Seguin", "Selma", "Seymour", "Shenandoah", "Sherman", "Smithville", "Snyder", "Socorro", "Sonora", "Southlake", "Southside Place", "Spring Valley", "Stafford", "Stephenville", "Sugar Land", "Sulphur Springs", "Sweeny", "Tahoka", "Taylor", "Temple", "Terrell", "Texarkana", "Texas City", "The Colony", "The Woodlands", "Tomball", "Tyler", "Universal City", "University Park", "Victoria", "Waco", "Watauga", "Waxahachie", "Weatherford", "Webster", "Weslaco", "West Columbia", "West Lake Hills", "West Orange", "West University Place", "Weston", "Wharton", "White Settlement", "Wichita Falls", "Willow Park", "Windcrest", "Woodway", "Wylie", "Yoakum"]

all_cities.shuffle.take(n).each do |city|
salesperson.schedule_city(Place.build("#{city}, TX"))
end
end


Benchmark.bm do |x|
x.report("Benchmarking 2 city route:"){ run_route(2) }
x.report("Benchmarking 10 city route:"){ run_route(10) }
x.report("Benchmarking 50 city route:"){ run_route(50) }
x.report("Benchmarking 200 city route:"){ run_route(100) }
end
13 changes: 11 additions & 2 deletions salesperson.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
phil = SalesPerson.new
phil.schedule_city(Place.build("Dallas, TX"))
phil.schedule_city(Place.build("El Paso, TX"))
phil.schedule_city(Place.build("Austin, TX"))
phil.schedule_city(Place.build("Austin, TX", true))
phil.schedule_city(Place.build("Lubbock, TX"))

puts phil.route
puts "The original route was:"
phil.unrouted_cities.each do |city|
puts city
end
puts "\nThe new route is:"
phil.route.each do |city|
puts city
end
puts "There are a total of #{phil.total_miles.round(2)} miles on the route."
puts "The total travel time is #{phil.formatted_total_travel_time}"
9 changes: 5 additions & 4 deletions spec/map_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require_relative "../lib/map"
require 'geocoder'
require 'rspec'

describe Map do

Expand All @@ -10,17 +11,17 @@
end

it "should use the first item in the array" do
austin = stub("Austin")
dallas = stub("Dallas")
austin = double("Austin")
dallas = double("Dallas")
Geocoder.stub(:search) {[austin, dallas]}
Map.search("austin, tx").should eq(austin)
end
end

describe ":distance" do
it "should calculate distance between two sets of coordinates" do
alpha = stub
beta = stub
alpha = double
beta = double
Geocoder::Calculations.should_receive(:distance_between).with(alpha, beta)
Map.distance_between(alpha, beta)
end
Expand Down
20 changes: 17 additions & 3 deletions spec/place_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require_relative "../lib/place"
require_relative "../lib/map"
require 'rspec'

describe Place do

Expand All @@ -11,19 +12,32 @@
subject.coordinates.should eq([29,-95])
end

it "should have a starting point property" do
subject.should respond_to(:starting_point)
end
it "should not be a starting point by default" do
subject.starting_point.should be_false
end


describe ":build" do
let(:name) { "El Paso, TX"}
let(:result) { stub("el paso", coordinates: [29, -95])}
let(:result) { double("el paso", coordinates: [29, -95])}

it "should build from the map" do
Map.should_receive(:search).with(name).and_return(result)
Place.build(name)
end

it "should be place" do
it "should be place" do
Map.stub(:search).with(name).and_return(result)
Place.build(name).should be_a(Place)
end

it "should be set to the starting point when asked" do
Map.stub(:search).with(name).and_return(result)
Place.build(name).should be_a(Place)
place = Place.build(name, true)
place.starting_point.should be_true
end
end

Expand Down
45 changes: 38 additions & 7 deletions spec/sales_person_spec.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,61 @@
require_relative "../lib/sales_person"
require_relative "../lib/calculates_route"
require_relative "../lib/place"
require 'rspec'

describe SalesPerson do

it "should have many cities" do
city = stub
city = double(:city, starting_point: false)
subject.schedule_city(city)
subject.cities.should include(city)
end

it "should keep the cities only scheduled once" do
city = stub
it "should keep the cities only scheduled once" do
city = double(:city, starting_point: false)
expect{
subject.schedule_city(city)
subject.schedule_city(city)
}.to change(subject.cities,:count).by(1)
}.to change(subject.cities,:count).by(1)
end

it "should calculate a route via the CalculatesRoute" do
cities = [stub, stub, stub]
subject.stub(:cities) { cities }
cities = [double, double, double]
subject.stub(:cities) { cities }
CalculatesRoute.should_receive(:calculate).with(cities)
subject.route
end

it "should returns the route from CalculatesRoute" do
route_stub = [stub, stub]
route_stub = [double, double]
CalculatesRoute.stub(:calculate) { route_stub }
subject.route.should eq(route_stub)
end

context "working with live datasets" do

let (:city){Place.build("Dallas, TX")}
let (:another_city){Place.build("El Paso, TX", true)}
let (:yet_another_city){Place.build("El Paso, TX")}

before :each do
subject.schedule_city(city)
subject.schedule_city(another_city)
subject.schedule_city(yet_another_city)
subject.route
end

it 'should be able to choose his/her starting point' do
subject.cities.should == [another_city, city, yet_another_city]
end

it 'should log the total miles for the route' do
subject.total_miles.should be_within(201.1).of(1100)
end

it 'should output the total traveling time (assuming 55 mph)' do
subject.total_travel_time.should be_within(1).of(20)
end
end

end