a-star 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/a-star +15 -0
  3. data/lib/A_Star.rb +214 -0
  4. metadata +46 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eabf290e90562cb865fd597d9c7a446ccf6a10ad
4
+ data.tar.gz: 61f5081fdde910f354b26d53dfc0fa7b0479810d
5
+ SHA512:
6
+ metadata.gz: 6542dba427725e0a7f01d8c1ff7f7da16a31d38c340ae6ba3fc767714bbc8f8c8f1a76aa744f42e233c906f1853b482da9dd0a65fde093cb164c403dbc8cc75e
7
+ data.tar.gz: eb8e7625a42a6e7a9e0ce58dd8378d0c19b89d25b59b0edec2180772c1104aff77388853ebe89f427d1529480380baa8ec61e4cc8c67cb308107eaf52720ab6c
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require 'A_Star'
3
+
4
+ image = nil
5
+ begin
6
+ image = ChunkyPNG::Image.from_file(ARGV[ARGV.length - 1])
7
+ rescue
8
+ puts "Please supply correct commandline arguments.\nNo image given or wrong image format, PNG only!"
9
+ end
10
+
11
+ unless image.nil?
12
+ loc = FromTo.new image
13
+ init = AStar.new image, loc.findStart, loc.findEnd
14
+ init.draw
15
+ end
@@ -0,0 +1,214 @@
1
+ require 'chunky_png'
2
+
3
+ class FromTo
4
+ def initialize image
5
+ @image = image
6
+ end
7
+
8
+ def findEnd
9
+ 0.upto @image.dimension.width - 1 do |i|
10
+ red = ChunkyPNG::Color.r(@image[i, @image.dimension.height - 1])
11
+ green = ChunkyPNG::Color.g(@image[i, @image.dimension.height - 1])
12
+ blue = ChunkyPNG::Color.b(@image[i, @image.dimension.height - 1])
13
+
14
+ if red > 255/2 && green > 255/2 && blue > 255/2
15
+ return [i, @image.dimension.height - 1]
16
+ end
17
+ end
18
+ return Array.new
19
+ end
20
+
21
+ def findStart
22
+ 0.upto @image.dimension.width - 1 do |i|
23
+ red = ChunkyPNG::Color.r(@image[i, 0])
24
+ green = ChunkyPNG::Color.g(@image[i, 0])
25
+ blue = ChunkyPNG::Color.b(@image[i, 0])
26
+
27
+ if red > 255/2 && green > 255/2 && blue > 255/2
28
+ return [i, 0]
29
+ end
30
+ end
31
+ return Array.new
32
+ end
33
+ end
34
+
35
+ def node x, y, index, cost, destCost, totalCost
36
+ return [x, y, index, cost, destCost, totalCost]
37
+ end
38
+
39
+ class AStar
40
+ def initialize maze, start, dest
41
+ raise "Either start, end or both locations not found!" if start.empty? || dest.empty?
42
+ @maze = maze
43
+ @start = start
44
+ @dest = dest
45
+ @solvedMaze = @maze
46
+ mazeName = ARGV[ARGV.length - 1]
47
+ @mazeLabel = mazeName.split( /()\s|\./)[0]
48
+ @mazeFileType = "." + mazeName.split(/\s|\./)[1]
49
+
50
+ @firstNode = node start[0], start[1], -1, -1, -1, -1 # [x, y, index, startCost, destcost, heuristic]
51
+ @destNode = node dest[0], dest[1], -1, -1, -1, -1
52
+
53
+ @height = maze.dimension.height
54
+ @width = maze.dimension.width
55
+ @perimiter = (2 * @width) + (2 * @height)
56
+ @area = @width * @height
57
+ @visited = []
58
+ @unvisited = []
59
+ @visited << @firstNode
60
+ end
61
+
62
+ def solve
63
+
64
+ while not @visited.empty? do
65
+ minIndex = 0
66
+ 0.upto @visited.length - 1 do |i|
67
+ if @visited[i][5] < @visited[minIndex][5]
68
+ minIndex = i
69
+ end
70
+ end
71
+ chosenNode = minIndex
72
+
73
+ here = @visited[chosenNode]
74
+
75
+ if here[0] == @destNode[0] && here[1] == @destNode[1]
76
+ path = [@destNode]
77
+ puts "We're here! Final node at: (x: #{here[0]}, y: #{here[1]})"
78
+ while here[2] != -1 do
79
+ here = @unvisited[here[2]]
80
+ path.unshift here
81
+ end
82
+ puts "The entire path from #{@start} to #{@dest} is: \n#{path}"
83
+ path.each do |arr|
84
+ @solvedMaze[arr[0], arr[1]] = ChunkyPNG::Color.from_hex "#752bff"
85
+ end
86
+ return path
87
+ end
88
+
89
+ @visited.delete_at chosenNode
90
+ @unvisited << here
91
+
92
+ friendNodes = lookAround here
93
+ 0.upto friendNodes.length - 1 do |j|
94
+ horizontalFriend = friendNodes[j][0]
95
+ verticalFriend = friendNodes[j][1]
96
+
97
+ if passable? horizontalFriend, verticalFriend || (horizontalFriend = @destNode[0] && verticalFriend == @destNode[1])
98
+ onUnvisited = false
99
+ 0.upto @unvisited.length - 1 do |k|
100
+ unvisitedNode = @unvisited[k]
101
+ if horizontalFriend == unvisitedNode[0] && verticalFriend == unvisitedNode[1]
102
+ onUnvisited = true
103
+ break
104
+ end
105
+ end
106
+ next if onUnvisited
107
+
108
+ onVisited = false
109
+ 0.upto @visited.length - 1 do |k|
110
+ visitedNode = @visited[k]
111
+ if horizontalFriend == visitedNode[0] && verticalFriend == visitedNode[1]
112
+ onVisited = true
113
+ break
114
+ end
115
+ end
116
+ friendHeuristics = Array.new
117
+ for k in 0..friendNodes.length - 1 do
118
+ friendHeuristics << heuristic(friendNodes[k], @dest)
119
+ end
120
+ lowestHeuristic = friendHeuristics.min
121
+ if not onVisited && heuristic(friendNodes[j], @dest) == lowestHeuristic # If you're somwhere new and is fastest
122
+ newNode = node horizontalFriend, verticalFriend, @unvisited.length - 1, -1, -1, -1
123
+ newNode[3] = here[3] + cost(here, newNode)
124
+ newNode[4] = heuristic newNode, @destNode
125
+ newNode[5] = newNode[3] + newNode[4]
126
+
127
+ @visited << newNode
128
+ #puts "!! New Node at\n(x: " + horizontalFriend.to_s + ", y: " + verticalFriend.to_s + ")"
129
+ #puts "Destination = " + @destNode[0].to_s + ", " + @destNode[1].to_s
130
+ # Uncoment below to see unvisited nodes!
131
+ #@solvedMaze[horizontalFriend, verticalFriend] = ChunkyPNG::Color.from_hex "#999"
132
+ end
133
+ end
134
+ end
135
+ end
136
+ return []
137
+ end
138
+
139
+ def heuristic here, destination
140
+ return ( Math.sqrt( ((here[0] - destination[0]) ** 2) + ((here[1] - destination[1]) ** 2) ) ).floor
141
+ end
142
+
143
+ def cost here, destination
144
+ direction = direction here, destination
145
+ if [2, 4, 6, 8].include? direction
146
+ return 10
147
+ end
148
+ return 14
149
+ end
150
+
151
+ def passable? x, y
152
+ if (x < 0 || y < 0) || (x > @width - 1 || y > @height - 1)
153
+ return false
154
+ end
155
+ red = ChunkyPNG::Color.r(@maze[x, y])
156
+ green = ChunkyPNG::Color.g(@maze[x, y])
157
+ blue = ChunkyPNG::Color.b(@maze[x, y])
158
+ if red > 255/2 && green > 255/2 && blue > 255/2
159
+ return true
160
+ end
161
+ return false
162
+ end
163
+
164
+ def direction here, destination
165
+ direction = [ destination[1] - here[1], destination[0] - here[0] ]
166
+ return case
167
+ when direction[0] > 0 && direction[1] == 0
168
+ 2 # y-negative, down
169
+ when direction[1] < 0 && direction[0] == 0
170
+ 4 # x-negative, left
171
+ when direction[0] < 0 && direction[1] == 0
172
+ 8 # y-positive, up
173
+ when direction[1] > 0 && direction[0] == 0
174
+ 6 # x-positive, right
175
+ end
176
+ end
177
+
178
+ def lookAround here
179
+ return [
180
+ [here[0], (here[1] + 1)], # y-positive, up
181
+ [here[0], (here[1] - 1)], # y-negative, down
182
+ [(here[0] + 1), here[1]], # x-positive, right
183
+ [(here[0] - 1), here[1]] # x-negative, left
184
+ ]
185
+ end
186
+
187
+ def draw
188
+ puts "Solving..."
189
+ go = Time.new
190
+
191
+ path = solve # Here we go
192
+ unless path.empty?
193
+ finish = Time.new
194
+ puts "\n\nTime taken to solve: " + (finish - go).to_s + " seconds!"
195
+ minutes = ((finish - go) / 60).round
196
+ if minutes > 0
197
+ if minutes > 1
198
+ puts "Circa " + minutes.to_s + " Minutes."
199
+ else
200
+ puts "Circa " + minutes.to_s + " Minute."
201
+ end
202
+ end
203
+ else
204
+ puts "No solution found, solve function returned empty array for path!\nPlease make sure your maze is solvable!"
205
+ end
206
+
207
+ startColour = "#ff3c5e"
208
+ destColour = "#68ff9f"
209
+ @solvedMaze[@start[0], @start[1]] = ChunkyPNG::Color.from_hex startColour
210
+ @solvedMaze[@dest[0], @dest[1]] = ChunkyPNG::Color.from_hex destColour
211
+
212
+ @solvedMaze.save(@mazeLabel + "-solved" + @mazeFileType)
213
+ end
214
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: a-star
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Demonstrandum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A* Search Algorithm for PNG images
14
+ email: knutsen@jetspace.co
15
+ executables:
16
+ - a-star
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/a-star
21
+ - lib/A_Star.rb
22
+ homepage: https://github.com/Demonstrandum/A-Star
23
+ licenses:
24
+ - GPL-3.0
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.6.11
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: A* Path finding
46
+ test_files: []