pathing 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +18 -0
- data/LICENSE +22 -0
- data/README.md +2 -0
- data/Rakefile +3 -0
- data/ext/pathing/extconf.rb +5 -0
- data/ext/pathing/graph.c +176 -0
- data/ext/pathing/graph.h +10 -0
- data/ext/pathing/pathing.c +10 -0
- data/ext/pathing/pathing.h +4 -0
- data/ext/pathing/util.h +8 -0
- metadata +69 -0
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
data/Gemfile.lock
ADDED
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
data/Rakefile
ADDED
data/ext/pathing/graph.c
ADDED
@@ -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
|
+
}
|
data/ext/pathing/graph.h
ADDED
@@ -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);
|
data/ext/pathing/util.h
ADDED
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: []
|