chingu-pathfinding 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ext/chingu_pathfinding/chingu_pathfinding.cpp +304 -0
- data/ext/chingu_pathfinding/extconf.rb +17 -0
- data/lib/chingu_pathfinding.rb +5 -0
- metadata +47 -0
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')
|
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: []
|