pathing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ff3d8c9d22d0eee8f8a4ee996b77e1a41a768339
4
+ data.tar.gz: 2086abcafcf1e8f00cd5e0b3b7620129f2192a29
5
+ SHA512:
6
+ metadata.gz: fe8479732bb37702e52c7b4ad9b230ae95a76d9af7ec36b09be5b5b0a6f03259cdfe83dc64ac7724775d4c002d9322cd9bdaa68900e7afe8a3afcf945e2e3aca
7
+ data.tar.gz: d14ec4fb8c2ce9fdf604d3f09cfa25cc6458f720dbddc643bcb79775b35e81baba95b42408bcbd3f0ddfca58281e09d472fa8d7ffb735ae6c0d71fcb6a620021
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pathing (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ rake (10.4.2)
10
+ rake-compiler (0.9.5)
11
+ rake
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ pathing!
18
+ rake-compiler
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Nick Lowery
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # pathing
2
+ Dijkstra's algorithm pathfinding in Ruby
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'rake/extensiontask'
2
+
3
+ Rake::ExtensionTask.new 'pathing'
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'pathing'
4
+ dir_config(extension_name)
5
+ create_makefile(extension_name)
@@ -0,0 +1,176 @@
1
+ #include "graph.h"
2
+
3
+ extern VALUE Pathing;
4
+ extern VALUE Node;
5
+ VALUE Graph = Qnil;
6
+
7
+ void define_graph_stuff(void)
8
+ {
9
+ Graph = rb_define_class_under(Pathing, "Graph", rb_cObject);
10
+ rb_define_method(Graph, "initialize", Graph_initialize, 1);
11
+ rb_define_method(Graph, "path", Graph_path, 2);
12
+ }
13
+
14
+ /*
15
+ * Creates a new Graph object with the given interface object.
16
+ */
17
+ VALUE Graph_initialize(VALUE self, VALUE interface)
18
+ {
19
+ if (!rb_respond_to(interface, rb_intern("neighbors_for")))
20
+ {
21
+ rb_raise(rb_eArgError, "interface must respond to \"neighbors_for\"");
22
+ return Qnil;
23
+ }
24
+
25
+ if (!rb_respond_to(interface, rb_intern("edge_cost_between")))
26
+ {
27
+ rb_raise(rb_eArgError, "interface must respond to \"edge_cost_between\"");
28
+ return Qnil;
29
+ }
30
+
31
+ rb_iv_set(self, "@interface", interface);
32
+
33
+ return self;
34
+ }
35
+
36
+ /*
37
+ * Returns the neighbors for a given node key as described by the interface.
38
+ */
39
+ VALUE Graph_neighbors(VALUE interface, VALUE key)
40
+ {
41
+ return rb_funcall(interface, rb_intern("neighbors_for"), 1, key);
42
+ }
43
+
44
+ /*
45
+ * Returns the edge cost between the two given node keys as described by the
46
+ * interface.
47
+ */
48
+ VALUE Graph_edge_cost(VALUE interface, VALUE key1, VALUE key2)
49
+ {
50
+ return rb_funcall(interface, rb_intern("edge_cost_between"), 2, key1, key2);
51
+ }
52
+
53
+ /*
54
+ * Calculates a path from the key1 Node to the key2 node using Dijksta's
55
+ * algorithm. Returns an ordered array of the keys of each Node in the path.
56
+ */
57
+ VALUE Graph_path(VALUE self, VALUE key1, VALUE key2)
58
+ {
59
+ DPRINT("pathing from %s to %s\n", RSTRING_PTR(rb_inspect(key1)),
60
+ RSTRING_PTR(rb_inspect(key2)));
61
+ VALUE path = rb_ary_new();
62
+ VALUE interface = rb_iv_get(self, "@interface");
63
+
64
+ // beware: Dijksta's algorithm ahead
65
+
66
+ VALUE settled = rb_ary_new();
67
+ VALUE unsettled = rb_ary_new();
68
+ VALUE prev_nodes = rb_hash_new();
69
+ VALUE costs = rb_hash_new();
70
+ VALUE neighbors;
71
+ VALUE least;
72
+ VALUE args;
73
+
74
+ // add start node to unsettled
75
+ rb_ary_push(unsettled, key1);
76
+ rb_hash_aset(costs, key1, DBL2NUM(0.0));
77
+
78
+ // pathfinding loop
79
+ while (RARRAY_LEN(unsettled) > 0)
80
+ {
81
+ // find least-cost unsettled node and move it to settled
82
+ least = rb_block_call(unsettled, rb_intern("min_by"), 0, NULL,
83
+ value_from_key, costs);
84
+ DPRINT("move %s to settled\n", RSTRING_PTR(rb_inspect(least)));
85
+ rb_ary_delete(unsettled, least);
86
+ if (least == key2) break;
87
+ rb_ary_push(settled, least);
88
+
89
+ // update cost for each neighbor of moved node if less than existing cost
90
+
91
+ args = rb_ary_new();
92
+ rb_ary_push(args, interface);
93
+ rb_ary_push(args, least);
94
+ rb_ary_push(args, costs);
95
+ rb_ary_push(args, prev_nodes);
96
+ rb_ary_push(args, unsettled);
97
+
98
+ neighbors = Graph_neighbors(interface, least);
99
+ if (neighbors == Qnil)
100
+ {
101
+ rb_raise(rb_eRuntimeError, "node %s neighbor collection is nil",
102
+ RSTRING_PTR(rb_inspect(least)));
103
+ return Qnil;
104
+ }
105
+
106
+ rb_block_call(neighbors,
107
+ rb_intern("each"), 0, NULL, update_neighbor_cost, args);
108
+ }
109
+
110
+ // build path from end to start
111
+ VALUE curr_node = key2;
112
+ while (curr_node != Qnil)
113
+ {
114
+ rb_ary_unshift(path, curr_node);
115
+ curr_node = rb_hash_aref(prev_nodes, curr_node);
116
+ }
117
+
118
+ return path;
119
+ }
120
+
121
+ /*
122
+ * Updates the cost to reach the given node neighbor to the other given node.
123
+ */
124
+ VALUE update_neighbor_cost(VALUE neighbor, VALUE args)
125
+ {
126
+ VALUE interface = rb_ary_entry(args, 0);
127
+ VALUE node = rb_ary_entry(args, 1);
128
+ VALUE costs = rb_ary_entry(args, 2);
129
+ VALUE prev_nodes = rb_ary_entry(args, 3);
130
+ VALUE unsettled = rb_ary_entry(args, 4);
131
+
132
+ float old_cost;
133
+ VALUE rb_old_cost = rb_hash_aref(costs, neighbor);
134
+ if (rb_old_cost != Qnil)
135
+ old_cost = (float) NUM2DBL(rb_old_cost);
136
+
137
+ VALUE rb_rel_cost = Graph_edge_cost(interface, node, neighbor);
138
+ if (rb_rel_cost == Qnil)
139
+ {
140
+ rb_raise(rb_eRuntimeError, "cost between nodes %s and %s is nil",
141
+ RSTRING_PTR(rb_inspect(node)), RSTRING_PTR(rb_inspect(neighbor)));
142
+ return Qnil;
143
+ }
144
+
145
+ // cost to start node + cost between start and neighbor node
146
+ float new_cost = (float) NUM2DBL(rb_hash_aref(costs, node)) +
147
+ (float) NUM2DBL(Graph_edge_cost(interface, node, neighbor));
148
+
149
+ if (rb_old_cost != Qnil)
150
+ {
151
+ old_cost = (float) NUM2DBL(rb_old_cost);
152
+ if (new_cost < old_cost)
153
+ {
154
+ rb_hash_aset(costs, neighbor, DBL2NUM(new_cost));
155
+ rb_hash_aset(prev_nodes, neighbor, node);
156
+ rb_ary_push(unsettled, neighbor);
157
+ }
158
+ }
159
+ else
160
+ {
161
+ rb_hash_aset(costs, neighbor, DBL2NUM(new_cost));
162
+ rb_hash_aset(prev_nodes, neighbor, node);
163
+ rb_ary_push(unsettled, neighbor);
164
+ }
165
+
166
+ return Qnil;
167
+ }
168
+
169
+ /*
170
+ * Returns the value in the given hash with the given key. Used as a callback
171
+ * for rb_block_call.
172
+ */
173
+ VALUE value_from_key(VALUE key, VALUE hash)
174
+ {
175
+ return rb_hash_aref(hash, key);
176
+ }
@@ -0,0 +1,10 @@
1
+ #include "ruby.h"
2
+ #include "util.h"
3
+
4
+ void define_graph_stuff(void);
5
+ VALUE Graph_initialize(VALUE self, VALUE interface);
6
+ VALUE Graph_neighbors(VALUE interface, VALUE key);
7
+ VALUE Graph_edge_cost(VALUE interface, VALUE key1, VALUE key2);
8
+ VALUE Graph_path(VALUE self, VALUE key1, VALUE key2);
9
+ VALUE update_neighbor_cost(VALUE neighbor, VALUE args);
10
+ VALUE value_from_key(VALUE key, VALUE hash);
@@ -0,0 +1,10 @@
1
+ #include "pathing.h"
2
+
3
+ VALUE Pathing = Qnil;
4
+
5
+ void Init_pathing(void)
6
+ {
7
+ Pathing = rb_define_module("Pathing");
8
+ define_graph_stuff();
9
+ }
10
+
@@ -0,0 +1,4 @@
1
+ #include "ruby.h"
2
+ #include "graph.h"
3
+
4
+ void Init_pathing(void);
@@ -0,0 +1,8 @@
1
+ #ifndef __PATHING_UTILS_H__
2
+ #define __PATHING_UTILS_H__
3
+
4
+ #define DPRINT(...)
5
+ // uncomment the line below to print pathfinding debug information
6
+ //#define DPRINT(...) (printf(__VA_ARGS__))
7
+
8
+ #endif
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pathing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Lowery
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake-compiler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Dijkstra's algorithm pathfinding
28
+ email: nick.a.lowery@gmail.com
29
+ executables: []
30
+ extensions:
31
+ - ext/pathing/extconf.rb
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - Gemfile.lock
36
+ - LICENSE
37
+ - README.md
38
+ - Rakefile
39
+ - ext/pathing/extconf.rb
40
+ - ext/pathing/graph.c
41
+ - ext/pathing/graph.h
42
+ - ext/pathing/pathing.c
43
+ - ext/pathing/pathing.h
44
+ - ext/pathing/util.h
45
+ homepage: https://github.com/ClockVapor/pathing
46
+ licenses:
47
+ - MIT
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.4.3
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Dijkstra's algorithm pathfinding
69
+ test_files: []