maneuver 0.0.1pre
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.
- data/lib/maneuver.rb +2 -0
- data/lib/maneuver/a_star.rb +56 -0
- data/lib/maneuver/cost_algorithm.rb +29 -0
- data/lib/maneuver/edge.rb +17 -0
- data/lib/maneuver/first_search.rb +66 -0
- data/lib/maneuver/graph.rb +39 -0
- data/lib/maneuver/maneuver.rb +23 -0
- data/lib/maneuver/node.rb +24 -0
- data/lib/maneuver/search_algorithm.rb +7 -0
- metadata +53 -0
data/lib/maneuver.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'maneuver/search_algorithm'
|
2
|
+
|
3
|
+
module Maneuver
|
4
|
+
class AStar < SearchAlgorithm
|
5
|
+
def self.path(from, to, cost_algorithm)
|
6
|
+
raise "EdgeAlgorithm hueristic (param #3 => cost_algorithm) can not be nil" unless cost_algorithm
|
7
|
+
open = [from]
|
8
|
+
estimate = Maneuver.cost_algorithms[cost_algorithm]
|
9
|
+
came_from = {}
|
10
|
+
g_score = { from => 0}
|
11
|
+
f_score = { from => g_score[from] + estimate.compute(from, to)}
|
12
|
+
|
13
|
+
while !open.empty?
|
14
|
+
current = self.key_with_min_value open, f_score
|
15
|
+
return reconstruct_path(came_from, to) if current == to
|
16
|
+
open.delete current
|
17
|
+
current.outgoing_edges.each do |e|
|
18
|
+
n = e.to
|
19
|
+
t_g_score = g_score[current] + e.cost(cost_algorithm)
|
20
|
+
if !open.include? n || t_g_score <= g_score[n]
|
21
|
+
came_from[n] = current
|
22
|
+
g_score[n] = t_g_score
|
23
|
+
f_score[n] = t_g_score + estimate.compute(n, to)
|
24
|
+
open << n unless open.include? n
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.key_with_min_value(set, hash)
|
32
|
+
min_value = Float::MAX
|
33
|
+
min_key = nil
|
34
|
+
set.each do |k|
|
35
|
+
m = hash[k]
|
36
|
+
if m < min_value
|
37
|
+
min_key = k
|
38
|
+
min_value = m
|
39
|
+
end
|
40
|
+
end
|
41
|
+
min_key
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.reconstruct_path(came_from, current)
|
45
|
+
path = [current]
|
46
|
+
while came_from.key? current
|
47
|
+
n = came_from[current]
|
48
|
+
path.unshift n
|
49
|
+
current = n
|
50
|
+
end
|
51
|
+
path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
add_search_algorithm :a_star, AStar
|
56
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Maneuver
|
2
|
+
class CostAlgorithm
|
3
|
+
class << self
|
4
|
+
attr_accessor :cache
|
5
|
+
|
6
|
+
def compute(from, to)
|
7
|
+
0
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_added(method)
|
11
|
+
return unless method.to_sym == :compute
|
12
|
+
self.send :define_singleton_method, :_sing_compute, self.method(method.to_sym)
|
13
|
+
self.singleton_method_added(:_sing_compute)
|
14
|
+
end
|
15
|
+
|
16
|
+
def singleton_method_added(method)
|
17
|
+
return if method.to_sym != :compute &&
|
18
|
+
method.to_sym != :_sing_compute
|
19
|
+
internal = lambda do |from, to|
|
20
|
+
self.cache ||= {}
|
21
|
+
self.cache[[from, to]] ||= begin
|
22
|
+
self.send :compute, from, to
|
23
|
+
end
|
24
|
+
end
|
25
|
+
self.send :define_singleton_method, :_compute, internal
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Maneuver
|
2
|
+
class Edge
|
3
|
+
attr_reader :from, :to
|
4
|
+
|
5
|
+
def initialize(from, to)
|
6
|
+
@from, @to = from, to
|
7
|
+
from.edges << self
|
8
|
+
to.edges << self
|
9
|
+
end
|
10
|
+
|
11
|
+
def cost(algorithm)
|
12
|
+
ca = Maneuver.cost_algorithms[algorithm]
|
13
|
+
raise "Unknow Cost Algorithm: #{algorithm}" unless ca
|
14
|
+
ca._compute(@from, @to)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'maneuver/search_algorithm'
|
2
|
+
|
3
|
+
module Maneuver
|
4
|
+
|
5
|
+
class FirstSearch < SearchAlgorithm
|
6
|
+
def self.prep_node
|
7
|
+
Maneuver::Node.send :define_method, :_fs_parent, lambda { instance_variable_get :@_fs_parent }
|
8
|
+
Maneuver::Node.send :define_method, :_fs_parent=, lambda {|a| instance_variable_set(:@_fs_parent, a)}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.clean_node
|
12
|
+
Maneuver::Node.send :remove_method, :_fs_parent
|
13
|
+
Maneuver::Node.send :remove_method, :_fs_parent=
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.reconstruct_path(node)
|
17
|
+
path = [node]
|
18
|
+
while node._fs_parent
|
19
|
+
path.unshift node._fs_parent
|
20
|
+
node = node._fs_parent
|
21
|
+
end
|
22
|
+
self.clean_node
|
23
|
+
path
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.get_node(nodes_to_check)
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.path(from, to, edge_algorithm = nil)
|
31
|
+
return from if from == to
|
32
|
+
self.prep_node
|
33
|
+
nodes_to_check = []
|
34
|
+
visited = [from]
|
35
|
+
from.outgoing_edges.each { |edge| edge.to._fs_parent = from; nodes_to_check << edge.to }
|
36
|
+
while !nodes_to_check.empty?
|
37
|
+
node = self.get_node nodes_to_check
|
38
|
+
return self.reconstruct_path(to) if node == to
|
39
|
+
node.outgoing_edges.each do |e|
|
40
|
+
test = e.to
|
41
|
+
unless visited.include? test
|
42
|
+
test._fs_parent = node
|
43
|
+
visited << test
|
44
|
+
nodes_to_check.unshift test
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
self.clean_node
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class DFS < FirstSearch
|
54
|
+
def self.get_node(nodes_to_check)
|
55
|
+
nodes_to_check.shift
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class BFS < FirstSearch
|
60
|
+
def self.get_node(nodes_to_check)
|
61
|
+
nodes_to_check.pop
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
[[:bfs, BFS], [:dfs, DFS]].each { |alg| add_search_algorithm alg[0], alg[1]}
|
66
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Maneuver
|
2
|
+
class Graph
|
3
|
+
def initialize(edges = [])
|
4
|
+
@nodes = []
|
5
|
+
@edges = edges.map { |e| Edge.new e[0], e[1] }
|
6
|
+
@edges.each do |e|
|
7
|
+
@nodes << e.to unless @nodes.include? e.to
|
8
|
+
@nodes << e.from unless @nodes.include? e.from
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def insert_nodes(*nodes)
|
13
|
+
nodes.each { |n| @nodes << n unless @nodes.include? n }
|
14
|
+
end
|
15
|
+
|
16
|
+
def insert_edges(*edges)
|
17
|
+
edges.each do |e|
|
18
|
+
unless has_edge e
|
19
|
+
@edges << e
|
20
|
+
@nodes << e.to unless @nodes.include? e.to
|
21
|
+
@nodes << e.from unless @nodes.include? e.from
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_edge(edge)
|
27
|
+
exists = false
|
28
|
+
@edges.each { |e| break if (exists = (e.from == edge.from &&
|
29
|
+
e.to == edge.to)) }
|
30
|
+
exists
|
31
|
+
end
|
32
|
+
|
33
|
+
def path(from, to, search_algorithm, cost_algorithm = nil)
|
34
|
+
search = Maneuver.search_algorithms[search_algorithm]
|
35
|
+
raise "Unknown Search Algorithm: #{search_algorithm}" unless search
|
36
|
+
search.path(from, to, cost_algorithm)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Maneuver
|
2
|
+
@@cost_algorithms = {}
|
3
|
+
@@search_algorithms = {}
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def add_cost_algorithm(sym, klass)
|
7
|
+
@@cost_algorithms[sym] = klass
|
8
|
+
end
|
9
|
+
|
10
|
+
def cost_algorithms
|
11
|
+
@@cost_algorithms
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_search_algorithm(sym, klass)
|
15
|
+
@@search_algorithms[sym] = klass
|
16
|
+
end
|
17
|
+
|
18
|
+
def search_algorithms
|
19
|
+
@@search_algorithms
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Maneuver
|
2
|
+
class Node
|
3
|
+
attr_reader :edges
|
4
|
+
def initialize(*args)
|
5
|
+
@edges = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def outgoing_edges
|
9
|
+
@edges.reject { |e| e.to == self }
|
10
|
+
end
|
11
|
+
|
12
|
+
def incoming_edges
|
13
|
+
@edges.reject { |e| e.from == self }
|
14
|
+
end
|
15
|
+
|
16
|
+
def out_degree
|
17
|
+
outgoing_edges.length
|
18
|
+
end
|
19
|
+
|
20
|
+
def in_degree
|
21
|
+
incoming_edges.length
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maneuver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1pre
|
5
|
+
prerelease: 5
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kayle Gishen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A simple graph library with path planning.
|
15
|
+
email: kayle@moremagic.io
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/maneuver/a_star.rb
|
21
|
+
- lib/maneuver/cost_algorithm.rb
|
22
|
+
- lib/maneuver/edge.rb
|
23
|
+
- lib/maneuver/first_search.rb
|
24
|
+
- lib/maneuver/graph.rb
|
25
|
+
- lib/maneuver/maneuver.rb
|
26
|
+
- lib/maneuver/node.rb
|
27
|
+
- lib/maneuver/search_algorithm.rb
|
28
|
+
- lib/maneuver.rb
|
29
|
+
homepage: https://github.com/kayleg/maneuver
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>'
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.3.1
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.8.24
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: Graph and Path Planning
|
53
|
+
test_files: []
|