planner 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/planner +41 -0
  3. data/lib/route_planner.rb +81 -0
  4. metadata +46 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 63093d4582c46625834f301072d0028811166a5a
4
+ data.tar.gz: d214c079dd8c04a60cdfbc52d3bbc9e3b7efb6ce
5
+ SHA512:
6
+ metadata.gz: eed262eeb94e21853e3406b9af3700c8303994e40a00e5bfb0afa7270806370de518eb171e31d636266a6a4c3b382336f8c7b7a4276e71abfad1825172c13e00
7
+ data.tar.gz: 569dd0f55ee1ecaba3dbe8ddacdd43938f1854355052c0c6c50f4c7bfcd26fd291453a6f0458381ebecc2f489b607d1243e8d31544aaad37ff8e561e88391856
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless ARGV.first and File.exist? ARGV.first
4
+ abort "Type: #{$PROGRAM_NAME} samples/sample.txt"
5
+ end
6
+
7
+ $LOAD_PATH << File.join(File.dirname(__FILE__), *%w[.. lib])
8
+
9
+ require "route_planner"
10
+
11
+ planner = nil
12
+
13
+ def answer(task, result)
14
+ puts "#{task}: #{result}"
15
+ end
16
+
17
+ File.foreach(ARGV.first) do |line|
18
+
19
+ case line
20
+
21
+ when /\AGRAPH: (.+)/
22
+ planner = RoutePlanner.new($1)
23
+
24
+ when /\ADISTANCE (\w+): (\w+)/
25
+ if planner
26
+ answer $1, planner.distance($2) || 'NO SUCH ROUTE'
27
+ end
28
+
29
+ when /\ASHORTEST (\w+): (\w) (\w)/
30
+ if planner
31
+ answer $1, planner.shortest_distance($2, $3) || 'NO SUCH ROUTE'
32
+ end
33
+
34
+ when /\ACOUNT_ROUTES (\w+): (\w) (\w) (\d+) (\d+) (\d+)/
35
+ if planner
36
+ opts = { :min_stops => $4.to_i, :max_stops => $5.to_i, :max_distance => $6.to_i }
37
+ answer $1, planner.trips_number($2, $3, opts)
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,81 @@
1
+
2
+
3
+ class RoutePlanner
4
+ Infinity = 1.0 / 0
5
+
6
+ def initialize(graph_info)
7
+ @graph = { }
8
+ graph_info.scan(/\w\w\d+/).each do |x|
9
+ start = x[0,1]
10
+ finish = x[1,1]
11
+ @graph[start] ||= { }
12
+ @graph[start][finish] = x[2..-1].to_i
13
+ end
14
+ end
15
+
16
+ # Determines the distance between two points (towns).
17
+ # Returns nil if no path is available.
18
+ def distance(path)
19
+ dist = 0
20
+ path.each_char.map.inject do |prev, cur|
21
+ return nil unless @graph[prev][cur]
22
+ dist += @graph[prev][cur]
23
+ cur
24
+ end
25
+ dist
26
+ end
27
+
28
+ # Number of possible trips between two points (towns)
29
+ def trips_number(start, finish, opts = { })
30
+ opts = { :min_stops => 1, :max_stops => Infinity, :max_distance => Infinity }.merge(opts)
31
+ total = 0
32
+ explore_routes(start, finish, opts) { total += 1}
33
+ total
34
+ end
35
+
36
+ def shortest_distance(start, finish)
37
+ all_nodes = @graph.each.map { |k,v| [k, v.keys] }.flatten.uniq
38
+ dist = { }
39
+ all_nodes.each { |x| dist[x] = Infinity }
40
+ dist[start] = 0
41
+ if start == finish then
42
+
43
+ all_nodes.delete(start)
44
+ dist[start] = Infinity
45
+ @graph[start].each { |nxt, how_far| dist[nxt] = how_far }
46
+ end
47
+
48
+ while not all_nodes.empty? do
49
+ min_node = all_nodes.min { |a,b| dist[a] <=> dist[b] }
50
+ if !min_node || min_node == Infinity
51
+ return nil
52
+ end
53
+ all_nodes.delete(min_node)
54
+
55
+ @graph[min_node].each_key do |v|
56
+ alt = dist[min_node] + (@graph[min_node][v] || Infinity)
57
+ dist[v] = alt if dist[v] && alt < dist[v]
58
+ end
59
+ end
60
+ return nil if dist[finish] == Infinity
61
+ dist[finish]
62
+ end
63
+
64
+ private
65
+
66
+ # Traverses the graph until maximum stops or distance is reached.
67
+ def explore_routes(start, finish, opts, stats = {:distance => 0, :stops => 0}, &callback)
68
+ lower_bound_ok = stats[:stops] >= opts[:min_stops]
69
+ upper_bound_ok = stats[:distance] <= opts[:max_distance] && stats[:stops] <= opts[:max_stops]
70
+
71
+ callback.call(stats[:path]) if start == finish && lower_bound_ok && upper_bound_ok
72
+ # Recursion basis: stop exploration when reached upper-bound restrictions
73
+ if upper_bound_ok
74
+ @graph[start].each do |nxt, dist|
75
+ inner_stats = {:distance => stats[:distance] + dist, :stops => stats[:stops] + 1}
76
+ explore_routes(nxt, finish, opts, inner_stats, &callback)
77
+ end
78
+ end
79
+ end
80
+
81
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: planner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Marc Bluemner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Train route planner
14
+ email: marc.bluemner@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - bin/planner
20
+ - lib/route_planner.rb
21
+ homepage: https://rubygems.org/gems/planner
22
+ licenses:
23
+ - apache
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 2.2.2
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: Planner!
45
+ test_files: []
46
+ has_rdoc: