astrid 0.0.1

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: 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: