rmaze 1.5.0 → 2.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +29 -6
- data/LICENSE.txt +21 -0
- data/README.md +11 -5
- data/bin/rmaze +8 -4
- data/lib/algorithm/backtrace.rb +46 -0
- data/lib/game/game.rb +11 -9
- data/lib/maze/maze.rb +110 -43
- data/lib/maze/maze_cell.rb +55 -53
- data/lib/solver/tree_solver.rb +5 -5
- data/rmaze.gemspec +1 -1
- data/spec/algorithm/backtrace_spec.rb +22 -0
- data/spec/game/game_spec.rb +8 -7
- data/spec/maze/maze_cell_spec.rb +102 -33
- data/spec/maze/maze_spec.rb +203 -27
- data/spec/solver/tree_solver_spec.rb +18 -8
- data/spec/spec_helper.rb +2 -3
- metadata +6 -5
- data/lib/maze/maze_btrace.rb +0 -57
- data/spec/maze/maze_btrace_spec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9e0430fb1d38141827770a72ae3438a451ed1ca
|
4
|
+
data.tar.gz: 15a61f5a73f128132ed27b0a2f08b2bfaa5bb3e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5a3263729d2f09ded3176dbef2531779c8ebb1f7f3ab38342b473dabd4a94142c1ab7709276c264788eca2f201da5a39728bd4720186fd5da88c096521bfc8a
|
7
|
+
data.tar.gz: 324f2f23e46d31728e3e4e77f71f31fd958aea40b4bf056b348a33b42c9f9213f490c3e0b3fe25bc68bd0d3ad19aacc8a79820fa26cb105162180f4ea106b03d
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.1
|
1
|
+
ruby-2.2.1
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -2,11 +2,27 @@ GEM
|
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
4
|
OptionParser (0.5.1)
|
5
|
+
coveralls (0.8.10)
|
6
|
+
json (~> 1.8)
|
7
|
+
rest-client (>= 1.6.8, < 2)
|
8
|
+
simplecov (~> 0.11.0)
|
9
|
+
term-ansicolor (~> 1.3)
|
10
|
+
thor (~> 0.19.1)
|
11
|
+
tins (~> 1.6.0)
|
5
12
|
diff-lcs (1.2.5)
|
6
13
|
docile (1.1.5)
|
14
|
+
domain_name (0.5.25)
|
15
|
+
unf (>= 0.0.5, < 1.0.0)
|
16
|
+
http-cookie (1.0.2)
|
17
|
+
domain_name (~> 0.5)
|
7
18
|
json (1.8.1)
|
8
|
-
|
19
|
+
mime-types (2.99)
|
20
|
+
netrc (0.11.0)
|
9
21
|
rake (10.1.0)
|
22
|
+
rest-client (1.8.0)
|
23
|
+
http-cookie (>= 1.0.2, < 2.0)
|
24
|
+
mime-types (>= 1.16, < 3.0)
|
25
|
+
netrc (~> 0.7)
|
10
26
|
rspec (3.1.0)
|
11
27
|
rspec-core (~> 3.1.0)
|
12
28
|
rspec-expectations (~> 3.1.0)
|
@@ -22,22 +38,29 @@ GEM
|
|
22
38
|
rubytree (0.9.4)
|
23
39
|
json (~> 1.8)
|
24
40
|
structured_warnings (~> 0.1)
|
25
|
-
simplecov (0.
|
41
|
+
simplecov (0.11.1)
|
26
42
|
docile (~> 1.1.0)
|
27
|
-
|
28
|
-
simplecov-html (~> 0.
|
29
|
-
simplecov-html (0.
|
43
|
+
json (~> 1.8)
|
44
|
+
simplecov-html (~> 0.10.0)
|
45
|
+
simplecov-html (0.10.0)
|
30
46
|
structured_warnings (0.1.4)
|
47
|
+
term-ansicolor (1.3.2)
|
48
|
+
tins (~> 1.0)
|
49
|
+
thor (0.19.1)
|
50
|
+
tins (1.6.0)
|
51
|
+
unf (0.1.4)
|
52
|
+
unf_ext
|
53
|
+
unf_ext (0.0.7.1)
|
31
54
|
|
32
55
|
PLATFORMS
|
33
56
|
ruby
|
34
57
|
|
35
58
|
DEPENDENCIES
|
36
59
|
OptionParser (~> 0.5.1)
|
60
|
+
coveralls
|
37
61
|
rake (~> 10.1.0)
|
38
62
|
rspec (~> 3.1.0)
|
39
63
|
rubytree (~> 0.9.4)
|
40
|
-
simplecov
|
41
64
|
|
42
65
|
BUNDLED WITH
|
43
66
|
1.11.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Pedro Henrique Marques Lira
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
# RMaze
|
2
|
-
Ruby library
|
2
|
+
Ruby library for multidimensional maze generation
|
3
3
|
|
4
|
-
[](https://rubygems.org/gems/rmaze)
|
5
|
+
[](http://travis-ci.org/pedrohml/rmaze)
|
6
|
+
[](https://coveralls.io/github/pedrohml/rmaze)
|
5
7
|
[](https://hakiri.io/github/pedrohml/rmaze/master)
|
8
|
+
[](https://github.com/pedrohml/rmaze/blob/master/LICENSE.txt)
|
6
9
|
|
7
10
|
#### Description
|
8
|
-
This
|
11
|
+
This library generates multidimensional mazes.
|
12
|
+
|
13
|
+
- What the purpose ? Mainly, games.
|
14
|
+
- What do I want Nth-d mazes for ? I do not know yet.
|
9
15
|
|
10
16
|
#### Tool usage
|
11
|
-
The **rmaze** is in initial version and
|
17
|
+
The tool **rmaze** is in initial version and only supports bidimensional mazes.
|
12
18
|
|
13
19
|
```
|
14
|
-
Usage: rmaze
|
20
|
+
Usage: rmaze [options]
|
15
21
|
|
16
22
|
Basic options:
|
17
23
|
-w, --width width Specify the maze width (default: 10)
|
data/bin/rmaze
CHANGED
@@ -27,14 +27,18 @@ OptionParser.new do |opts|
|
|
27
27
|
end
|
28
28
|
end.parse!
|
29
29
|
|
30
|
-
maze =
|
30
|
+
maze = Maze.new options[:width], options[:height]
|
31
|
+
maze.set_raw_value_all 1
|
32
|
+
|
31
33
|
case options[:algorithm]
|
32
34
|
when :backtrace
|
33
|
-
|
35
|
+
backtrace = Backtrace.new maze
|
36
|
+
backtrace.generate
|
34
37
|
else
|
35
38
|
$stderr.puts "Error: the algorithm must be set."
|
36
39
|
exit(-1)
|
37
40
|
end
|
38
41
|
|
39
|
-
maze.
|
40
|
-
|
42
|
+
maze.matrix.each_with_index do |row, index|
|
43
|
+
puts row.map(&:to_s).join(' ').gsub('0', ' ').gsub('1', '#')
|
44
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'maze/maze'
|
2
|
+
|
3
|
+
class Backtrace
|
4
|
+
attr_reader :maze
|
5
|
+
|
6
|
+
def initialize(maze)
|
7
|
+
@maze = maze
|
8
|
+
end
|
9
|
+
|
10
|
+
def generate
|
11
|
+
stack = []
|
12
|
+
visited_cells = []
|
13
|
+
all_cells = []
|
14
|
+
@maze.set_raw_value_all 1
|
15
|
+
all_cells = @maze.cells
|
16
|
+
|
17
|
+
all_cells.each do |cell|
|
18
|
+
@maze.set_value *cell.coords.clone.push(0)
|
19
|
+
end
|
20
|
+
|
21
|
+
current_cell = @maze.cell *@maze.dimensions.map { |d| rand(d) }
|
22
|
+
visited_cells.push current_cell
|
23
|
+
|
24
|
+
while visited_cells.size != @maze.total_cells
|
25
|
+
neighbours = current_cell.neighbours
|
26
|
+
visited_cells.push current_cell
|
27
|
+
unvisited_neighbours = (neighbours - visited_cells)
|
28
|
+
if !unvisited_neighbours.empty?
|
29
|
+
stack.push current_cell
|
30
|
+
random_neighbour = unvisited_neighbours.shuffle.shift
|
31
|
+
@maze.connect_cells current_cell, random_neighbour
|
32
|
+
current_cell = random_neighbour
|
33
|
+
visited_cells.push current_cell
|
34
|
+
elsif !stack.empty?
|
35
|
+
current_cell = stack.pop
|
36
|
+
else
|
37
|
+
unvisited_index_cells = all_cells - visited_cells
|
38
|
+
unvisited_index_cell = unvisited_index_cells.shuffle.shift
|
39
|
+
current_cell = unvisited_index_cell
|
40
|
+
visited_cells.push current_cell
|
41
|
+
end
|
42
|
+
visited_cells.uniq!
|
43
|
+
end
|
44
|
+
@maze
|
45
|
+
end
|
46
|
+
end
|
data/lib/game/game.rb
CHANGED
@@ -7,9 +7,9 @@ class Game
|
|
7
7
|
|
8
8
|
def select_goal_cell(dificulty)
|
9
9
|
dificulty = dificulty.to_sym
|
10
|
-
solver = TreeSolver.new
|
11
|
-
|
12
|
-
paths = solver.paths
|
10
|
+
solver = TreeSolver.new @maze
|
11
|
+
start_coords = [0]*@maze.dimensions.length
|
12
|
+
paths = solver.paths *start_coords
|
13
13
|
leafs = {}
|
14
14
|
paths.each_leaf do |leaf|
|
15
15
|
leafs[leaf.node_depth] = leaf
|
@@ -17,21 +17,23 @@ class Game
|
|
17
17
|
depths = leafs.keys.sort
|
18
18
|
case dificulty
|
19
19
|
when :easy
|
20
|
-
selected_depth = depths[(depths.size
|
20
|
+
selected_depth = depths[(depths.size/4).ceil]
|
21
21
|
when :medium
|
22
|
-
selected_depth = depths[(depths.size
|
22
|
+
selected_depth = depths[(depths.size/2).ceil]
|
23
23
|
when :hard
|
24
24
|
selected_depth = depths.last
|
25
25
|
else
|
26
|
-
selected_depth = depths[
|
26
|
+
selected_depth = depths[(depths.size/2.0).floor + rand((depths.size/2.0).round)]
|
27
27
|
end
|
28
28
|
leafs[selected_depth].content
|
29
29
|
end
|
30
30
|
|
31
31
|
def generate_goal(dificulty)
|
32
|
-
selected_cell = select_goal_cell
|
33
|
-
|
34
|
-
|
32
|
+
selected_cell = select_goal_cell dificulty
|
33
|
+
indices = @maze.coords_to_indices *selected_cell.coords
|
34
|
+
params = indices.clone
|
35
|
+
params.push 2
|
36
|
+
@maze.set_raw_value *params
|
35
37
|
@maze
|
36
38
|
end
|
37
39
|
end
|
data/lib/maze/maze.rb
CHANGED
@@ -2,73 +2,140 @@ require 'maze/maze_cell'
|
|
2
2
|
|
3
3
|
class Maze
|
4
4
|
protected
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def allocate(value = 0)
|
6
|
+
axis = []
|
7
|
+
axis_before = []
|
8
|
+
@raw_dimensions.reverse.each_with_index do |dr, dr_index|
|
9
|
+
axis = []
|
10
|
+
if dr_index == 0
|
11
|
+
axis = [value] * dr
|
12
|
+
else
|
13
|
+
(0...dr).each do
|
14
|
+
axis << axis_before.clone
|
15
|
+
end
|
16
|
+
end
|
17
|
+
axis_before = axis
|
9
18
|
end
|
19
|
+
@matrix = axis
|
10
20
|
end
|
11
21
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
22
|
+
def set_between_cells(cell_a, cell_b, value)
|
23
|
+
indices = between_cells cell_a, cell_b
|
24
|
+
params = indices.clone
|
25
|
+
params.push value
|
26
|
+
set_raw_value *params
|
27
|
+
indices
|
28
|
+
end
|
29
|
+
|
30
|
+
def evaluate_indices(dim_sizes)
|
31
|
+
matrix_aux = @matrix
|
32
|
+
indices = []
|
33
|
+
stack = []
|
34
|
+
begin
|
35
|
+
if not stack.empty? and stack.last >= dim_sizes[stack.length - 1]
|
36
|
+
stack.pop
|
37
|
+
stack[-1] += 1 if not stack.empty?
|
38
|
+
elsif stack.length < dim_sizes.length - 1
|
39
|
+
matrix_before = matrix_aux
|
40
|
+
stack.push 0
|
41
|
+
matrix_aux = matrix_aux[stack.last]
|
42
|
+
else
|
43
|
+
(0...dim_sizes.last).each { |d| indices.push(stack + [d]) }
|
44
|
+
stack[-1] += 1
|
45
|
+
end
|
46
|
+
end until stack.empty?
|
47
|
+
indices
|
48
|
+
end
|
49
|
+
|
50
|
+
public
|
51
|
+
attr_reader :dimensions, :width_full, :height_full, :matrix
|
52
|
+
|
53
|
+
def initialize(*dimensions)
|
54
|
+
@dimensions = dimensions.map { |d| d.to_i }.freeze
|
55
|
+
@raw_dimensions = @dimensions.map { |d| 1 + 2*d }.freeze
|
56
|
+
allocate 0
|
57
|
+
@hash = @dimensions.reduce(""){ |accum, d| "#{accum}#{d}" }.to_i
|
18
58
|
end
|
19
59
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
60
|
+
def total_cells
|
61
|
+
dimensions.reduce(1) { |accum, d| accum*d }
|
62
|
+
end
|
63
|
+
|
64
|
+
def total_raw
|
65
|
+
dimensions.reduce(1) { |accum, d| accum*(1 + 2*d) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def coords_to_indices(*coords)
|
69
|
+
coords.map { |c| 1 + 2 * c }
|
28
70
|
end
|
29
71
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@height_full, @width_full = (1 + 2*@height), (1 + 2*@width)
|
37
|
-
initialize_matrix
|
38
|
-
@hash = "#{@width}#{@height}".to_i # optimized pre-computed hash
|
72
|
+
def get_raw_value(*indices)
|
73
|
+
matrix_aux = @matrix
|
74
|
+
indices.each_with_index do |c|
|
75
|
+
matrix_aux = matrix_aux[c]
|
76
|
+
end
|
77
|
+
matrix_aux
|
39
78
|
end
|
40
79
|
|
41
|
-
def
|
42
|
-
@matrix
|
43
|
-
|
80
|
+
def set_raw_value(*indices)
|
81
|
+
matrix_aux = @matrix
|
82
|
+
indices = indices.clone
|
83
|
+
value = indices.pop
|
84
|
+
indices.each_with_index do |c, c_index|
|
85
|
+
matrix_aux[c] = value if c_index == indices.length - 1
|
86
|
+
matrix_aux = matrix_aux[c]
|
44
87
|
end
|
45
|
-
|
88
|
+
matrix_aux
|
46
89
|
end
|
47
90
|
|
48
|
-
def value
|
49
|
-
|
91
|
+
def set_raw_value_all(value)
|
92
|
+
allocate value
|
50
93
|
end
|
51
94
|
|
52
|
-
def
|
53
|
-
|
95
|
+
def get_value(*coords)
|
96
|
+
get_raw_value *self.coords_to_indices(*coords)
|
54
97
|
end
|
55
98
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
[i, j]
|
99
|
+
def set_value(*coords)
|
100
|
+
coords = coords.clone
|
101
|
+
value = coords.pop
|
102
|
+
set_raw_value *self.coords_to_indices(*coords).push(value)
|
61
103
|
end
|
62
104
|
|
63
|
-
def
|
64
|
-
|
105
|
+
def cell(*coords)
|
106
|
+
params = coords.clone
|
107
|
+
params.unshift self
|
108
|
+
MazeCell.new *params
|
65
109
|
end
|
66
110
|
|
111
|
+
def cells
|
112
|
+
evaluate_indices(@dimensions).map { |coord| cell *coord }
|
113
|
+
end
|
114
|
+
|
115
|
+
def between_cells(cell_a, cell_b)
|
116
|
+
indices_a = coords_to_indices *cell_a.coords
|
117
|
+
indices_b = coords_to_indices *cell_b.coords
|
118
|
+
indices_a.zip(indices_b).map { |pair| (pair[0] + pair[1]) / 2.0 }
|
119
|
+
end
|
120
|
+
|
121
|
+
def connect_cells(cell_a, cell_b)
|
122
|
+
set_between_cells cell_a, cell_b, 0
|
123
|
+
end
|
124
|
+
|
125
|
+
def disconnect_cells(cell_a, cell_b)
|
126
|
+
set_between_cells cell_a, cell_b, 1
|
127
|
+
end
|
128
|
+
|
67
129
|
def hash
|
68
130
|
@hash
|
69
131
|
end
|
70
132
|
|
71
133
|
def inspect
|
72
|
-
|
134
|
+
if not @inspect
|
135
|
+
d_index = 0
|
136
|
+
dimension_list = @dimensions.map { |d| "@d#{d_index+=1}=#{d}" }
|
137
|
+
@inspect = "#<#{self.class}: #{dimension_list.join(', ')}>"
|
138
|
+
end
|
139
|
+
@inspect
|
73
140
|
end
|
74
141
|
end
|
data/lib/maze/maze_cell.rb
CHANGED
@@ -1,99 +1,101 @@
|
|
1
1
|
class MazeCell
|
2
|
-
attr_reader :maze, :
|
2
|
+
attr_reader :maze, :coords
|
3
3
|
|
4
|
-
def initialize(
|
5
|
-
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@hash = "#{@maze.hash}#{@
|
4
|
+
def initialize(*params)
|
5
|
+
params = params.clone
|
6
|
+
@maze = params.shift
|
7
|
+
@coords = params.map { |c| c.to_i }
|
8
|
+
@hash = "#{@maze.hash}#{ @coords.reduce(""){ |accum, c| "#{accum}#{c}" }.to_i }".to_i
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
puts " #{@maze.value(i - 1, j)} "
|
14
|
-
puts "#{@maze.value(i, j - 1)} #{@maze.value(i, j)} #{@maze.value(i, j + 1)}"
|
15
|
-
puts " #{@maze.value(i + 1, j)} "
|
16
|
-
end
|
17
|
-
|
18
|
-
def left
|
19
|
-
MazeCell.new(@maze, @x - 1, @y) if @maze.mirrored || @x > 0
|
20
|
-
end
|
21
|
-
|
22
|
-
def up
|
23
|
-
MazeCell.new(@maze, @x, @y - 1) if @maze.mirrored || @y > 0
|
24
|
-
end
|
25
|
-
|
26
|
-
def right
|
27
|
-
MazeCell.new(@maze, @x + 1, @y) if @maze.mirrored || @x < (@maze.width - 1)
|
28
|
-
end
|
29
|
-
|
30
|
-
def down
|
31
|
-
MazeCell.new(@maze, @x, @y + 1) if @maze.mirrored || @y < (@maze.height - 1)
|
11
|
+
def value
|
12
|
+
@maze.get_value *@coords
|
32
13
|
end
|
33
14
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
15
|
+
def backward(dimension_index)
|
16
|
+
coords = @coords.clone
|
17
|
+
coords[dimension_index] -= 1
|
18
|
+
params = coords.clone
|
19
|
+
params.unshift @maze
|
20
|
+
MazeCell.new *params if @coords[dimension_index] > 0
|
37
21
|
end
|
38
22
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
23
|
+
def forward(dimension_index)
|
24
|
+
coords = @coords.clone
|
25
|
+
coords[dimension_index] += 1
|
26
|
+
params = coords.clone
|
27
|
+
params.unshift @maze
|
28
|
+
MazeCell.new *params if @coords[dimension_index] < (@maze.dimensions[dimension_index] - 1)
|
42
29
|
end
|
43
30
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
31
|
+
def has_wall_forward(dimension_index)
|
32
|
+
indices = @maze.coords_to_indices *@coords
|
33
|
+
indices[dimension_index] += 1
|
34
|
+
@coords[dimension_index] >= @maze.dimensions[dimension_index] - 1 or @maze.get_raw_value(*indices) == 1
|
47
35
|
end
|
48
36
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
37
|
+
def has_wall_backward(dimension_index)
|
38
|
+
indices = @maze.coords_to_indices *@coords
|
39
|
+
indices[dimension_index] -= 1
|
40
|
+
@coords[dimension_index] <= 0 or @maze.get_raw_value(*indices) == 1
|
52
41
|
end
|
53
42
|
|
54
43
|
def connected?
|
55
|
-
|
44
|
+
has_wall = true
|
45
|
+
(0...@maze.dimensions.length).each do |d|
|
46
|
+
has_wall = has_wall && has_wall_forward(d) && has_wall_backward(d)
|
47
|
+
break unless has_wall
|
48
|
+
end
|
49
|
+
not has_wall
|
56
50
|
end
|
57
51
|
|
58
52
|
def neighbours
|
59
|
-
|
53
|
+
(0...@maze.dimensions.length).map { |d| [forward(d), backward(d)] }.flatten.compact
|
60
54
|
end
|
61
55
|
|
62
56
|
def connected_neighbours
|
63
57
|
neighbours = []
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
58
|
+
(0...@maze.dimensions.length).each do |d|
|
59
|
+
neighbours << forward(d) unless has_wall_forward(d)
|
60
|
+
neighbours << backward(d) unless has_wall_backward(d)
|
61
|
+
end
|
68
62
|
neighbours
|
69
63
|
end
|
70
64
|
|
71
|
-
def value
|
72
|
-
i, j = xy_to_ij(@x, @y)
|
73
|
-
@maze.value(i, j)
|
74
|
-
end
|
75
|
-
|
76
65
|
def hash
|
77
66
|
@hash
|
78
67
|
end
|
79
68
|
|
80
69
|
def eql?(object)
|
81
70
|
if (object.class == self.class)
|
82
|
-
|
71
|
+
eq = (@maze == object.maze)
|
72
|
+
@coords.each_with_index do |c, c_idx|
|
73
|
+
eq = eq && c == object[c_idx]
|
74
|
+
end
|
75
|
+
eq
|
83
76
|
elsif
|
84
77
|
super(object)
|
85
78
|
end
|
86
79
|
end
|
87
80
|
|
81
|
+
def [](dimension_index)
|
82
|
+
@coords[dimension_index]
|
83
|
+
end
|
84
|
+
|
88
85
|
def ==(object)
|
89
86
|
self.eql? object
|
90
87
|
end
|
91
88
|
|
92
89
|
def !=(object)
|
93
|
-
|
90
|
+
not self.eql? object
|
94
91
|
end
|
95
92
|
|
96
93
|
def inspect
|
97
|
-
|
94
|
+
unless @inspect
|
95
|
+
c_index = 0
|
96
|
+
coord_list = @coords.map { |c| "@c#{c_index+=1}=#{c}" }
|
97
|
+
@inspect = "#<#{self.class}: @maze=#{@maze.inspect}, #{coord_list.join(', ')}>"
|
98
|
+
end
|
99
|
+
@inspect
|
98
100
|
end
|
99
101
|
end
|
data/lib/solver/tree_solver.rb
CHANGED
@@ -7,13 +7,13 @@ class TreeSolver
|
|
7
7
|
@maze = maze
|
8
8
|
end
|
9
9
|
|
10
|
-
def paths(
|
11
|
-
current_cell = @maze.cell
|
12
|
-
root_node = current_node = Tree::TreeNode.new(
|
10
|
+
def paths(*coords)
|
11
|
+
current_cell = @maze.cell *coords
|
12
|
+
root_node = current_node = Tree::TreeNode.new(current_cell.coords.map(&:to_s).join(','), current_cell)
|
13
13
|
visited_cells = [current_cell]
|
14
14
|
stack_node = []
|
15
15
|
stack_cell = []
|
16
|
-
while visited_cells.size != @maze.
|
16
|
+
while visited_cells.size != @maze.total_cells
|
17
17
|
neighbours = current_cell.connected_neighbours
|
18
18
|
unvisited_neighbours = neighbours - visited_cells
|
19
19
|
if unvisited_neighbours.size == 0
|
@@ -25,7 +25,7 @@ class TreeSolver
|
|
25
25
|
stack_node += [current_node]*unvisited_neighbours.size
|
26
26
|
end
|
27
27
|
visited_cells << current_cell
|
28
|
-
new_node = Tree::TreeNode.new(
|
28
|
+
new_node = Tree::TreeNode.new(current_cell.coords.map(&:to_s).join(','), current_cell)
|
29
29
|
current_node << new_node
|
30
30
|
current_node = new_node
|
31
31
|
end
|
data/rmaze.gemspec
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
describe Backtrace do
|
2
|
+
it '#initialize' do
|
3
|
+
maze = Maze.new(rand(10) + 15, rand(10) + 15)
|
4
|
+
backtrace = Backtrace.new maze
|
5
|
+
expect(backtrace.maze).to eq(maze)
|
6
|
+
end
|
7
|
+
|
8
|
+
it '#generate 2d' do
|
9
|
+
maze = Maze.new(rand(10) + 15, rand(10) + 15)
|
10
|
+
backtrace = Backtrace.new maze
|
11
|
+
backtrace.generate
|
12
|
+
expect(maze.cells.map { |cell| cell.connected? }).to be_all
|
13
|
+
end
|
14
|
+
|
15
|
+
it '#generate Nd' do
|
16
|
+
dimensions = ([nil]*(rand(4)+2)).map { |_| 1 + rand(4) }
|
17
|
+
maze = Maze.new *dimensions
|
18
|
+
backtrace = Backtrace.new maze
|
19
|
+
backtrace.generate
|
20
|
+
expect(maze.cells.map { |cell| cell.connected? }).to be_all
|
21
|
+
end
|
22
|
+
end
|
data/spec/game/game_spec.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
describe Game do
|
2
2
|
before do
|
3
|
-
@maze =
|
4
|
-
|
5
|
-
|
3
|
+
@maze = Maze.new 8, 8
|
4
|
+
backtrace = Backtrace.new @maze
|
5
|
+
backtrace.generate
|
6
|
+
@game = Game.new @maze
|
6
7
|
end
|
7
8
|
|
8
9
|
it '#select_goal_cell' do
|
9
10
|
selected_cell = @game.select_goal_cell(:hard)
|
10
|
-
expect(
|
11
|
+
expect(selected_cell.coords).not_to eq([0, 0])
|
11
12
|
expect(selected_cell.connected_neighbours.size).to eq(1)
|
12
13
|
end
|
13
14
|
|
@@ -16,17 +17,17 @@ describe Game do
|
|
16
17
|
expect(maze.matrix.flatten).to include(2)
|
17
18
|
end
|
18
19
|
|
19
|
-
it '#generate_goal :
|
20
|
+
it '#generate_goal :medium' do
|
20
21
|
maze = @game.generate_goal(:medium)
|
21
22
|
expect(maze.matrix.flatten).to include(2)
|
22
23
|
end
|
23
24
|
|
24
|
-
it '#generate_goal :
|
25
|
+
it '#generate_goal :hard' do
|
25
26
|
maze = @game.generate_goal(:hard)
|
26
27
|
expect(maze.matrix.flatten).to include(2)
|
27
28
|
end
|
28
29
|
|
29
|
-
it '#generate_goal :
|
30
|
+
it '#generate_goal :random' do
|
30
31
|
maze = @game.generate_goal(:random)
|
31
32
|
expect(maze.matrix.flatten).to include(2)
|
32
33
|
end
|
data/spec/maze/maze_cell_spec.rb
CHANGED
@@ -2,46 +2,113 @@ describe MazeCell do
|
|
2
2
|
before do
|
3
3
|
@width = 4
|
4
4
|
@height = 4
|
5
|
-
@maze =
|
6
|
-
@maze_zero = Maze.new(@width, @height)
|
5
|
+
@maze = Maze.new(@width, @height)
|
7
6
|
end
|
8
7
|
|
9
8
|
it '#initialize' do
|
9
|
+
coords = [1, 0]
|
10
|
+
indices = @maze.coords_to_indices *coords
|
11
|
+
params = indices.clone
|
12
|
+
params.push rand(99)
|
13
|
+
@maze.set_raw_value *params
|
10
14
|
maze_cell = MazeCell.new(@maze, 1, 0)
|
11
15
|
expect(maze_cell.maze).to eq(@maze)
|
12
|
-
expect(maze_cell.
|
13
|
-
expect(maze_cell.
|
16
|
+
expect(maze_cell.coords[0]).to eq(1)
|
17
|
+
expect(maze_cell.coords[1]).to eq(0)
|
18
|
+
expect(maze_cell.value).to eq(params.last)
|
14
19
|
end
|
15
20
|
|
16
|
-
it '
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
it '#value' do
|
22
|
+
coords = [0, 0]
|
23
|
+
params = coords.clone
|
24
|
+
params.push rand(99)
|
25
|
+
@maze.set_value *params
|
26
|
+
maze_cell = @maze.cell *coords
|
27
|
+
expect(maze_cell.value).to eq(params.last)
|
22
28
|
end
|
23
29
|
|
24
|
-
it '
|
25
|
-
maze_cell = @maze.cell
|
26
|
-
expect(maze_cell).to
|
27
|
-
expect(maze_cell).to
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
expect(
|
33
|
-
expect(
|
34
|
-
|
30
|
+
it '#forward' do
|
31
|
+
maze_cell = @maze.cell 1, 1
|
32
|
+
expect(maze_cell.forward(0)).to eq(@maze.cell(2, 1))
|
33
|
+
expect(maze_cell.forward(1)).to eq(@maze.cell(1, 2))
|
34
|
+
end
|
35
|
+
|
36
|
+
it '#backward' do
|
37
|
+
maze_cell = @maze.cell 1, 1
|
38
|
+
expect(maze_cell.backward(0)).to eq(@maze.cell(0, 1))
|
39
|
+
expect(maze_cell.backward(1)).to eq(@maze.cell(1, 0))
|
40
|
+
end
|
41
|
+
|
42
|
+
it '#has_wall_forward' do
|
43
|
+
@maze.set_raw_value_all 1
|
44
|
+
coords = [1, 1]
|
45
|
+
maze_cell = @maze.cell *coords
|
46
|
+
indices = @maze.coords_to_indices *coords
|
47
|
+
params = indices.clone
|
48
|
+
params[1] += 1
|
49
|
+
params.push 0
|
50
|
+
@maze.set_raw_value *params
|
51
|
+
expect(maze_cell.has_wall_forward(0)).to be_truthy
|
52
|
+
expect(maze_cell.has_wall_forward(1)).to be_falsy
|
53
|
+
end
|
54
|
+
|
55
|
+
it '#has_wall_backward' do
|
56
|
+
@maze.set_raw_value_all 1
|
57
|
+
coords = [1, 1]
|
58
|
+
maze_cell = @maze.cell *coords
|
59
|
+
indices = @maze.coords_to_indices *coords
|
60
|
+
params = indices.clone
|
61
|
+
params[1] -= 1
|
62
|
+
params.push 0
|
63
|
+
@maze.set_raw_value *params
|
64
|
+
expect(maze_cell.has_wall_backward(0)).to be_truthy
|
65
|
+
expect(maze_cell.has_wall_backward(1)).to be_falsy
|
66
|
+
end
|
67
|
+
|
68
|
+
it '#has_wall_forward on border' do
|
69
|
+
@maze.set_raw_value_all 1
|
70
|
+
coords = [@maze.dimensions[0] - 1, @maze.dimensions[1] - 1]
|
71
|
+
maze_cell = @maze.cell *coords
|
72
|
+
expect(maze_cell.has_wall_forward(0)).to be_truthy
|
73
|
+
expect(maze_cell.has_wall_forward(1)).to be_truthy
|
74
|
+
end
|
75
|
+
|
76
|
+
it '#has_wall_backward on border' do
|
77
|
+
@maze.set_raw_value_all 1
|
78
|
+
coords = [0, 0]
|
79
|
+
maze_cell = @maze.cell *coords
|
80
|
+
expect(maze_cell.has_wall_backward(0)).to be_truthy
|
81
|
+
expect(maze_cell.has_wall_backward(1)).to be_truthy
|
35
82
|
end
|
36
83
|
|
37
84
|
it '#connected?' do
|
38
|
-
|
39
|
-
|
85
|
+
coords = [0, 0]
|
86
|
+
@maze.set_raw_value_all 0
|
87
|
+
maze_cell = @maze.cell *coords
|
88
|
+
expect(maze_cell).to be_connected
|
89
|
+
|
90
|
+
@maze.set_raw_value_all 1
|
91
|
+
maze_cell = @maze.cell *coords
|
92
|
+
expect(maze_cell).not_to be_connected
|
93
|
+
|
94
|
+
indices = @maze.coords_to_indices *coords
|
95
|
+
params = indices.clone
|
96
|
+
params[1] += 1
|
97
|
+
params.push 0
|
98
|
+
@maze.set_raw_value *params
|
99
|
+
expect(maze_cell).to be_connected
|
40
100
|
end
|
41
101
|
|
42
|
-
it '#
|
43
|
-
|
44
|
-
|
102
|
+
it '#connected? (3x3)' do
|
103
|
+
maze = Maze.new 3, 3
|
104
|
+
maze.set_raw_value_all 1
|
105
|
+
maze_cell1 = maze.cell 1, 1
|
106
|
+
maze_cell2 = maze.cell 1, 2
|
107
|
+
maze.set_value 1, 1, 0
|
108
|
+
maze.set_value 1, 2, 0
|
109
|
+
maze.connect_cells maze_cell1, maze_cell2
|
110
|
+
|
111
|
+
expect(maze_cell1).to be_connected
|
45
112
|
end
|
46
113
|
|
47
114
|
it '#neighbours' do
|
@@ -53,6 +120,14 @@ describe MazeCell do
|
|
53
120
|
expect(neighbours).not_to include(@maze.cell(0, 0))
|
54
121
|
end
|
55
122
|
|
123
|
+
it '#connected_neighbours' do
|
124
|
+
@maze.set_raw_value_all 1
|
125
|
+
expect(@maze.cell(0, 0).connected_neighbours).to be_empty
|
126
|
+
|
127
|
+
@maze.set_raw_value_all 0
|
128
|
+
expect(@maze.cell(0, 0).connected_neighbours).to eq([@maze.cell(1, 0), @maze.cell(0, 1)])
|
129
|
+
end
|
130
|
+
|
56
131
|
it '#hash' do
|
57
132
|
allow_any_instance_of(MazeCell).to receive(:eql?).and_return(true) # remove elements only based on hash
|
58
133
|
cells = [@maze.cell(0, 0), @maze.cell(1, 0), @maze.cell(0, 1)]
|
@@ -82,14 +157,8 @@ describe MazeCell do
|
|
82
157
|
expect(maze_cell != nil).to be_truthy
|
83
158
|
end
|
84
159
|
|
85
|
-
it '#print' do
|
86
|
-
allow($stdout).to receive(:puts).exactly(3).times
|
87
|
-
maze_cell = @maze.cell(0, 0)
|
88
|
-
maze_cell.print
|
89
|
-
end
|
90
|
-
|
91
160
|
it '#inspect' do
|
92
161
|
maze_cell = @maze.cell(1, 0)
|
93
|
-
expect(maze_cell.inspect).to eq("#<MazeCell: @maze=#{@maze.inspect}, @
|
162
|
+
expect(maze_cell.inspect).to eq("#<MazeCell: @maze=#{@maze.inspect}, @c1=#{1}, @c2=#{0}>")
|
94
163
|
end
|
95
164
|
end
|
data/spec/maze/maze_spec.rb
CHANGED
@@ -1,47 +1,223 @@
|
|
1
1
|
describe Maze do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
it '#initialize 2d' do
|
3
|
+
width = rand(10) + 15
|
4
|
+
height = rand(10) + 15
|
5
|
+
maze = Maze.new(width, height)
|
6
|
+
expect(maze.dimensions[0]).to eq(width)
|
7
|
+
expect(maze.dimensions[1]).to eq(height)
|
8
|
+
expect(maze.matrix.length).to eq(width * 2 + 1) # raw dimensions
|
9
|
+
expect(maze.matrix[0].length).to eq(height * 2 + 1) # raw dimensions
|
6
10
|
end
|
7
11
|
|
8
|
-
it '#initialize' do
|
9
|
-
|
10
|
-
|
12
|
+
it '#initialize Nd' do
|
13
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
14
|
+
maze = Maze.new *dimensions
|
15
|
+
matrix_iter = maze.matrix
|
16
|
+
dimensions.each_with_index do |d, d_index|
|
17
|
+
expect(maze.dimensions[d_index]).to eq(dimensions[d_index])
|
18
|
+
expect(matrix_iter.length).to eq(dimensions[d_index] * 2 + 1) # raw dimensions
|
19
|
+
matrix_iter = matrix_iter[0]
|
20
|
+
end
|
11
21
|
end
|
12
22
|
|
13
|
-
it '#
|
14
|
-
|
23
|
+
it '#total_cells 2d' do
|
24
|
+
maze = Maze.new 4, 8
|
25
|
+
expect(maze.total_cells).to eq(32)
|
15
26
|
end
|
16
27
|
|
17
|
-
it '#
|
18
|
-
|
28
|
+
it '#total_cells Nd' do
|
29
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
30
|
+
maze = Maze.new *dimensions
|
31
|
+
expect(maze.total_cells).to eq(maze.dimensions.reduce(1) { |accum, d| accum*d })
|
32
|
+
end
|
33
|
+
|
34
|
+
it '#total_raw 2d' do
|
35
|
+
maze = Maze.new 4, 8
|
36
|
+
expect(maze.total_raw).to eq(9*17)
|
37
|
+
end
|
38
|
+
|
39
|
+
it '#cells 2d (0)' do
|
40
|
+
maze = Maze.new 2, 2
|
41
|
+
expect(maze.cells.length).to eq(4)
|
42
|
+
expect(maze.cells).to eq([[0, 0], [0, 1], [1, 0], [1, 1]].map { |coord| maze.cell *coord })
|
43
|
+
end
|
44
|
+
|
45
|
+
it '#cells 2d (1)' do
|
46
|
+
maze = Maze.new 5, 4
|
47
|
+
expect(maze.cells.length).to eq(20)
|
48
|
+
expect(maze.cells).to eq([[0,0],[0,1],[0,2],[0,3],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3],[3,0],[3,1],[3,2],[3,3],[4,0],[4,1],[4,2],[4,3]].map { |coord| maze.cell *coord })
|
49
|
+
end
|
50
|
+
|
51
|
+
it '#coords_to_indices 2d' do
|
52
|
+
maze = Maze.new 4, 8
|
53
|
+
x, y = 2, 3
|
54
|
+
i, j = maze.coords_to_indices x, y
|
55
|
+
expect([i, j]).to eq([5, 7])
|
56
|
+
end
|
57
|
+
|
58
|
+
it '#coords_to_indices 3d' do
|
59
|
+
maze = Maze.new 4, 8, 7
|
60
|
+
x, y, z = 2, 3, 1
|
61
|
+
i, j, k = maze.coords_to_indices x, y, z
|
62
|
+
expect([i, j, k]).to eq([5, 7, 3])
|
63
|
+
end
|
64
|
+
|
65
|
+
it '#coords_to_indices Nd' do
|
66
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
67
|
+
maze = Maze.new *dimensions
|
68
|
+
coords = dimensions.map { |d| rand(d) }
|
69
|
+
indices = maze.coords_to_indices *coords
|
70
|
+
expect(indices).to eq(coords.map { |c| 1 + 2 * c })
|
71
|
+
end
|
72
|
+
|
73
|
+
it '#get_raw_value 2d' do
|
74
|
+
maze = Maze.new 3, 3
|
75
|
+
expect(maze.get_raw_value(0, 0)).to eq(0)
|
76
|
+
end
|
77
|
+
|
78
|
+
it '#get_raw_value Nd' do
|
79
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
80
|
+
maze = Maze.new *dimensions
|
81
|
+
coords = dimensions.map { |d| rand(d) }
|
82
|
+
indices = maze.coords_to_indices *coords
|
83
|
+
expect(maze.get_raw_value(*indices)).to eq(0)
|
84
|
+
end
|
85
|
+
|
86
|
+
it '#set_raw_value 2d' do
|
87
|
+
maze = Maze.new 3, 3
|
88
|
+
maze.set_raw_value(0, 0, 10)
|
89
|
+
expect(maze.get_raw_value(0, 0)).to eq(10)
|
90
|
+
maze.set_raw_value(2, 5, 20)
|
91
|
+
expect(maze.get_raw_value(2, 5)).to eq(20)
|
92
|
+
end
|
93
|
+
|
94
|
+
it '#set_raw_value Nd' do
|
95
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
96
|
+
maze = Maze.new *dimensions
|
97
|
+
coords = dimensions.map { |d| rand(d) }
|
98
|
+
value = rand(99) + 1
|
99
|
+
indices = maze.coords_to_indices *coords
|
100
|
+
params = indices.clone
|
101
|
+
params.push value
|
102
|
+
maze.set_raw_value *params
|
103
|
+
expect(maze.get_raw_value(*indices)).to eq(value)
|
104
|
+
end
|
105
|
+
|
106
|
+
it '#set_raw_value_all 2d' do
|
107
|
+
maze = Maze.new 3, 3
|
108
|
+
maze.set_raw_value_all 10
|
109
|
+
coords = maze.dimensions.map { |d| rand(d) }
|
110
|
+
indices = maze.coords_to_indices *coords
|
111
|
+
expect(maze.get_raw_value(*indices)).to eq(10)
|
112
|
+
end
|
113
|
+
|
114
|
+
it '#set_raw_value_all Nd' do
|
115
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
116
|
+
maze = Maze.new *dimensions
|
117
|
+
maze.set_raw_value_all 11
|
118
|
+
coords = maze.dimensions.map { |d| rand(d) }
|
119
|
+
indices = maze.coords_to_indices *coords
|
120
|
+
expect(maze.get_raw_value(*indices)).to eq(11)
|
121
|
+
end
|
122
|
+
|
123
|
+
it '#get_value 2d' do
|
124
|
+
maze = Maze.new 3, 3
|
125
|
+
expect(maze.get_value(0, 0)).to eq(0)
|
126
|
+
end
|
127
|
+
|
128
|
+
it '#get_value Nd' do
|
129
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
130
|
+
maze = Maze.new *dimensions
|
131
|
+
coords = dimensions.map { |d| rand(d) }
|
132
|
+
expect(maze.get_value(*coords)).to eq(0)
|
133
|
+
end
|
134
|
+
|
135
|
+
it '#set_value 2d' do
|
136
|
+
maze = Maze.new 3, 3
|
137
|
+
maze.set_value(0, 0, 10)
|
138
|
+
expect(maze.get_value(0, 0)).to eq(10)
|
139
|
+
maze.set_value(2, 1, 20)
|
140
|
+
expect(maze.get_value(2, 1)).to eq(20)
|
141
|
+
end
|
142
|
+
|
143
|
+
it '#set_value Nd' do
|
144
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
145
|
+
maze = Maze.new *dimensions
|
146
|
+
coords = dimensions.map { |d| rand(d) }
|
147
|
+
value = rand(99) + 1
|
148
|
+
params = coords.clone
|
149
|
+
params.push value
|
150
|
+
maze.set_value *params
|
151
|
+
expect(maze.get_value(*coords)).to eq(value)
|
152
|
+
end
|
153
|
+
|
154
|
+
it '#cell 2d' do
|
155
|
+
maze = Maze.new 4, 9
|
156
|
+
cell = maze.cell 3, 8
|
19
157
|
expect(cell).to be_a_kind_of(MazeCell)
|
20
|
-
expect(cell.maze).to eq(
|
21
|
-
expect(cell
|
22
|
-
expect(cell
|
158
|
+
expect(cell.maze).to eq(maze)
|
159
|
+
expect(cell[0]).to eq(3)
|
160
|
+
expect(cell[1]).to eq(8)
|
23
161
|
end
|
24
162
|
|
25
|
-
it '#
|
26
|
-
|
27
|
-
|
28
|
-
|
163
|
+
it '#cell Nd' do
|
164
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
165
|
+
maze = Maze.new *dimensions
|
166
|
+
coords = dimensions.map { |d| rand(d) }
|
167
|
+
cell = maze.cell *coords
|
168
|
+
expect(cell).to be_a_kind_of(MazeCell)
|
169
|
+
expect(cell.maze).to eq(maze)
|
170
|
+
coords.each_with_index do |c, c_index|
|
171
|
+
expect(cell[c_index]).to eq(c)
|
172
|
+
end
|
29
173
|
end
|
30
174
|
|
31
|
-
|
32
|
-
|
175
|
+
it '#between_cells 2d' do
|
176
|
+
maze = Maze.new 4, 8
|
177
|
+
cell_a = maze.cell 1, 1
|
178
|
+
cell_b = maze.cell 2, 3
|
179
|
+
expect(maze.between_cells(cell_a, cell_b)).to eq([4.0, 5.0])
|
180
|
+
expect(maze.between_cells(cell_a, cell_a)).to eq([3.0, 3.0])
|
181
|
+
end
|
182
|
+
|
183
|
+
it '#connect_cells 2d' do
|
184
|
+
maze = Maze.new 4, 8
|
185
|
+
maze.set_raw_value_all 1
|
186
|
+
cell_a = maze.cell 1, 1
|
187
|
+
cell_b = maze.cell 2, 3
|
188
|
+
indices = maze.between_cells cell_a, cell_b
|
189
|
+
expect(maze.connect_cells(cell_a, cell_b)).to eq(indices)
|
190
|
+
expect(maze.get_raw_value(*indices)).to eq(0)
|
191
|
+
end
|
192
|
+
|
193
|
+
it '#disconnect_cells 2d' do
|
194
|
+
maze = Maze.new 4, 8
|
195
|
+
maze.set_raw_value_all 0
|
196
|
+
cell_a = maze.cell 1, 1
|
197
|
+
cell_b = maze.cell 2, 3
|
198
|
+
indices = maze.between_cells cell_a, cell_b
|
199
|
+
expect(maze.disconnect_cells(cell_a, cell_b)).to eq(indices)
|
200
|
+
expect(maze.get_raw_value(*indices)).to eq(1)
|
201
|
+
end
|
202
|
+
|
203
|
+
it '#hash 2d' do
|
204
|
+
maze = Maze.new 4, 8
|
205
|
+
expect(maze.hash).to eq(48)
|
33
206
|
end
|
34
207
|
|
35
|
-
it '#hash' do
|
36
|
-
|
208
|
+
it '#hash Nd' do
|
209
|
+
dimensions = ([nil]*(rand(10)+3)).map { |_| 1 + rand(4) }
|
210
|
+
maze = Maze.new *dimensions
|
211
|
+
expect(maze.hash).to eq(dimensions.reduce(""){ |accum, d| "#{accum}#{d}" }.to_i)
|
37
212
|
end
|
38
213
|
|
39
|
-
it '#inspect' do
|
40
|
-
|
214
|
+
it '#inspect 2d' do
|
215
|
+
maze = Maze.new 3, 6
|
216
|
+
expect(maze.inspect).to eq("#<Maze: @d1=3, @d2=6>")
|
41
217
|
end
|
42
218
|
|
43
|
-
it '#
|
44
|
-
|
45
|
-
|
219
|
+
it '#inspect 3d' do
|
220
|
+
maze = Maze.new 1, 5, 7
|
221
|
+
expect(maze.inspect).to eq("#<Maze: @d1=1, @d2=5, @d3=7>")
|
46
222
|
end
|
47
223
|
end
|
@@ -1,13 +1,23 @@
|
|
1
1
|
describe TreeSolver do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
it '#paths' do
|
9
|
-
paths = @tree_solver.paths(0, 0)
|
2
|
+
it '#paths 2d' do
|
3
|
+
maze = Maze.new 4, 4
|
4
|
+
backtrace = Backtrace.new maze
|
5
|
+
backtrace.generate
|
6
|
+
tree_solver = TreeSolver.new maze
|
7
|
+
paths = tree_solver.paths 0, 0
|
10
8
|
expect(paths.name).to eq('0,0')
|
11
9
|
expect(paths.size).to eq(16)
|
12
10
|
end
|
11
|
+
|
12
|
+
it '#paths Nd' do
|
13
|
+
dimensions = ([nil]*(rand(4)+2)).map { |_| 1 + rand(4) }
|
14
|
+
maze = Maze.new *dimensions
|
15
|
+
backtrace = Backtrace.new maze
|
16
|
+
backtrace.generate
|
17
|
+
tree_solver = TreeSolver.new maze
|
18
|
+
start_coords = [0]*dimensions.length
|
19
|
+
paths = tree_solver.paths *start_coords
|
20
|
+
expect(paths.name).to eq(start_coords.map(&:to_s).join(','))
|
21
|
+
expect(paths.size).to be > 0
|
22
|
+
end
|
13
23
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
SimpleCov.start
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rmaze
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pedro Lira
|
@@ -52,18 +52,19 @@ files:
|
|
52
52
|
- ".ruby-version"
|
53
53
|
- Gemfile
|
54
54
|
- Gemfile.lock
|
55
|
+
- LICENSE.txt
|
55
56
|
- README.md
|
56
57
|
- Rakefile
|
57
58
|
- bin/rmaze
|
59
|
+
- lib/algorithm/backtrace.rb
|
58
60
|
- lib/game/game.rb
|
59
61
|
- lib/maze/maze.rb
|
60
|
-
- lib/maze/maze_btrace.rb
|
61
62
|
- lib/maze/maze_cell.rb
|
62
63
|
- lib/rmaze.rb
|
63
64
|
- lib/solver/tree_solver.rb
|
64
65
|
- rmaze.gemspec
|
66
|
+
- spec/algorithm/backtrace_spec.rb
|
65
67
|
- spec/game/game_spec.rb
|
66
|
-
- spec/maze/maze_btrace_spec.rb
|
67
68
|
- spec/maze/maze_cell_spec.rb
|
68
69
|
- spec/maze/maze_spec.rb
|
69
70
|
- spec/solver/tree_solver_spec.rb
|
@@ -88,13 +89,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
89
|
version: '0'
|
89
90
|
requirements: []
|
90
91
|
rubyforge_project:
|
91
|
-
rubygems_version: 2.4.
|
92
|
+
rubygems_version: 2.4.6
|
92
93
|
signing_key:
|
93
94
|
specification_version: 4
|
94
95
|
summary: RMaze
|
95
96
|
test_files:
|
97
|
+
- spec/algorithm/backtrace_spec.rb
|
96
98
|
- spec/game/game_spec.rb
|
97
|
-
- spec/maze/maze_btrace_spec.rb
|
98
99
|
- spec/maze/maze_cell_spec.rb
|
99
100
|
- spec/maze/maze_spec.rb
|
100
101
|
- spec/solver/tree_solver_spec.rb
|
data/lib/maze/maze_btrace.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'maze/maze'
|
2
|
-
|
3
|
-
class MazeBTrace < Maze
|
4
|
-
protected
|
5
|
-
def initialize_matrix
|
6
|
-
super
|
7
|
-
make_unconnected
|
8
|
-
end
|
9
|
-
|
10
|
-
public
|
11
|
-
def initialize(width, height)
|
12
|
-
super(width, height)
|
13
|
-
end
|
14
|
-
|
15
|
-
def between_cells(cell_a, cell_b)
|
16
|
-
i_a, j_a = xy_to_ij(cell_a.x, cell_a.y)
|
17
|
-
i_b, j_b = xy_to_ij(cell_b.x, cell_b.y)
|
18
|
-
[(i_a + i_b).abs / 2, (j_a + j_b).abs / 2]
|
19
|
-
end
|
20
|
-
|
21
|
-
def generate
|
22
|
-
super
|
23
|
-
stack = []
|
24
|
-
visited_cells = []
|
25
|
-
all_index_cells = []
|
26
|
-
current_cell = cell(rand(@width), rand(@height))
|
27
|
-
visited_cells.push current_cell
|
28
|
-
(0...@width).each do |x|
|
29
|
-
(0...@height).each do |y|
|
30
|
-
all_index_cells << [x, y]
|
31
|
-
end
|
32
|
-
end
|
33
|
-
while visited_cells.size != @width * @height
|
34
|
-
neighbours = current_cell.neighbours
|
35
|
-
visited_cells.push current_cell
|
36
|
-
unvisited_neighbours = (neighbours - visited_cells)
|
37
|
-
if !unvisited_neighbours.empty?
|
38
|
-
stack.push current_cell
|
39
|
-
random_neighbour = unvisited_neighbours.shuffle.shift
|
40
|
-
i, j = xy_to_ij(random_neighbour.x, random_neighbour.y)
|
41
|
-
inner_i, inner_j = between_cells(current_cell, random_neighbour)
|
42
|
-
@matrix[inner_i][inner_j] = 0
|
43
|
-
current_cell = random_neighbour
|
44
|
-
visited_cells.push current_cell
|
45
|
-
elsif !stack.empty?
|
46
|
-
current_cell = stack.pop
|
47
|
-
else
|
48
|
-
unvisited_index_cells = all_index_cells - visited_cells.map { |c| [c.x, c.y] }
|
49
|
-
unvisited_index_cell = unvisited_index_cells.shuffle.shift
|
50
|
-
current_cell = cell(unvisited_index_cell[0], unvisited_index_cell[0])
|
51
|
-
visited_cells.push current_cell
|
52
|
-
end
|
53
|
-
visited_cells.uniq!
|
54
|
-
end
|
55
|
-
self
|
56
|
-
end
|
57
|
-
end
|