chingu-pathfinding 1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5905265e59f555d40f77ca80734ca2817bf181f
4
+ data.tar.gz: 7a39008793acafb2210cbb86b65afa62a7962518
5
+ SHA512:
6
+ metadata.gz: 09dc4135a6379f593c84096aef0855c49073040b6d51e8157a3f5d2badade4befb5e34f440a961bf158308d654c74f7676e3d325cbc479b8e26d9cf37d006c5c
7
+ data.tar.gz: 0eed28da1c204fec071e320f049390ccac15d03c3e4bc3800e13bbc3a482326621aa194192bec3337e38e166aa824803a72943385cf69ee66b39578772165704
@@ -0,0 +1,304 @@
1
+ #include <ruby.h>
2
+ #include <math.h>
3
+ #include <vector>
4
+ #include <map>
5
+ #include <queue>
6
+ #include <set>
7
+ #include <iostream>
8
+ #include <algorithm>
9
+ #include <functional>
10
+ #include <stdexcept>
11
+ #include <sstream>
12
+
13
+ static void Init_pathfinding();
14
+
15
+ extern "C" {
16
+ void Init_chingu_pathfinding() {
17
+ Init_pathfinding();
18
+ }
19
+ }
20
+
21
+ int dir(const long int a, const long int b) {
22
+ if (a == b) return 0;
23
+ if (a < b) return 1;
24
+ return -1;
25
+ }
26
+
27
+ struct point_t {
28
+ long int x, y;
29
+ bool valid;
30
+ point_t(long int xx, long int yy) {
31
+ x = xx;
32
+ y = yy;
33
+ valid = true;
34
+ }
35
+ point_t() : x(0), y(0), valid(false) { }
36
+ point_t(const point_t& p) : x(p.x), y(p.y), valid(p.valid) { }
37
+ };
38
+ typedef std::pair<long int, long int> location_t;
39
+
40
+ long int dist(const location_t& a, const location_t& b) {
41
+ long int dx = a.first-b.first;
42
+ long int dy = a.second-b.second;
43
+ return std::abs(dx) + std::abs(dy);
44
+ }
45
+
46
+ class path_t {
47
+ std::map< location_t, point_t >* map_p;
48
+ std::multimap< location_t, location_t >* connectivity_p;
49
+ long int cost_paid;
50
+
51
+ public:
52
+ std::vector<location_t> path;
53
+ path_t() : cost_paid(0) { }
54
+ path_t(const path_t& p) : map_p(p.map_p), connectivity_p(p.connectivity_p), path(p.path), cost_paid(p.cost_paid) { }
55
+ path_t(std::map< location_t, point_t >* mm,
56
+ std::multimap< location_t, location_t >* cc)
57
+ : map_p(mm), connectivity_p(cc), cost_paid(0), path() { }
58
+ long int get_cost_paid() const { return cost_paid; }
59
+ void add_loc(location_t l, const location_t& goal) {
60
+ if (!path.empty()) {
61
+ location_t last = *path.crbegin();
62
+ cost_paid += dist(last, l);
63
+ }
64
+ path.push_back(l);
65
+ }
66
+ long int dist_to(const location_t& goal) const {
67
+ return dist(*path.crbegin(), goal);
68
+ }
69
+
70
+ location_t end() const {
71
+ if (path.empty()) throw std::runtime_error("no elements in path");
72
+ return *path.crbegin();
73
+ }
74
+ };
75
+
76
+ class by_estimated_cost {
77
+ public:
78
+ location_t goal;
79
+ by_estimated_cost() : goal(0,0) { }
80
+ by_estimated_cost(const by_estimated_cost& p) : goal(p.goal) { }
81
+ by_estimated_cost(location_t& goal) : goal(goal) { }
82
+ bool operator() (const path_t& l, const path_t& r) const {
83
+ return l.get_cost_paid() + l.dist_to(goal) > r.get_cost_paid() + l.dist_to(goal);
84
+ }
85
+ };
86
+
87
+
88
+ class pathfinder_t {
89
+ std::map< location_t, point_t > map; // Spartial hashing
90
+ std::multimap< location_t, location_t > connectivity; // sparse connectivity
91
+ long int height, width, block_size;
92
+ ID blocked_method;
93
+ VALUE self;
94
+
95
+ bool is_blocked(const long int x, const long int y) {
96
+ VALUE blocked = rb_funcall(self, blocked_method, 2, INT2NUM(x), INT2NUM(y));
97
+ return blocked != Qfalse && blocked != Qnil;
98
+ }
99
+
100
+ bool line_blocked(const long int x1, const long int y1, const long int x2, const long int y2) {
101
+ long int dx = dir(x1, x2);
102
+ long int dy = dir(y1, y2);
103
+ long int runner_x = x1;
104
+ long int runner_y = y1;
105
+ while (runner_x * dx <= x2 * dx &&
106
+ runner_y * dy <= y2 * dy) {
107
+ if (is_blocked(runner_x, runner_y)) {
108
+ return true;
109
+ }
110
+ runner_x += dx;
111
+ runner_y += dy;
112
+ }
113
+ return false;
114
+ }
115
+
116
+ public:
117
+ path_t find_path(long int x1, long int y1, long int x2, long int y2) {
118
+ typedef std::priority_queue<path_t, std::vector<path_t>, by_estimated_cost> queue_t;
119
+ location_t start(x1,y1);
120
+ location_t goal(x2,y2);
121
+ by_estimated_cost estimator(goal);
122
+ queue_t queue(estimator);
123
+ std::set<location_t> visited;
124
+ path_t init_path(&map, &connectivity);
125
+ long int next_node_x = (x1 / block_size)*block_size + block_size/2;
126
+ long int next_node_y = (y1 / block_size)*block_size + block_size/2;
127
+ init_path.add_loc(start, goal);
128
+ location_t start_block(next_node_x, next_node_y);
129
+ if (start != start_block) {
130
+ init_path.add_loc(start_block, goal);
131
+ }
132
+ queue.push(init_path);
133
+ while (!queue.empty()) {
134
+ path_t current = queue.top();
135
+ location_t end = current.end();
136
+ queue.pop();
137
+ if (visited.find(end) != visited.end()) continue;
138
+ visited.insert(end);
139
+ if (dist(end, goal) < block_size/2) {
140
+ if (end != goal) {
141
+ current.add_loc(goal, goal);
142
+ }
143
+ return current;
144
+ }
145
+ auto range = connectivity.equal_range(end);
146
+ for (auto c = range.first; c != range.second; c++) {
147
+ auto to = c->second;
148
+ if (visited.find(to) != visited.end()){
149
+ continue;
150
+ }
151
+ path_t new_path = current;
152
+ new_path.add_loc(to, goal);
153
+ queue.push(new_path);
154
+ }
155
+ }
156
+ return path_t();
157
+ }
158
+
159
+ path_t find_path_update(long int x1, long int y1, long int x2, long int y2, std::vector<location_t> current) {
160
+ path_t x;
161
+ // Add every prefix of current to initial guess and converge to new location.
162
+ return x;
163
+ }
164
+
165
+ void Init(VALUE self, long int width, long int height, long int block_size) {
166
+ this->width = width;
167
+ this->height = height;
168
+ this->block_size = block_size;
169
+ this->self = self;
170
+ blocked_method = rb_intern("blocked?");
171
+ // Build internal map:
172
+ for (long int x = block_size/2; x < width; x += block_size) {
173
+ for (long int y = block_size/2; y < height; y += block_size) {
174
+ // add point if it is not blocked
175
+ if (is_blocked(x, y)) continue;
176
+ auto cur_loc = location_t(x,y);
177
+ map[cur_loc] = point_t(x,y);
178
+ // check if point is connected to neighbors
179
+ // ^
180
+ // | o . .
181
+ // | \
182
+ // | o--* .
183
+ // y| / |
184
+ // | o o .
185
+ // +---------->
186
+ // x
187
+ // This means that every possible connection is stored as the map is
188
+ // built.
189
+ auto left = location_t(x-block_size, y);
190
+ if (map[left].valid && !line_blocked(x, y, x-block_size, y)) {
191
+ connectivity.emplace(cur_loc, left);
192
+ connectivity.emplace(left, cur_loc);
193
+ }
194
+ auto down = location_t(x, y-block_size);
195
+ if (map[down].valid && !line_blocked(x, y, x, y-block_size)) {
196
+ connectivity.emplace(cur_loc, down);
197
+ connectivity.emplace(down, cur_loc);
198
+ }
199
+ auto left_down = location_t(x-block_size, y-block_size);
200
+ if (map[left_down].valid && !line_blocked(x, y, x-block_size, y-block_size)) {
201
+ connectivity.emplace(cur_loc, left_down);
202
+ connectivity.emplace(left_down, cur_loc);
203
+ }
204
+ auto left_up = location_t(x-block_size, y+block_size);
205
+ if (map[left_up].valid && !line_blocked(x, y, x-block_size, y+block_size)) {
206
+ connectivity.emplace(cur_loc, left_up);
207
+ connectivity.emplace(left_up, cur_loc);
208
+ }
209
+ }
210
+ }
211
+ // print_map();
212
+ } // END Init
213
+
214
+ std::string print_map() {
215
+ // DEBUGGING
216
+
217
+ auto mapp = std::map<location_t, char>();
218
+ for (long int x = 0; x < width; x++) {
219
+ for (long int y = 0; y < height; y++) {
220
+ mapp[location_t(x, y)] = ' ';
221
+ }
222
+ }
223
+ for (long int x = block_size/2; x < width; x += block_size) {
224
+ for (long int y = block_size/2; y < height; y += block_size) {
225
+ if (!map[location_t(x, y)].valid) {
226
+ mapp[location_t(x, y)] = '~';
227
+ continue;
228
+ }
229
+ location_t cur_loc(x,y);
230
+ mapp[cur_loc] = 'o';
231
+ auto c = connectivity.equal_range(cur_loc);
232
+ for (int i = -1; i <= 1; i++) {
233
+ for (int j = -1; j <= 1; j++) {
234
+ std::pair<const location_t, location_t> item(cur_loc, location_t(x+ i * block_size, y + j*block_size));
235
+ if (std::find(c.first, c.second, item) != c.second) {
236
+ mapp[location_t(x+i, y+j)] = '.';
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+ std::stringstream out;
243
+ out << "map:";
244
+ for (long int y = 0; y < height; y++) {
245
+ out << std::endl;
246
+ for (long int x = 0; x < width; x++) {
247
+ out << mapp[location_t(x, y)];
248
+ }
249
+ }
250
+ out << std::endl;
251
+ return out.str();
252
+ } // END print_map
253
+ };
254
+
255
+ static void free_pathfinder(pathfinder_t* pf) {
256
+ if (pf) {
257
+ delete pf;
258
+ }
259
+ }
260
+
261
+ static VALUE alloc_pathfinder(VALUE klass) {
262
+ pathfinder_t* pf = new pathfinder_t();
263
+ return Data_Wrap_Struct(klass, 0, free_pathfinder, pf);
264
+ }
265
+
266
+ static VALUE init(VALUE self, VALUE width_a, VALUE height_a, VALUE block_size_a) {
267
+ pathfinder_t* pf;
268
+ long int width, height, block_size;
269
+ long int x, y;
270
+ Data_Get_Struct(self, pathfinder_t, pf);
271
+ pf->Init(self, NUM2INT(width_a), NUM2INT(height_a), NUM2INT(block_size_a));
272
+ return self;
273
+ }
274
+
275
+ static VALUE find_path(VALUE self, VALUE x1, VALUE y1, VALUE x2, VALUE y2) {
276
+ pathfinder_t* pf;
277
+ Data_Get_Struct(self, pathfinder_t, pf);
278
+ path_t path = pf->find_path(NUM2INT(x1), NUM2INT(y1), NUM2INT(x2), NUM2INT(y2));
279
+ if (path.path.empty()) return Qnil;
280
+ VALUE* path_ruby = new VALUE[path.path.size()];
281
+ int i = 0;
282
+ for (auto loc: path.path) {
283
+ path_ruby[i++] = rb_ary_new3(2, INT2NUM(loc.first), INT2NUM(loc.second));
284
+ }
285
+ return rb_ary_new4(i, path_ruby);
286
+ }
287
+
288
+ static VALUE print_map(VALUE self) {
289
+ pathfinder_t* pf;
290
+ Data_Get_Struct(self, pathfinder_t, pf);
291
+ std::string s = pf->print_map();
292
+ return rb_str_new(s.c_str(), s.length());
293
+ }
294
+
295
+ typedef VALUE (*rb_method)(...);
296
+
297
+ static void Init_pathfinding() {
298
+ VALUE klass;
299
+ klass = rb_const_get(rb_cObject, rb_intern("Pathfinding"));
300
+ rb_define_alloc_func(klass, alloc_pathfinder);
301
+ rb_define_method(klass, "initialize", (rb_method) init, 3);
302
+ rb_define_method(klass, "find_path", (rb_method) find_path, 4);
303
+ rb_define_method(klass, "to_s", (rb_method) print_map, 0);
304
+ }
@@ -0,0 +1,17 @@
1
+ require 'mkmf'
2
+
3
+ def try_compiler_option(opt, &block)
4
+ result = false
5
+ checking_for "#{opt} option to compiler" do
6
+ if result = try_cpp('', opt, &block)
7
+ $CPPFLAGS += ' ' unless $CFLAGS.empty?
8
+ $CPPFLAGS += opt
9
+ end
10
+ end
11
+ result
12
+ end
13
+
14
+ have_header("ruby.h")
15
+ have_library("stdc++")
16
+ try_compiler_option('-std=c++11')
17
+ create_makefile('chingu_pathfinding/chingu_pathfinding')
@@ -0,0 +1,5 @@
1
+ class Pathfinding
2
+ VERSION = '1.0'
3
+ end
4
+
5
+ require_relative 'chingu_pathfinding/chingu_pathfinding'
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chingu-pathfinding
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - zombiecalypse
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Find paths C supported on a static map with the A* algorithm.
14
+ email: maergil@gmail.com
15
+ executables: []
16
+ extensions:
17
+ - ext/chingu_pathfinding/extconf.rb
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ext/chingu_pathfinding/chingu_pathfinding.cpp
21
+ - ext/chingu_pathfinding/extconf.rb
22
+ - lib/chingu_pathfinding.rb
23
+ homepage: https://github.com/zombiecalypse/chingu-pathfinding
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.2.2
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: Find paths fast with the A* algorithm
47
+ test_files: []