planner 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: