planner 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|