chingu-pathfinding 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: 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: []