theseus 1.0.2 → 1.1.0
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 +7 -0
- data/Rakefile +1 -24
- data/bin/theseus +2 -259
- data/lib/theseus/algorithms/base.rb +31 -0
- data/lib/theseus/algorithms/kruskal.rb +81 -0
- data/lib/theseus/algorithms/prim.rb +81 -0
- data/lib/theseus/algorithms/recursive_backtracker.rb +80 -0
- data/lib/theseus/cli.rb +377 -0
- data/lib/theseus/formatters/png.rb +13 -45
- data/lib/theseus/maze.rb +74 -103
- data/lib/theseus/version.rb +2 -2
- data/test/maze_test.rb +9 -9
- metadata +48 -58
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7cab672f9d3d677b4e11d56540a0050cc6f1f26f
|
4
|
+
data.tar.gz: 29094f4def583c3371b6c47ce93370012256def8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 731e708c8c4fadd6f0f934bb42ee65aea8b686fb45ecbe437fa4023550ae6693c5d6579806dcf3b37c7949fecb5082023607a0d3ed5138a2dae8d15a020791a0
|
7
|
+
data.tar.gz: 307256f22616f13b05e4763657b96fe638af856c2bfa08beba59f5509059270a4837a80494395ee61e9102f4e0b2ce62e42f7271985a7577ec3c2d741276e0e9
|
data/Rakefile
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
require 'rake'
|
2
|
-
require '
|
3
|
-
require 'rake/rdoctask'
|
2
|
+
require 'rdoc/task'
|
4
3
|
require 'rake/testtask'
|
5
4
|
|
6
|
-
require './lib/theseus/version'
|
7
|
-
|
8
5
|
task :default => :test
|
9
6
|
|
10
7
|
Rake::TestTask.new do |t|
|
@@ -12,26 +9,6 @@ Rake::TestTask.new do |t|
|
|
12
9
|
t.verbose = true
|
13
10
|
end
|
14
11
|
|
15
|
-
spec = Gem::Specification.new do |s|
|
16
|
-
s.platform = Gem::Platform::RUBY
|
17
|
-
s.summary = "Maze generator for Ruby"
|
18
|
-
s.name = 'theseus'
|
19
|
-
s.version = Theseus::Version::STRING
|
20
|
-
s.files = FileList["README.rdoc", "Rakefile", "lib/**/*.rb", "examples/**/*.rb", "bin/*", "test/**/*.rb"].to_a
|
21
|
-
s.executables << "theseus"
|
22
|
-
s.add_dependency "chunky_png", "~> 0.12.0"
|
23
|
-
s.requirements << "Ruby 1.9"
|
24
|
-
s.description = "Theseus is a library for building random mazes."
|
25
|
-
s.author = "Jamis Buck"
|
26
|
-
s.email = "jamis@jamisbuck.org"
|
27
|
-
s.homepage = "http://github.com/jamis/theseus"
|
28
|
-
end
|
29
|
-
|
30
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
31
|
-
pkg.need_zip = true
|
32
|
-
pkg.need_tar = true
|
33
|
-
end
|
34
|
-
|
35
12
|
Rake::RDocTask.new do |rd|
|
36
13
|
rd.main = "README.rdoc"
|
37
14
|
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
data/bin/theseus
CHANGED
@@ -1,262 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require '
|
4
|
-
require 'theseus'
|
5
|
-
require 'theseus/formatters/png'
|
3
|
+
require 'theseus/cli'
|
6
4
|
|
7
|
-
|
8
|
-
"ortho" => Theseus::OrthogonalMaze,
|
9
|
-
"delta" => Theseus::DeltaMaze,
|
10
|
-
"sigma" => Theseus::SigmaMaze,
|
11
|
-
"upsilon" => Theseus::UpsilonMaze
|
12
|
-
}
|
13
|
-
|
14
|
-
animate = false
|
15
|
-
output = "maze"
|
16
|
-
sparse = 0
|
17
|
-
unicursal = false
|
18
|
-
type = "ortho"
|
19
|
-
format = :ascii
|
20
|
-
|
21
|
-
png_opts = Theseus::Formatters::PNG::DEFAULTS.dup
|
22
|
-
maze_opts = { mask: nil, width: nil, height: nil,
|
23
|
-
randomness: 50, weave: 0, symmetry: :none, braid: 0, wrap: :none,
|
24
|
-
entrance: nil, exit: nil }
|
25
|
-
|
26
|
-
OptionParser.new do |opts|
|
27
|
-
opts.separator ""
|
28
|
-
opts.separator "Required options:"
|
29
|
-
|
30
|
-
opts.on("-w", "--width N", Integer, "width of the maze (default 20, or mask width)") do |w|
|
31
|
-
maze_opts[:width] = w
|
32
|
-
end
|
33
|
-
|
34
|
-
opts.on("-H", "--height N", Integer, "height of the maze (default 20 or mask height)") do |h|
|
35
|
-
maze_opts[:height] = h
|
36
|
-
end
|
37
|
-
|
38
|
-
opts.on("-m", "--mask FILE", "png file to use as mask") do |m|
|
39
|
-
case m
|
40
|
-
when /^triangle:(\d+)$/ then maze_opts[:mask] = Theseus::TriangleMask.new($1.to_i)
|
41
|
-
else maze_opts[:mask] = Theseus::Mask.from_png(m)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
opts.separator ""
|
46
|
-
opts.separator "Output options:"
|
47
|
-
|
48
|
-
opts.on("-a", "--[no-]animate", "emit frames for each step") do |v|
|
49
|
-
animate = v
|
50
|
-
end
|
51
|
-
|
52
|
-
opts.on("-o", "--output FILE", "where to save the file(s) (for png only)") do |f|
|
53
|
-
output = f
|
54
|
-
end
|
55
|
-
|
56
|
-
opts.on("-f", "--format FMT", "png, ascii (default #{format})") do |f|
|
57
|
-
format = f.to_sym
|
58
|
-
end
|
59
|
-
|
60
|
-
opts.on("-V", "--solve [METHOD]", "whether to display the solution of the maze.", "METHOD is either `backtracker' (the default) or `astar'") do |s|
|
61
|
-
png_opts[:solution] = (s || :backtracker).to_sym
|
62
|
-
end
|
63
|
-
|
64
|
-
opts.separator ""
|
65
|
-
opts.separator "Maze options:"
|
66
|
-
|
67
|
-
opts.on("-s", "--seed N", Integer, "random seed to use") do |s|
|
68
|
-
srand(s)
|
69
|
-
end
|
70
|
-
|
71
|
-
opts.on("-t", "--type TYPE", "#{type_map.keys.sort.join(",")} (default: #{type})") do |t|
|
72
|
-
type = t
|
73
|
-
end
|
74
|
-
|
75
|
-
opts.on("-u", "--[no-]unicursal", "generate a unicursal maze (results in 2x size)") do |u|
|
76
|
-
unicursal = u
|
77
|
-
end
|
78
|
-
|
79
|
-
opts.on("-y", "--symmetry TYPE", "one of none,x,y,xy,radial (default is '#{maze_opts[:symmetry]}')") do |s|
|
80
|
-
maze_opts[:symmetry] = s.to_sym
|
81
|
-
end
|
82
|
-
|
83
|
-
opts.on("-e", "--weave N", Integer, "0-100, chance of a passage to go over/under another (default #{maze_opts[:weave]})") do |v|
|
84
|
-
maze_opts[:weave] = v
|
85
|
-
end
|
86
|
-
|
87
|
-
opts.on("-r", "--random N", Integer, "0-100, randomness of maze (default #{maze_opts[:randomness]})") do |r|
|
88
|
-
maze_opts[:randomness] = r
|
89
|
-
end
|
90
|
-
|
91
|
-
opts.on("-S", "--sparse N", Integer, "how sparse to make the maze (default #{sparse})") do |s|
|
92
|
-
sparse = s
|
93
|
-
end
|
94
|
-
|
95
|
-
opts.on("-d", "--braid N", Integer, "0-100, percentage of deadends to remove (default #{maze_opts[:braid]})") do |b|
|
96
|
-
maze_opts[:braid] = b
|
97
|
-
end
|
98
|
-
|
99
|
-
opts.on("-R", "--wrap axis", "none,x,y,xy (default #{maze_opts[:wrap]})") do |w|
|
100
|
-
maze_opts[:wrap] = w.to_sym
|
101
|
-
end
|
102
|
-
|
103
|
-
opts.on("-E", "--enter [X,Y]", "the entrance of the maze (default -1,0)") do |s|
|
104
|
-
maze_opts[:entrance] = s.split(/,/).map { |v| v.to_i }
|
105
|
-
end
|
106
|
-
|
107
|
-
opts.on("-X", "--exit [X,Y]", "the exit of the maze (default width,height-1)") do |s|
|
108
|
-
maze_opts[:exit] = s.split(/,/).map { |v| v.to_i }
|
109
|
-
end
|
110
|
-
|
111
|
-
opts.separator ""
|
112
|
-
opts.separator "Formatting options:"
|
113
|
-
|
114
|
-
opts.on("-B", "--background COLOR", "rgba hex background color for maze (default %08X)" % png_opts[:background]) do |c|
|
115
|
-
png_opts[:background] = c
|
116
|
-
end
|
117
|
-
|
118
|
-
opts.on("-C", "--cellcolor COLOR", "rgba hex cell color for maze (default %08X)" % png_opts[:cell_color]) do |c|
|
119
|
-
png_opts[:cell_color] = c
|
120
|
-
end
|
121
|
-
|
122
|
-
opts.on("-L", "--wallcolor COLOR", "rgba hex wall color for maze (default %08X)" % png_opts[:wall_color]) do |c|
|
123
|
-
png_opts[:wall_color] = c
|
124
|
-
end
|
125
|
-
|
126
|
-
opts.on("-U", "--solutioncolor COLOR", "rgba hex color for the answer path (default %08X)" % png_opts[:solution_color]) do |c|
|
127
|
-
png_opts[:solution_color] = c
|
128
|
-
end
|
129
|
-
|
130
|
-
opts.on("-c", "--cell N", Integer, "size of each cell (default #{png_opts[:cell_size]})") do |c|
|
131
|
-
png_opts[:cell_size] = c
|
132
|
-
end
|
133
|
-
|
134
|
-
opts.on("-b", "--border N", Integer, "border padding around outside (default #{png_opts[:outer_padding]})") do |c|
|
135
|
-
png_opts[:outer_padding] = c
|
136
|
-
end
|
137
|
-
|
138
|
-
opts.on("-p", "--padding N", Integer, "padding around cell (default #{png_opts[:cell_padding]})") do |c|
|
139
|
-
png_opts[:cell_padding] = c
|
140
|
-
end
|
141
|
-
|
142
|
-
opts.on("-W", "--wall N", Integer, "thickness of walls (default #{png_opts[:wall_width]})") do |c|
|
143
|
-
png_opts[:wall_width] = c
|
144
|
-
end
|
145
|
-
|
146
|
-
opts.separator ""
|
147
|
-
opts.separator "Other options:"
|
148
|
-
|
149
|
-
opts.on_tail("-v", "--version", "display the Theseus version and exit") do
|
150
|
-
maze = Theseus::OrthogonalMaze.generate(width: 20, height: 4)
|
151
|
-
s = maze.to_s(mode: :lines).strip
|
152
|
-
print s.gsub(/^/, " ").sub(/^\s*/, "theseus --")
|
153
|
-
|
154
|
-
require 'theseus/version'
|
155
|
-
puts "--> v#{Theseus::Version::STRING}"
|
156
|
-
puts "a maze generator, renderer, and solver by Jamis Buck <jamis@jamisbuck.org>"
|
157
|
-
exit
|
158
|
-
end
|
159
|
-
|
160
|
-
opts.on_tail("-h", "--help", "this helpful list of options") do
|
161
|
-
puts opts
|
162
|
-
exit
|
163
|
-
end
|
164
|
-
end.parse!
|
165
|
-
|
166
|
-
# default width to height, and vice-versa
|
167
|
-
maze_opts[:width] ||= maze_opts[:height]
|
168
|
-
maze_opts[:height] ||= maze_opts[:width]
|
169
|
-
|
170
|
-
if maze_opts[:mask].nil? && (maze_opts[:width].nil? || maze_opts[:height].nil?)
|
171
|
-
warn "You must specify either a mask (-m) or the maze dimensions(-w or -H)."
|
172
|
-
abort "Try --help for a full list of options."
|
173
|
-
end
|
174
|
-
|
175
|
-
if animate
|
176
|
-
abort "sparse cannot be used for animated mazes" if sparse > 0
|
177
|
-
abort "cannot animate unicursal mazes" if unicursal
|
178
|
-
|
179
|
-
png_opts[:background] = ChunkyPNG::Color.from_hex(png_opts[:background]) unless Fixnum === png_opts[:background]
|
180
|
-
solution = png_opts.delete(:solution)
|
181
|
-
|
182
|
-
if png_opts[:background] & 0xFF != 0xFF
|
183
|
-
warn "if you intend to make a movie out of the frames from the animation,"
|
184
|
-
warn "it is HIGHLY RECOMMENDED that you use a fully opaque background color."
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
if unicursal
|
189
|
-
unicursal_entrance = maze_opts.delete(:entrance)
|
190
|
-
maze_opts[:entrance] = [0,0]
|
191
|
-
maze_opts[:exit] = [0,0]
|
192
|
-
end
|
193
|
-
|
194
|
-
maze_opts[:mask] ||= Theseus::TransparentMask.new(maze_opts[:width], maze_opts[:height])
|
195
|
-
maze_opts[:width] ||= maze_opts[:mask].width
|
196
|
-
maze_opts[:height] ||= maze_opts[:mask].height
|
197
|
-
maze = type_map[type].new(maze_opts)
|
198
|
-
|
199
|
-
if unicursal && !maze.respond_to?(:to_unicursal)
|
200
|
-
abort "#{type} mazes do not support the -u (unicursal) option"
|
201
|
-
end
|
202
|
-
|
203
|
-
if animate
|
204
|
-
step = 0
|
205
|
-
maze.generate! do
|
206
|
-
if format == :ascii
|
207
|
-
system "clear"
|
208
|
-
puts maze.to_s(:mode => :unicode)
|
209
|
-
sleep 0.05
|
210
|
-
else
|
211
|
-
f = "%s-%04d.png" % [output, step]
|
212
|
-
step += 1
|
213
|
-
File.open(f, "w") { |io| io.write(maze.to(:png, png_opts)) }
|
214
|
-
print "."
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
if format == :ascii
|
219
|
-
system "clear"
|
220
|
-
puts maze.to_s(:mode => :unicode)
|
221
|
-
else
|
222
|
-
f = "%s-%04d.png" % [output, step]
|
223
|
-
File.open(f, "w") { |io| io.write(maze.to(:png, png_opts)) }
|
224
|
-
print "."
|
225
|
-
|
226
|
-
if solution
|
227
|
-
solver = maze.new_solver(type: solution)
|
228
|
-
|
229
|
-
while solver.step
|
230
|
-
path = solver.to_path(color: png_opts[:solution_color])
|
231
|
-
|
232
|
-
step += 1
|
233
|
-
f = "%s-%04d.png" % [output, step]
|
234
|
-
File.open(f, "w") { |io| io.write(maze.to(:png, png_opts.merge(paths: [path]))) }
|
235
|
-
print "."
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
puts
|
240
|
-
puts "done, %d frames written to %s-*.png" % [step+1, output]
|
241
|
-
end
|
242
|
-
else
|
243
|
-
maze.generate!
|
244
|
-
sparse.times { maze.sparsify! }
|
245
|
-
|
246
|
-
if unicursal
|
247
|
-
enter_at = unicursal_entrance || [-1,0]
|
248
|
-
if enter_at[0] > 0 && enter_at[0] < width*2
|
249
|
-
exit_at = [enter_at[0]+1, enter_at[1]]
|
250
|
-
else
|
251
|
-
exit_at = [enter_at[0], enter_at[1]+1]
|
252
|
-
end
|
253
|
-
maze = maze.to_unicursal(entrance: enter_at, exit: exit_at)
|
254
|
-
end
|
255
|
-
|
256
|
-
if format == :ascii
|
257
|
-
puts maze.to_s(:mode => :unicode)
|
258
|
-
else
|
259
|
-
File.open(output + ".png", "w") { |io| io.write(maze.to(:png, png_opts)) }
|
260
|
-
puts "maze written to #{output}.png"
|
261
|
-
end
|
262
|
-
end
|
5
|
+
Theseus::CLI.run(ARGV)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Theseus
|
2
|
+
module Algorithms
|
3
|
+
# A minimal abstract superclass for maze algorithms to descend
|
4
|
+
# from, mostly as a helper to provide some basic, common
|
5
|
+
# functionality.
|
6
|
+
class Base
|
7
|
+
# The maze object that the algorithm will operate on.
|
8
|
+
attr_reader :maze
|
9
|
+
|
10
|
+
# Create a new algorithm object that will operate on the
|
11
|
+
# given maze.
|
12
|
+
def initialize(maze, options={})
|
13
|
+
@maze = maze
|
14
|
+
@pending = true
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns true if the algorithm has not yet completed.
|
18
|
+
def pending?
|
19
|
+
@pending
|
20
|
+
end
|
21
|
+
|
22
|
+
# Execute a single step of the algorithm. Return true
|
23
|
+
# if the algorithm is still pending, or false if it has
|
24
|
+
# completed.
|
25
|
+
def step
|
26
|
+
return false unless pending?
|
27
|
+
do_step
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'theseus/algorithms/base'
|
2
|
+
|
3
|
+
module Theseus
|
4
|
+
module Algorithms
|
5
|
+
# Kruskal's algorithm is a means of finding a minimum spanning tree for a
|
6
|
+
# weighted graph. By changing how edges are selected, it becomes suitable
|
7
|
+
# for use as a maze generator.
|
8
|
+
#
|
9
|
+
# The mazes it generates tend to have a lot of short cul-de-sacs, which
|
10
|
+
# on the one hand makes the maze look "spiky", but on the other hand
|
11
|
+
# can potentially make the maze harder to solve.
|
12
|
+
#
|
13
|
+
# This implementation of Kruskal's algorithm does not support weave
|
14
|
+
# mazes.
|
15
|
+
class Kruskal < Base
|
16
|
+
class TreeSet #:nodoc:
|
17
|
+
attr_accessor :parent
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@parent = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def root
|
24
|
+
@parent ? @parent.root : self
|
25
|
+
end
|
26
|
+
|
27
|
+
def connected?(tree)
|
28
|
+
root == tree.root
|
29
|
+
end
|
30
|
+
|
31
|
+
def connect(tree)
|
32
|
+
tree.root.parent = self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(maze, options={}) #:nodoc:
|
37
|
+
super
|
38
|
+
|
39
|
+
if @maze.weave > 0
|
40
|
+
raise ArgumentError, "weave mazes cannot be generated with kruskal's algorithm"
|
41
|
+
end
|
42
|
+
|
43
|
+
@sets = Array.new(@maze.height) { Array.new(@maze.width) { TreeSet.new } }
|
44
|
+
@edges = []
|
45
|
+
|
46
|
+
maze.height.times do |y|
|
47
|
+
maze.row_length(y).times do |x|
|
48
|
+
next unless @maze.valid?(x, y)
|
49
|
+
@maze.potential_exits_at(x, y).each do |dir|
|
50
|
+
dx, dy = @maze.dx(dir), @maze.dy(dir)
|
51
|
+
if (dx < 0 || dy < 0) && @maze.valid?(x+dx, y+dy)
|
52
|
+
weight = rand(100) < @maze.randomness ? 0.5 + rand : 1
|
53
|
+
@edges << [x, y, dir, weight]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@edges = @edges.sort_by { |e| e.last }
|
60
|
+
end
|
61
|
+
|
62
|
+
def do_step #:nodoc:
|
63
|
+
until @edges.empty?
|
64
|
+
x, y, direction, _ = @edges.pop
|
65
|
+
nx, ny = x + @maze.dx(direction), y + @maze.dy(direction)
|
66
|
+
|
67
|
+
set1, set2 = @sets[y][x], @sets[ny][nx]
|
68
|
+
unless set1.connected?(set2)
|
69
|
+
set1.connect(set2)
|
70
|
+
@maze.apply_move_at(x, y, direction)
|
71
|
+
@maze.apply_move_at(nx, ny, @maze.opposite(direction))
|
72
|
+
return true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
@pending = false
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'theseus/algorithms/base'
|
2
|
+
|
3
|
+
module Theseus
|
4
|
+
module Algorithms
|
5
|
+
class Prim < Base
|
6
|
+
IN = 0x10000 # indicate that a cell, though blank, is part of the IN set
|
7
|
+
FRONTIER = 0x20000 # indicate that a cell is part of the frontier set
|
8
|
+
|
9
|
+
def initialize(maze, options={}) #:nodoc:
|
10
|
+
super
|
11
|
+
|
12
|
+
if @maze.weave > 0
|
13
|
+
raise ArgumentError, "weave mazes cannot be generated with prim's algorithm"
|
14
|
+
end
|
15
|
+
|
16
|
+
@frontier = []
|
17
|
+
|
18
|
+
loop do
|
19
|
+
y = rand(@maze.height)
|
20
|
+
x = rand(@maze.row_length(y))
|
21
|
+
next unless @maze.valid?(x, y)
|
22
|
+
|
23
|
+
mark_cell(x, y)
|
24
|
+
break
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Iterates over each cell in the frontier space, yielding the coordinates
|
29
|
+
# of each one.
|
30
|
+
def each_frontier
|
31
|
+
@frontier.each do |x, y|
|
32
|
+
yield x, y
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def do_step #:nodoc:
|
37
|
+
if rand(100) < @maze.randomness
|
38
|
+
x, y = @frontier.delete_at(rand(@frontier.length))
|
39
|
+
else
|
40
|
+
x, y = @frontier.pop
|
41
|
+
end
|
42
|
+
|
43
|
+
neighbors = find_neighbors_of(x, y)
|
44
|
+
direction, nx, ny = neighbors[rand(neighbors.length)]
|
45
|
+
|
46
|
+
@maze.apply_move_at(x, y, direction)
|
47
|
+
@maze.apply_move_at(nx, ny, @maze.opposite(direction))
|
48
|
+
|
49
|
+
mark_cell(x, y)
|
50
|
+
|
51
|
+
@pending = @frontier.any?
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def mark_cell(x, y) #:nodoc:
|
57
|
+
@maze[x, y] |= IN
|
58
|
+
@maze[x, y] &= ~FRONTIER
|
59
|
+
|
60
|
+
@maze.potential_exits_at(x, y).each do |dir|
|
61
|
+
nx, ny = x + @maze.dx(dir), y + @maze.dy(dir)
|
62
|
+
if @maze.valid?(nx, ny) && @maze[nx, ny] == 0
|
63
|
+
@maze[nx, ny] |= FRONTIER
|
64
|
+
@frontier << [nx, ny]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_neighbors_of(x, y) #:nodoc:
|
70
|
+
list = []
|
71
|
+
|
72
|
+
@maze.potential_exits_at(x, y).each do |dir|
|
73
|
+
nx, ny = x + @maze.dx(dir), y + @maze.dy(dir)
|
74
|
+
list << [dir, nx, ny] if @maze.valid?(nx,ny) && @maze[nx, ny] & IN != 0
|
75
|
+
end
|
76
|
+
|
77
|
+
return list
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|