maze 1.0.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.
@@ -0,0 +1,3 @@
1
+ .rvmrc
2
+ *.png
3
+
@@ -0,0 +1 @@
1
+ rvm use 1.9.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mazes (1.0.0)
5
+ oily_png
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ chunky_png (1.2.5)
11
+ oily_png (1.0.2)
12
+ chunky_png (~> 1.2.1)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ mazes!
@@ -0,0 +1,55 @@
1
+ ## Overview
2
+
3
+ This is a library for generating perfect mazes. The default tessellation is orthogonal. It supports ASCII output.
4
+
5
+ ## Tessellations
6
+
7
+ + Orthogonal - the standard rectangular grid with passages intersecting at right angles
8
+ + Sigma - composed of interlocking hexagons
9
+
10
+ ## Supported Output Formats
11
+
12
+ + ASCII
13
+ + PNG
14
+
15
+ ## Algorithms
16
+
17
+ + Recursive Backtracking
18
+
19
+ ## Examples
20
+
21
+ Generating an orthogonal maze with default parameters:
22
+
23
+ $ ruby -Ilib examples/orthogonal.rb
24
+
25
+ _ _ _ _ _ _ _ _ _ _
26
+ |_ _ _| _ | |
27
+ | _|_| _ _| |_ _| |
28
+ | _ _ _|_ _ | _|
29
+ |_ | _ _ |_| |_ |
30
+ | _| | |_ | |_|
31
+ |_ |_| | _|_ _| | |
32
+ | |_ _ _| _ _ |_ |
33
+ | _ _|_ | |_ _ _|
34
+ |_ |_ _ |_ _ | |
35
+ |_ _ _ _|_ _ _ _ _|_|
36
+ Generated with: examples/orthogonal.rb 10 10 4083063427
37
+ Written to output file: sample_orthogonal.png
38
+
39
+ Generating a sigma maze of dimensions 20x5
40
+
41
+ $ ruby -Ilib examples/sigma.rb 20 5
42
+ _ _ _ _ _ _ _ _ _ _
43
+ / \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_
44
+ \_ \_ / _/ _ _/ \_ _/ _ \
45
+ / _ \ / \_ _/ \_ \_/ \_/ / \_/
46
+ \_ \_ \ / \_/ _ \_/ \ / \ / _/ \ \
47
+ / \ / \_/ \ \_ \_ _/ / \_ \_ \ /
48
+ \ \ \_ \_ _/ \_/ \_/ \ / \_ \_/ \
49
+ / \_/ \_ \_/ \_/ _ _/ _/ _ _/ /
50
+ \_ \ \_/ _/ _/ \_ _/ _ \_/ _/ \
51
+ / \_/ \ / _ \_ \ \_/ \_/ \_/ \ / \ /
52
+ \_ _/ _ \_ _ \_ _ _ _ \_ \
53
+ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
54
+ Generated with: examples/sigma.rb 20 5 2645469508
55
+ Written to output file: sample_sigma.png
@@ -0,0 +1,27 @@
1
+ require 'maze'
2
+
3
+ module MazeHelper
4
+ ORTHOGONAL = :orthogonal
5
+ SIGMA = :sigma
6
+
7
+ def self.generate(type, format = :ascii, output_file = nil)
8
+ width = (ARGV[0] || 10).to_i
9
+ height = (ARGV[1] || width).to_i
10
+ seed = (ARGV[2] || rand(0xFFFF_FFFF)).to_i
11
+
12
+ srand(seed)
13
+
14
+ params = { :width => width, :height => height }
15
+ maze = Maze.const_get(type.capitalize).new(params)
16
+
17
+ puts maze.generate.draw(:ascii)
18
+ unless :ascii == format
19
+ maze.generate.draw(:png, output_file)
20
+ end
21
+
22
+ puts "Generated with: #{__FILE__} #{width} #{height} #{seed}"
23
+ puts "Written to output file: #{output_file}" if output_file
24
+ end
25
+ end
26
+
27
+
@@ -0,0 +1,3 @@
1
+ require_relative 'maze_helper'
2
+
3
+ MazeHelper.generate(MazeHelper::ORTHOGONAL, :png, "sample_orthogonal.png")
@@ -0,0 +1,3 @@
1
+ require_relative 'maze_helper'
2
+
3
+ MazeHelper.generate(MazeHelper::SIGMA, :png, "sample_sigma.png")
@@ -0,0 +1,8 @@
1
+ require 'oily_png'
2
+
3
+ require_relative 'maze/point'
4
+ require_relative 'maze/generic'
5
+ require_relative 'maze/orthogonal'
6
+ require_relative 'maze/sigma'
7
+ require_relative 'maze/version'
8
+
@@ -0,0 +1,46 @@
1
+ module Maze
2
+ module Algorithm
3
+ class RecursiveBacktracker
4
+
5
+ attr_reader :maze
6
+
7
+ def initialize(maze)
8
+ @maze = maze
9
+ end
10
+
11
+ # Start from any random point in the grid.
12
+ def run
13
+ find_path_from(Point.random(maze))
14
+ end
15
+
16
+ # 1. From the current point pick a neighbouring point which has not been
17
+ # visited in any direction.
18
+ # 2. The coordinates of the next point has to be fetched w.r.t the
19
+ # direction of traversal. In certain cases like for edge points its
20
+ # possible to step out of bounds. We ignore these moves and pick
21
+ # another one which will keep us within the grid.
22
+ # 3. When there is a valid move to a neighbouring point and it has not been
23
+ # visited earlier, both points are connected. Now the neighbouring point
24
+ # becomes the current point.
25
+ # 4. We continue recursively connecting paths until we reach certain points
26
+ # from which there are no unvisited neighbours, in which case we
27
+ # continuously backtrack until we find a neighbouring unvisited point.
28
+ # 5. The algorithm ends its run when it returns to initial point from where
29
+ # it started it's traversal. At this point it's guaranteed to have
30
+ # visited all the other points.
31
+ def find_path_from(current_point)
32
+ maze.random_directions.each do |direction|
33
+ next_point = current_point.next(maze, direction)
34
+
35
+ if next_point && next_point.unvisited?(maze)
36
+ maze.connect(current_point, next_point, direction)
37
+
38
+ find_path_from(next_point)
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,47 @@
1
+ module Maze
2
+ module Formatter
3
+ module Ascii
4
+
5
+ def to_s
6
+ canvas.map { |line| line.join }.join("\n")
7
+ end
8
+
9
+ alias_method :draw, :to_s
10
+
11
+ def canvas_draw
12
+ maze.height.times do |y|
13
+ maze.width.times do |x|
14
+ canvas_update Point.new(x, y)
15
+ end
16
+ end
17
+ end
18
+
19
+ def canvas_update(for_point)
20
+ origin = calculate_origin(for_point)
21
+
22
+ maze.directions.each do |direction|
23
+ offset = origin + calculate_offset(direction, for_point)
24
+
25
+ unless maze.connected?(for_point, direction)
26
+ canvas[offset.y][offset.x] = select_character(direction)
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def calculate_origin(for_point)
34
+ raise NotImplementedError, "#{self.class.name} does not implement private method: #{__method__}"
35
+ end
36
+
37
+ def calculate_offset(*args)
38
+ raise NotImplementedError, "#{self.class.name} does not implement private method: #{__method__}"
39
+ end
40
+
41
+ def select_character(direction)
42
+ raise NotImplementedError, "#{self.class.name} does not implement private method: #{__method__}"
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,51 @@
1
+ require_relative '../ascii'
2
+
3
+ module Maze
4
+ module Formatter
5
+ module Ascii
6
+ class Orthogonal
7
+
8
+ attr_reader :maze, :canvas
9
+
10
+ include Ascii
11
+
12
+ def initialize(maze)
13
+ @maze = maze
14
+ @canvas = Array.new(maze.height + 1) do
15
+ Array.new(maze.width * 2, ' ')
16
+ end
17
+
18
+ canvas_draw
19
+ end
20
+
21
+ private
22
+
23
+ def calculate_origin(for_point)
24
+ for_point.update do |other|
25
+ other.x *= 2
26
+ other.y += 1
27
+ end
28
+ end
29
+
30
+ def calculate_offset(*args)
31
+ direction = args.first
32
+
33
+ case direction
34
+ when maze.class::N then Point.new(1, -1)
35
+ when maze.class::W then Point.new(0, 0)
36
+ when maze.class::S then Point.new(1, 0)
37
+ when maze.class::E then Point.new(2, 0)
38
+ end
39
+ end
40
+
41
+ def select_character(for_direction)
42
+ case for_direction
43
+ when maze.class::N, maze.class::S then "_"
44
+ when maze.class::W, maze.class::E then "|"
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,56 @@
1
+ require_relative '../ascii'
2
+
3
+ module Maze
4
+ module Formatter
5
+ module Ascii
6
+ class Sigma
7
+
8
+ attr_reader :maze, :canvas
9
+
10
+ include Ascii
11
+
12
+ def initialize(maze)
13
+ @maze = maze
14
+ @canvas = Array.new(maze.height * 2 + 2) do
15
+ Array.new(maze.width * 2 + 2, ' ')
16
+ end
17
+
18
+ canvas_draw
19
+ end
20
+
21
+ private
22
+
23
+ def calculate_origin(for_point)
24
+ for_point.update do |other|
25
+ other.x *= 2
26
+ other.y *= 2
27
+ end
28
+ end
29
+
30
+ def calculate_offset(*args)
31
+ direction, point = args
32
+
33
+ shift_y = maze.shifted_down?(point) ? 1 : 0
34
+
35
+ case direction
36
+ when :north then Point.new(1, 0 + shift_y)
37
+ when :north_west then Point.new(0, 1 + shift_y)
38
+ when :north_east then Point.new(2, 1 + shift_y)
39
+ when :south_west then Point.new(0, 2 + shift_y)
40
+ when :south then Point.new(1, 2 + shift_y)
41
+ when :south_east then Point.new(2, 2 + shift_y)
42
+ end
43
+ end
44
+
45
+ def select_character(direction)
46
+ case direction
47
+ when :north, :south then "_"
48
+ when :north_west, :south_east then "/"
49
+ when :north_east, :south_west then "\\"
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ module Maze
2
+ module Formatter
3
+ module Png
4
+
5
+ PADDING = 10
6
+ LENGTH = 30
7
+
8
+ def draw(output_file = nil)
9
+ lines.each do |end_points|
10
+ image.line(*end_points, ChunkyPNG::Color::BLACK)
11
+ end
12
+ image.save(output_file, :fast_rgb) if output_file
13
+ end
14
+
15
+ private
16
+
17
+ def background_colour
18
+ ChunkyPNG::Color::WHITE
19
+ end
20
+
21
+ def padding_offset
22
+ PADDING * 2
23
+ end
24
+
25
+ def draw_image
26
+ maze.height.times do |row|
27
+ maze.width.times do |column|
28
+ closed_line_segments(row, column).each do |line_segment|
29
+ lines << line_segment.map { |point| point.raw }.flatten
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,127 @@
1
+ require_relative '../png'
2
+
3
+ module Maze
4
+ module Formatter
5
+ module Png
6
+ class Orthogonal
7
+
8
+ attr_reader :maze, :image, :lines
9
+
10
+ include Png
11
+
12
+ def initialize(maze)
13
+ @maze = maze
14
+ @image = ChunkyPNG::Image.new(width, height, background_colour)
15
+ @lines = []
16
+
17
+ draw_image
18
+ end
19
+
20
+ private
21
+
22
+ def width
23
+ (maze.width * LENGTH + padding_offset).to_i
24
+ end
25
+
26
+ def height
27
+ (maze.height * LENGTH + padding_offset).to_i
28
+ end
29
+
30
+ def origins
31
+ @origins ||= compute_origin_points
32
+ end
33
+
34
+ def compute_origin_points
35
+ points = Array.new(maze.height) { Array.new(maze.width) }
36
+
37
+ maze.height.times do |row|
38
+ maze.width.times do |column|
39
+ points[row][column] =
40
+ if 0 == column
41
+ first_origin_point(row)
42
+ else
43
+ next_horizontal_origin_point(points[row][column - 1])
44
+ end
45
+ end
46
+ end
47
+
48
+ points
49
+ end
50
+
51
+ def first_origin_point(for_row)
52
+ x = (PADDING + LENGTH * 0.5).to_i
53
+ y = (PADDING + LENGTH * 0.5 + for_row * LENGTH).to_i
54
+
55
+ Point.new(x, y)
56
+ end
57
+
58
+ def next_horizontal_origin_point(previous_point)
59
+ previous_point.update { |next_origin| next_origin.x += LENGTH }
60
+ end
61
+
62
+ #
63
+ # Given the origin computes the corner points of the square
64
+ #
65
+ # 1 2
66
+ # <--------L-------->
67
+ # +--------+--------+
68
+ # | ^ |<-.5 L->|
69
+ # | .5L| |
70
+ # | V | |
71
+ # +--------+--------+
72
+ # | |O |
73
+ # | | |
74
+ # | | |
75
+ # +--------+--------+
76
+ # 4 3
77
+ #
78
+ # O is Origin
79
+ #
80
+ # Faces in clockwise direction:
81
+ # * North : 1 - 2
82
+ # * East : 2 - 3
83
+ # * South : 3 - 4
84
+ # * West : 4 - 1
85
+ #
86
+ def corner_points(origin)
87
+ faces = []
88
+ offsets = [ [-LENGTH, -LENGTH],
89
+ [ LENGTH, -LENGTH],
90
+ [ LENGTH, LENGTH],
91
+ [-LENGTH, LENGTH],
92
+ [-LENGTH, -LENGTH] ]
93
+
94
+ offsets.each do |(offset_x, offset_y)|
95
+ faces << origin.update do |corner|
96
+ corner.x += offset_x / 2
97
+ corner.y += offset_y / 2
98
+ end
99
+ end
100
+ faces
101
+ end
102
+
103
+ #
104
+ # Group adjacent corner points into pairs
105
+ #
106
+ def face_line_segment(origin)
107
+ corner_points(origin).each_cons(2)
108
+ end
109
+
110
+ def clockwise_directions
111
+ [ maze.class::N, maze.class::E, maze.class::S, maze.class::W ]
112
+ end
113
+
114
+ def filter_open_faces(line_segments, open = [])
115
+ line_segments.reject.each_with_index do |segment, i|
116
+ open.include? clockwise_directions[i]
117
+ end
118
+ end
119
+
120
+ def closed_line_segments(row, column)
121
+ filter_open_faces( face_line_segment(origins[row][column]),
122
+ maze.grid[row][column] )
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,137 @@
1
+ require_relative '../png'
2
+
3
+ module Maze
4
+ module Formatter
5
+ module Png
6
+ class Sigma
7
+
8
+ attr_reader :maze, :image, :lines
9
+
10
+ include Png
11
+
12
+ def initialize(maze)
13
+ @maze = maze
14
+ @image = ChunkyPNG::Image.new(width, height, background_colour)
15
+ @lines = []
16
+
17
+ draw_image
18
+ end
19
+
20
+ private
21
+
22
+ def width
23
+ if 0 == maze.width % 2
24
+ (1.5 * maze.width * LENGTH + 0.5 * LENGTH + padding_offset).to_i
25
+ else
26
+ horizontal_units = (1..maze.width).map do |cell_number|
27
+ 0 == cell_number % 2 ? 1 : 2
28
+ end.inject do |s, x|
29
+ s + x
30
+ end
31
+
32
+ (horizontal_units * LENGTH + padding_offset).to_i
33
+ end
34
+ end
35
+
36
+ def height
37
+ (maze.height * 2 * LENGTH + LENGTH + padding_offset).to_i
38
+ end
39
+
40
+ def origins
41
+ @origins ||= compute_origin_points
42
+ end
43
+
44
+ def compute_origin_points
45
+ points = Array.new(maze.height) { Array.new(maze.width) }
46
+
47
+ maze.height.times do |row|
48
+ maze.width.times do |column|
49
+ points[row][column] =
50
+ if 0 == column
51
+ first_origin_point(row)
52
+ else
53
+ next_horizontal_origin_point(points[row][column - 1], column)
54
+ end
55
+ end
56
+ end
57
+
58
+ points
59
+ end
60
+
61
+ def first_origin_point(row)
62
+ x = (LENGTH + PADDING).to_i
63
+ y = (LENGTH + row * 2 * LENGTH + PADDING).to_i
64
+
65
+ Point.new(x, y)
66
+ end
67
+
68
+ def next_horizontal_origin_point(previous_point, current_column)
69
+ previous_point.update do |next_origin|
70
+ next_origin.x += (1.5 * LENGTH).to_i
71
+ next_origin.y += (current_column % 2 == 0 ? -LENGTH : LENGTH).to_i
72
+ end
73
+ end
74
+
75
+ #
76
+ # Compute corner points traversing in the clock-wise direction
77
+ #
78
+ # 0 _____ 1
79
+ # / \
80
+ # 5 / + \ 2
81
+ # \ origin/
82
+ # 4 \_____/ 3
83
+ #
84
+ # Once the origin is known the points 0 - 5 through can be computed
85
+ #
86
+ # Output: [x0, y0, x1, y1, ... , x5, y5, x0, y0]
87
+ # Is the closed path of the hexagon
88
+ #
89
+ def corner_points(origin)
90
+ faces = []
91
+ offsets = [ [ -LENGTH / 2, -LENGTH ],
92
+ [ LENGTH / 2, -LENGTH ],
93
+ [ LENGTH, 0 ],
94
+ [ LENGTH / 2, LENGTH ],
95
+ [ -LENGTH / 2, LENGTH ],
96
+ [ -LENGTH, 0 ],
97
+ [ -LENGTH / 2, -LENGTH ] ]
98
+
99
+ offsets.each do |(offset_x, offset_y)|
100
+ faces << origin.update do |corner|
101
+ corner.x += offset_x
102
+ corner.y += offset_y
103
+ end
104
+ end
105
+ faces
106
+ end
107
+
108
+ #
109
+ # Group adjacent corner points into pairs
110
+ #
111
+ def face_line_segment(origin)
112
+ corner_points(origin).each_cons(2)
113
+ end
114
+
115
+ #
116
+ # Traces the clockwise directions of the hexagon w.r.t the closed path
117
+ #
118
+ def clockwise_directions
119
+ [ maze.class::N, maze.class::NE, maze.class::SE,
120
+ maze.class::S, maze.class::SW, maze.class::NW ]
121
+ end
122
+
123
+ def filter_open_faces(line_segments, open = [])
124
+ line_segments.reject.each_with_index do |segment, i|
125
+ open.include? clockwise_directions[i]
126
+ end
127
+ end
128
+
129
+ def closed_line_segments(row, column)
130
+ filter_open_faces( face_line_segment(origins[row][column]),
131
+ maze.grid[row][column] )
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+
@@ -0,0 +1,63 @@
1
+ require_relative 'algorithms/recursive_backtracker'
2
+ require_relative 'formatters/ascii'
3
+
4
+ module Maze
5
+ class Generic
6
+
7
+ attr_reader :width, :height, :grid, :algorithm
8
+
9
+ N = :north
10
+ S = :south
11
+ E = :east
12
+ W = :west
13
+ NE = :north_east
14
+ NW = :north_west
15
+ SE = :south_east
16
+ SW = :south_west
17
+
18
+ def initialize(options)
19
+ @width = options[:width] || 10
20
+ @height = options[:height] || 10
21
+ @grid = Array.new(height) { Array.new(width) }
22
+ @algorithm = Algorithm::RecursiveBacktracker.new(self)
23
+ end
24
+
25
+ def generate
26
+ tap { algorithm.run }
27
+ end
28
+
29
+ def connected?(point, direction)
30
+ self[point].include?(direction)
31
+ end
32
+
33
+ def connect(current_point, next_point, direction)
34
+ self[current_point] = direction
35
+ self[next_point] = opposite(direction)
36
+ end
37
+
38
+ def []=(point, direction)
39
+ grid[point.y][point.x] ||= []
40
+ grid[point.y][point.x] << direction
41
+ end
42
+
43
+ def [](point)
44
+ grid[point.y][point.x]
45
+ end
46
+
47
+ def draw(format, output_file = nil)
48
+ format = format.capitalize.to_s
49
+ class_name = Formatter.const_get(format).const_get(tessellation)
50
+
51
+ unless output_file
52
+ class_name.new(self).draw
53
+ else
54
+ class_name.new(self).draw(output_file)
55
+ end
56
+ end
57
+
58
+ def tessellation
59
+ self.class.name[/::(.*)$/, 1]
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'formatters/ascii/orthogonal'
2
+ require_relative 'formatters/png/orthogonal'
3
+
4
+ module Maze
5
+ class Orthogonal < Generic
6
+ OPPOSITE = { N => S, S => N, E => W, W => E }
7
+
8
+ def random_directions
9
+ [N, S, E, W].sort_by { rand }
10
+ end
11
+
12
+ alias_method :directions, :random_directions
13
+
14
+ def opposite(direction)
15
+ OPPOSITE[direction]
16
+ end
17
+
18
+ def calculate_offset_x_axis(*args)
19
+ distance_x = { N => 0, S => 0, E => 1, W => -1 }
20
+ distance_x[args.first]
21
+ end
22
+
23
+ def calculate_offset_y_axis(*args)
24
+ distance_y = { N => -1, S => 1, E => 0, W => 0 }
25
+ distance_y[args.first]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Maze
2
+ class Point < Struct.new(:x, :y)
3
+
4
+ def self.random(maze)
5
+ new(rand(maze.width), rand(maze.height))
6
+ end
7
+
8
+ def out_of_bounds?(maze)
9
+ !(x.between?(0, maze.width - 1) && y.between?(0, maze.height - 1))
10
+ end
11
+
12
+ def unvisited?(maze)
13
+ maze[self].nil?
14
+ end
15
+
16
+ def next(maze, direction)
17
+ next_x = x + maze.calculate_offset_x_axis(direction, self)
18
+ next_y = y + maze.calculate_offset_y_axis(direction, self)
19
+ next_point = self.class.new(next_x, next_y)
20
+
21
+ next_point.out_of_bounds?(maze) ? nil : next_point
22
+ end
23
+
24
+ def update
25
+ other = self.dup
26
+ yield other if block_given?
27
+ other
28
+ end
29
+
30
+ def +(other)
31
+ self.class.new(x + other.x, y + other.y)
32
+ end
33
+
34
+ def raw
35
+ [x, y]
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,92 @@
1
+ require_relative 'formatters/ascii/sigma'
2
+ require_relative 'formatters/png/sigma'
3
+
4
+ module Maze
5
+ class Sigma < Generic
6
+ OPPOSITE = { N => S, S => N, NE => SW, SW => NE, NW => SE, SE => NW }
7
+
8
+ def random_directions
9
+ [N, S, NE, NW, SE, SW].sort_by { rand }
10
+ end
11
+
12
+ alias_method :directions, :random_directions
13
+
14
+ def opposite(direction)
15
+ OPPOSITE[direction]
16
+ end
17
+
18
+ def calculate_offset_x_axis(*args)
19
+ direction, point = args
20
+
21
+ case direction
22
+ when N, S
23
+ 0
24
+ when NW, SW
25
+ send("noexit_#{direction}?", point) ? 0 : -1
26
+ when NE, SE
27
+ send("noexit_#{direction}?", point) ? 0 : 1
28
+ end
29
+ end
30
+
31
+ def calculate_offset_y_axis(*args)
32
+ direction, point = args
33
+
34
+ case direction
35
+ when N
36
+ noexit_north?(point) ? 0 : -1
37
+ when S
38
+ noexit_south?(point) ? 0 : 1
39
+ when NW, NE
40
+ send("noexit_#{direction}?", point) || shifted_down?(point) ? 0 : -1
41
+ when SW, SE
42
+ send("noexit_#{direction}?", point) || shifted_up?(point) ? 0 : 1
43
+ end
44
+ end
45
+
46
+ def shifted_up?(point)
47
+ 0 == point.x % 2
48
+ end
49
+
50
+ def shifted_down?(point)
51
+ !shifted_up?(point)
52
+ end
53
+
54
+ private
55
+
56
+ def noexit_north?(point)
57
+ 0 == point.y
58
+ end
59
+
60
+ def noexit_south?(point)
61
+ height - 1 == point.y
62
+ end
63
+
64
+ def noexit_west?(point)
65
+ 0 == point.x
66
+ end
67
+
68
+ def noexit_east?(point)
69
+ width - 1 == point.y
70
+ end
71
+
72
+ def noexit_north_west?(point)
73
+ noexit_west?(point) &&
74
+ (noexit_north?(point) && shifted_up?(point))
75
+ end
76
+
77
+ def noexit_north_east?(point)
78
+ noexit_east?(point) &&
79
+ (noexit_north?(point) && shifted_up?(point))
80
+ end
81
+
82
+ def noexit_south_west?(point)
83
+ noexit_west?(point) &&
84
+ (noexit_south?(point) && shifted_down?(point))
85
+ end
86
+
87
+ def noexit_south_east?(point)
88
+ noexit_east?(point) &&
89
+ (noexit_south?(point) && shifted_down?(point))
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ module Maze
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,21 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'maze/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "maze"
6
+ s.version = Maze::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Sherin C"]
9
+ s.email = ["sherin.jc@gmail.com"]
10
+ s.homepage = "https://github.com/csherin/mazes"
11
+ s.summary = "A library for generating mazes."
12
+ s.description = <<-DESC
13
+ A library for generating mazes. It supports orthogonal & sigma tessellations
14
+ in the ASCII or PNG output formats.
15
+ DESC
16
+
17
+ s.add_runtime_dependency "oily_png"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.require_paths = ["lib"]
21
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maze
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sherin C
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-29 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: oily_png
16
+ requirement: &2157606700 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2157606700
25
+ description: ! " A library for generating mazes. It supports orthogonal & sigma
26
+ tessellations\n in the ASCII or PNG output formats.\n"
27
+ email:
28
+ - sherin.jc@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - .rvmrc.example
35
+ - Gemfile
36
+ - Gemfile.lock
37
+ - README.md
38
+ - examples/maze_helper.rb
39
+ - examples/orthogonal.rb
40
+ - examples/sigma.rb
41
+ - lib/maze.rb
42
+ - lib/maze/algorithms/recursive_backtracker.rb
43
+ - lib/maze/formatters/ascii.rb
44
+ - lib/maze/formatters/ascii/orthogonal.rb
45
+ - lib/maze/formatters/ascii/sigma.rb
46
+ - lib/maze/formatters/png.rb
47
+ - lib/maze/formatters/png/orthogonal.rb
48
+ - lib/maze/formatters/png/sigma.rb
49
+ - lib/maze/generic.rb
50
+ - lib/maze/orthogonal.rb
51
+ - lib/maze/point.rb
52
+ - lib/maze/sigma.rb
53
+ - lib/maze/version.rb
54
+ - maze.gemspec
55
+ homepage: https://github.com/csherin/mazes
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.13
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: A library for generating mazes.
79
+ test_files: []
80
+ has_rdoc: