astrid 0.0.1

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: 5805f0aabcdedf0d3f848c806e3dcd84c11108ec
4
+ data.tar.gz: 8122dbbd6d0a2f111154e65f75eb36a5cd72eaca
5
+ SHA512:
6
+ metadata.gz: 72ec33b083718e395523d2dc0a8c9e7268d24d05f1c27ffc5c845f4980e7d689226cf09a75b5f107d55b10d292fa24bda70640fd8468170ebb6366941fc89fcf
7
+ data.tar.gz: 1e17a34f968e762bace8f055800f443a209a452204bd4ffe69c7ebada0677c8a8b53643e999ffdddc9ad2d137307885970dbd00c3a33d95f484686312ece9c4d
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in astrid.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'pry'
8
+ gem "rspec"
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 JHawk
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Astrid
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'astrid'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install astrid
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/astrid/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/astrid.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'astrid/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "astrid"
8
+ spec.version = Astrid::VERSION
9
+ spec.authors = ["JHawk"]
10
+ spec.email = ["josh.r.hawk@gmail.com"]
11
+ spec.summary = %q{A Star in ruby.}
12
+ spec.description = %q{A Star in ruby.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,195 @@
1
+ require 'astrid/heuristics/manhattan'
2
+
3
+ module Pathfinder
4
+ module Finders
5
+ class AStar
6
+
7
+ attr_accessor :allow_diagonal, :current_grid, :current_path
8
+
9
+ def initialize(opts={})
10
+ @allow_diagonal = opts[:allow_diagonal] || false
11
+ @heuristic = opts[:heuristic] || Heuristics::Manhattan
12
+ # @diagonal_movement = opts[:diagonal_movement]
13
+ @weight = opts[:weight] || 1;
14
+ end
15
+
16
+ def sanitize(node)
17
+ node
18
+ end
19
+
20
+ # look at
21
+ # https://github.com/qiao/PathFinding.js/blob/master/src/finders/AStarFinder.js
22
+ # A*
23
+ # requires x & y
24
+ # node = g, f, h, x, y, opened, walkable, closed, parent
25
+ #
26
+ # extract a grid class
27
+ # clearly not optomized at all!
28
+ def find_path(start_node, end_node, grid)
29
+ start_node = sanitize(start_node)
30
+ end_node = sanitize(end_node)
31
+ if grid.nil?
32
+ [start_node]
33
+ else
34
+ @current_grid = grid.clone
35
+ _max_x = grid[:max_x]
36
+ _max_y = grid[:max_y]
37
+
38
+ raise 'max_x & max_y required' unless _max_x && _max_y
39
+
40
+ _start_node = start_node.clone
41
+ _end_node = end_node.clone
42
+
43
+ heuristic = @heuristic.new(_end_node, @weight)
44
+
45
+ _start_node[:f] = 0 # sum of g and h
46
+ _start_node[:g] = 0 # steps to start node
47
+ _start_node[:h] = nil # steps to end node
48
+ _start_node[:opened] = true
49
+
50
+ # use heap or tree for better perf
51
+ open = []
52
+ open.push _start_node
53
+
54
+ while !open.empty? do
55
+ _current_node = open.pop
56
+
57
+ _current_node[:closed] = true
58
+ @current_grid[node_to_a(_current_node)] = _current_node
59
+
60
+ if node_to_a(_current_node) == node_to_a(_end_node)
61
+ return final_path(_current_node)
62
+ end
63
+
64
+ new_g = _current_node[:g] + 1
65
+
66
+ x = _current_node[:x]
67
+ y = _current_node[:y]
68
+
69
+ neighbors = []
70
+
71
+ neighbors << [x-1, y] if x > 0
72
+ neighbors << [x, y-1] if y > 0
73
+ neighbors << [x+1, y] if x < _max_x-1
74
+ neighbors << [x, y+1] if y < _max_y-1
75
+
76
+ _neighbors = neighbors.map do |position|
77
+ node = @current_grid[position]
78
+ if node.nil? || node[:walkable]
79
+ node ||= {}
80
+ @current_grid[position] = node.merge({
81
+ x: position.first,
82
+ y: position[1],
83
+ closed: false,
84
+ opened: false
85
+ })
86
+ end
87
+ end.compact
88
+
89
+ _neighbors.each do |neighbor|
90
+ if (!neighbor[:opened] || new_g < neighbor[:g])
91
+ neighbor[:g] = new_g
92
+ neighbor[:h] ||= heuristic.h(neighbor)
93
+ neighbor[:f] = neighbor[:g] + neighbor[:h]
94
+ neighbor[:parent] = node_to_a(_current_node)
95
+
96
+ if (!neighbor[:opened])
97
+ open.push neighbor
98
+ neighbor[:opened] = true
99
+ else
100
+ # ???
101
+ puts "got here some how!!!"
102
+ end
103
+ end
104
+ end
105
+
106
+ open.sort_by! {|i| [-i[:f], -i[:h]]}
107
+ # grid_p
108
+ end
109
+ end
110
+ end
111
+
112
+ def final_path(_end_node)
113
+ path = [_end_node]
114
+ _current_node = _end_node
115
+
116
+ while _current_node[:parent] do
117
+ parent_position = _current_node[:parent]
118
+ parent_node = @current_grid[parent_position]
119
+
120
+ path << parent_node
121
+ _current_node = parent_node
122
+ end
123
+
124
+ @current_path = path.reverse
125
+ end
126
+
127
+ def find_path_a(start_node, end_node, grid)
128
+ p = find_path(start_node, end_node, grid)
129
+ if p
130
+ p.map do |node|
131
+ node_to_a(node)
132
+ end
133
+ end
134
+ end
135
+
136
+ def find_path_a_with_time(start_node, end_node, grid)
137
+ result, time = find_path_a(start_node, end_node, grid)
138
+ end
139
+
140
+ def grid_from_s_map(str)
141
+ normalize = str.lines.reverse
142
+
143
+ max_x = normalize.map(&:length).max
144
+ max_y = normalize.length
145
+
146
+ y_idx = 0
147
+ normalize.inject({max_x: max_x, max_y: max_y}) do |grid, line|
148
+ line.each_char.each_with_index do |c, x_idx|
149
+ if c == '|' || c == '_'
150
+ grid.merge!(
151
+ [x_idx,y_idx] => { walkable: false}
152
+ )
153
+ end
154
+ end
155
+ y_idx += 1
156
+ grid
157
+ end
158
+ end
159
+
160
+ def visited_positions
161
+ @current_grid.select do |k,v|
162
+ k.is_a?(Array) && (v[:opened] || v[:closed])
163
+ end.map {|a| a[1]}
164
+ end
165
+
166
+ def grid_p
167
+ puts ""
168
+ puts "#"*80
169
+ s = ""
170
+ n = @current_grid[:max_y].to_s.size + 2
171
+ (-@current_grid[:max_y]..0).each do |y|
172
+ (0..@current_grid[:max_x]).each do |x|
173
+ node = @current_grid[[x,-y]]
174
+ if node
175
+ if node[:walkable].nil? || node[:walkable]
176
+ s << node[:f].to_s.rjust(n, " ")
177
+ else
178
+ s << "|".rjust(n, " ")
179
+ end
180
+ else
181
+ s << ''.rjust(n, '.')
182
+ end
183
+ end
184
+ s << "\n"
185
+ end
186
+ puts s
187
+ end
188
+
189
+ def node_to_a(node)
190
+ [node[:x], node[:y]]
191
+ end
192
+ end
193
+ end
194
+ end
195
+
@@ -0,0 +1,17 @@
1
+ module Pathfinder
2
+ module Heuristics
3
+ class Manhattan
4
+
5
+ def initialize(end_node, weight=1)
6
+ @end_x = end_node[:x]
7
+ @end_y = end_node[:y]
8
+ @weight = weight
9
+ end
10
+
11
+ def h(node, weight=@weight)
12
+ weight * ((node[:x] - @end_x).abs + (node[:y] - @end_y).abs)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,3 @@
1
+ module Astrid
2
+ VERSION = "0.0.1"
3
+ end
data/lib/astrid.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "astrid/version"
2
+
3
+ module Astrid
4
+ # Your code goes here...
5
+ end
6
+
7
+ require 'astrid/finders/a_star'
@@ -0,0 +1,230 @@
1
+ require 'spec_helper'
2
+ require 'astrid/finders/a_star'
3
+
4
+ describe 'Pathfinder::Finders::AStar' do
5
+
6
+
7
+ let(:a_star) { Pathfinder::Finders::AStar.new }
8
+
9
+ describe "#grid" do
10
+ describe "#visited_positions" do
11
+ let(:closed) { {:x=>0, :y=>0, :f=>0, :g=>0, :h=>nil, :opened=>true, :closed=>true} }
12
+ let(:opened) { {:x=>0, :y=>0, :f=>0, :g=>0, :h=>nil, :opened=>true} }
13
+ let(:not_visited) { {:x=>0, :y=>0, :f=>0, :g=>0, :h=>nil} }
14
+
15
+ let(:grid) do
16
+ {
17
+ :max_x => 10,
18
+ :max_y => 10,
19
+
20
+ [0,0] => closed,
21
+ [0,1] => opened,
22
+ [0,2] => not_visited
23
+ }
24
+ end
25
+
26
+ before do
27
+ a_star.current_grid = grid
28
+ end
29
+
30
+ subject { a_star.visited_positions }
31
+
32
+ it { is_expected.to include(closed) }
33
+ it { is_expected.to include(opened) }
34
+ it { is_expected.not_to include(not_visited) }
35
+ end
36
+
37
+ describe "#grid_from_s_map" do
38
+ context "when | on map" do
39
+ subject do
40
+ a_star.grid_from_s_map(<<-G)
41
+ .|.
42
+ .|.
43
+ G
44
+ end
45
+
46
+ it 'makes the | not walkable' do
47
+ grid = subject
48
+
49
+ expect(grid[[1,0]][:walkable]).to be_falsey
50
+ end
51
+ end
52
+
53
+ context "when _ on map" do
54
+ subject do
55
+ a_star.grid_from_s_map(<<-G)
56
+ ._.
57
+ ._.
58
+ G
59
+ end
60
+
61
+ it 'makes the _ not walkable' do
62
+ grid = subject
63
+
64
+ expect(grid[[1,0]][:walkable]).to be_falsey
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#find_path" do
71
+ context 'timed' do
72
+ subject { a_star.find_path_a_with_time(position1, position2, grid) }
73
+
74
+ context 'origin to 100,100' do
75
+ let(:position1) { {x: 0, y: 0} }
76
+ let(:position2) { {x: 100, y: 100} }
77
+ let(:grid) { {max_x:10000, max_y:10000} }
78
+
79
+ it 'should not take too long' do
80
+ path = subject
81
+
82
+ expect(path).to include([0,0])
83
+ expect(path).to include([100,100])
84
+ end
85
+ end
86
+ end
87
+
88
+ context 'no diagonals' do
89
+ subject { a_star.find_path_a(position1, position2, grid) }
90
+
91
+ context 'origin to origin' do
92
+ let(:position1) { {x: 0, y: 0} }
93
+ let(:position2) { {x: 0, y: 0} }
94
+ let(:grid) { {max_x:10, max_y:10} }
95
+
96
+ it { is_expected.to eq([[0,0]]) }
97
+ end
98
+
99
+ context 'origin to 1,1' do
100
+ let(:position1) { {x: 0, y: 0} }
101
+ let(:position2) { {x: 1, y: 1} }
102
+
103
+ context "when grid is nil" do
104
+ let(:grid) { nil }
105
+
106
+ it { is_expected.to eq([[0,0]]) }
107
+ end
108
+
109
+ context "when grid contains start and end" do
110
+ let(:grid) { {max_x:2, max_y:2} }
111
+
112
+ it { is_expected.to eq([[0,0],[0,1],[1,1]]) }
113
+ end
114
+ end
115
+
116
+ context 'origin to 2,2' do
117
+ let(:position1) { {x: 0, y: 0} }
118
+ let(:position2) { {x: 2, y: 2} }
119
+ let(:grid) { {max_x:5, max_y:5} }
120
+
121
+ it { is_expected.to include([0,0]) }
122
+ it { is_expected.to include([2,2]) }
123
+
124
+ it 'has the correct length' do
125
+ path = subject
126
+
127
+ expect(path.length).to eq(5)
128
+ end
129
+ end
130
+
131
+ context 'unsolvable' do
132
+ context '1' do
133
+ let(:grid) {
134
+ a_star.grid_from_s_map(<<-G)
135
+ ....|......
136
+ .1..|..2...
137
+ ....|......
138
+ G
139
+ }
140
+
141
+ let(:position1) { {x: 1, y: 1} }
142
+ let(:position2) { {x: 7, y: 1} }
143
+
144
+ it { is_expected.to be_nil }
145
+ end
146
+
147
+ end
148
+
149
+ context 'obstructed paths' do
150
+ context '1' do
151
+ let(:grid) {
152
+ a_star.grid_from_s_map(<<-G)
153
+ ........
154
+ ...........
155
+ ...........
156
+ ...........
157
+ ....|......
158
+ .1..|..2...
159
+ ....|......
160
+ ..........
161
+ ...........
162
+ G
163
+ }
164
+
165
+ let(:position1) { {x: 1, y: 3} }
166
+ let(:position2) { {x: 7, y: 3} }
167
+
168
+ it { is_expected.to include([1,3]) }
169
+ it { is_expected.to include([7,3]) }
170
+
171
+ it { is_expected.not_to include([4,2]) }
172
+ it { is_expected.not_to include([4,3]) }
173
+ it { is_expected.not_to include([4,4]) }
174
+
175
+ it 'has the correct length' do
176
+ path = subject
177
+ expect(path.length).to eq(11)
178
+ end
179
+
180
+ it 'should only visit necessary positions' do
181
+ subject
182
+
183
+ expect(a_star.current_grid).not_to be_nil
184
+ expect(a_star.visited_positions.count).to eq(31)
185
+ end
186
+ end
187
+
188
+ context '2' do
189
+ let(:grid) {
190
+ a_star.grid_from_s_map(<<-G)
191
+ ......|.....
192
+ .1....|.....
193
+ ......|.....
194
+ ......|.....
195
+ ......|..2..
196
+ ......|.....
197
+ ......|.....
198
+ ............
199
+ ............
200
+ ............
201
+ ............
202
+ G
203
+ }
204
+
205
+ let(:position1) { {x: 1, y: 10} }
206
+ let(:position2) { {x: 9, y: 6} }
207
+
208
+ it { is_expected.to include([1,10]) }
209
+ it { is_expected.to include([9,6]) }
210
+
211
+ it { is_expected.not_to include([6,4]) }
212
+ it { is_expected.not_to include([6,5]) }
213
+ it { is_expected.not_to include([6,6]) }
214
+ it { is_expected.not_to include([6,7]) }
215
+ it { is_expected.not_to include([6,8]) }
216
+ it { is_expected.not_to include([6,9]) }
217
+ it { is_expected.not_to include([6,10]) }
218
+ it { is_expected.not_to include([6,11]) }
219
+
220
+ it 'has the correct length' do
221
+ path = subject
222
+
223
+ expect(path.length).to eq(19)
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+
@@ -0,0 +1,4 @@
1
+ RSpec.configure do |config|
2
+ config.order = "random"
3
+ end
4
+
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: astrid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - JHawk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A Star in ruby.
42
+ email:
43
+ - josh.r.hawk@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - astrid.gemspec
54
+ - lib/astrid.rb
55
+ - lib/astrid/finders/a_star.rb
56
+ - lib/astrid/heuristics/manhattan.rb
57
+ - lib/astrid/version.rb
58
+ - spec/astrid/finders/a_star_spec.rb
59
+ - spec/spec_helper.rb
60
+ homepage: ''
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.2.2
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: A Star in ruby.
84
+ test_files:
85
+ - spec/astrid/finders/a_star_spec.rb
86
+ - spec/spec_helper.rb
87
+ has_rdoc: