a-star 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.
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: []