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.
- checksums.yaml +7 -0
- data/bin/planner +41 -0
- data/lib/route_planner.rb +81 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -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
|
data/bin/planner
ADDED
@@ -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:
|