subparry_labyrinth_solver 1.0.2 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +69 -1
- data/labyrinth.gemspec +6 -4
- data/lib/subparry_labyrinth_solver.rb +8 -0
- data/lib/subparry_labyrinth_solver/labyrinth.rb +36 -34
- data/lib/subparry_labyrinth_solver/node.rb +17 -15
- data/lib/subparry_labyrinth_solver/solver.rb +29 -28
- data/lib/subparry_labyrinth_solver/version.rb +1 -1
- metadata +2 -3
- data/lib/subparry_labyrinth_solver/exceptions.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0bf1b3d99443815ae5ff546b0abbd55512351f75ddb9d7de6299a2166c6f796
|
4
|
+
data.tar.gz: 4c7f159471b6f6e9c24178cd8d165f60e8a593f6f89160e92720e6c826e9a747
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2efa961d309f5b2c729adb7f8c2c4b8a576195566fd27605401e2c9882bdedfdcf15a7562b47bf0a65e96ef5ffc0788ed358c29353abab9169440f33787278dd
|
7
|
+
data.tar.gz: 566eb78c672a2bec211480ac09651dbdf458f7674983dbae0b4079691d553410ca71dfa9f4f7753a91a9cae1d59e13b5e55b7a18bdc37ab707690ad56a0d232f
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -6,4 +6,72 @@
|
|
6
6
|
|
7
7
|
## Description
|
8
8
|
|
9
|
-
Fun little project about labyrinth solving.
|
9
|
+
Fun little project about labyrinth solving inspired in mice that find cheese in a maze.
|
10
|
+
|
11
|
+
It exposes some classes to create a labyrinth and then find the path to the cheese.
|
12
|
+
|
13
|
+
Be advised: **It does not support labyrinths with circular paths and if you try to solve one of these, it will result in an infinite loop**.
|
14
|
+
|
15
|
+
That being said, I plan to support that kind of labyrinths soon.
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
```
|
20
|
+
gem install subparry_labyrinth_solver
|
21
|
+
```
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'subparry_labyrinth_solver'
|
25
|
+
|
26
|
+
# First define labyrinth data as a 2 dimensional array where
|
27
|
+
# each square is represented by a hash with :up, :down, :left and :right
|
28
|
+
# as keys and an optional key :cheese representing the "goal" square.
|
29
|
+
# A value of true for a direction represents an open path, and false
|
30
|
+
# represents a wall.
|
31
|
+
|
32
|
+
data = [
|
33
|
+
[ # The first row
|
34
|
+
{ up: false, right: false, left: false, down: true },
|
35
|
+
{ up: false, right: false, left: false, down: true, cheese: true },
|
36
|
+
],
|
37
|
+
[ # The second row
|
38
|
+
{ up: true, down: false, right: true, left: false },
|
39
|
+
{ up: true, down: false, right: false, left: true }
|
40
|
+
]
|
41
|
+
]
|
42
|
+
|
43
|
+
# This data represents this maze:
|
44
|
+
# ___ ___
|
45
|
+
# | | 🧀|
|
46
|
+
# | | |
|
47
|
+
# | |
|
48
|
+
# |_______|
|
49
|
+
|
50
|
+
# Then initialize the labyrinth:
|
51
|
+
lab = LabyrinthSolver::Labyrinth.new(data)
|
52
|
+
|
53
|
+
# Then initialize the solver:
|
54
|
+
solver = LabyrinthSolver::Solver.new(lab)
|
55
|
+
|
56
|
+
# And call solve on the instance. This call will trigger
|
57
|
+
# the cheese-finding loop and record the right path.
|
58
|
+
solver.solve
|
59
|
+
|
60
|
+
# Confirm that cheese was found:
|
61
|
+
solver.cheese? # => true
|
62
|
+
|
63
|
+
# Retrieve the path:
|
64
|
+
solver.path # => [:down, :right, :up]
|
65
|
+
|
66
|
+
# Get the coordinates of the cheese:
|
67
|
+
solver.position # => <struct x: 1, y: 0>
|
68
|
+
|
69
|
+
# Note that the coordinates are like image coordinates where top-left
|
70
|
+
# corner is x: 0, y: 0 and bottom-right is x: width, y: length
|
71
|
+
```
|
72
|
+
|
73
|
+
## Pending
|
74
|
+
|
75
|
+
- Support labyrinths with circular paths
|
76
|
+
- Create a labyrinth maker class
|
77
|
+
- Visual representation of labyrinths
|
data/labyrinth.gemspec
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'subparry_labyrinth_solver/version'
|
4
6
|
|
5
7
|
Gem::Specification.new do |spec|
|
6
8
|
spec.name = 'subparry_labyrinth_solver'
|
@@ -8,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
8
10
|
spec.authors = ['Adrian Parry']
|
9
11
|
spec.email = ['subparry@gmail.com']
|
10
12
|
spec.summary = 'An exercise for gem creation based on solving a labyrinth'
|
11
|
-
spec.description = 'This gem allows to find path
|
13
|
+
spec.description = 'This gem allows to find path to cheese in a labyrinth'
|
12
14
|
spec.homepage = 'https://github.com/subparry/subparry-labyrinth-solver'
|
13
15
|
spec.license = 'MIT'
|
14
16
|
spec.platform = Gem::Platform::RUBY
|
@@ -23,8 +25,8 @@ Gem::Specification.new do |spec|
|
|
23
25
|
'lib/**/*.rb',
|
24
26
|
'labyrinth.gemspec'
|
25
27
|
]
|
26
|
-
spec.extra_rdoc_files
|
27
|
-
|
28
|
+
spec.extra_rdoc_files = ['README.md']
|
29
|
+
|
28
30
|
spec.add_development_dependency 'codecov', '~> 0.1'
|
29
31
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
30
32
|
spec.add_development_dependency 'rubocop', '~> 1.8'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'subparry_labyrinth_solver/node'
|
2
|
+
require 'subparry_labyrinth_solver/labyrinth'
|
3
|
+
require 'subparry_labyrinth_solver/solver'
|
4
|
+
module LabyrinthSolver
|
5
|
+
class MissingPathsError < ArgumentError; end
|
6
|
+
class InvalidMoveError < RuntimeError; end
|
7
|
+
class MissingCheeseError < ArgumentError; end
|
8
|
+
end
|
@@ -2,47 +2,49 @@
|
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
class
|
8
|
-
|
5
|
+
module LabyrinthSolver
|
6
|
+
Point = Struct.new(:x, :y)
|
7
|
+
# Labyrinth class in charge of keeping track of all nodes and performing movements
|
8
|
+
class Labyrinth
|
9
|
+
attr_reader :height, :width, :position
|
9
10
|
|
10
|
-
|
11
|
+
extend Forwardable
|
11
12
|
|
12
|
-
|
13
|
+
def_delegators :current_node, :open?, :cheese?, :close
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def self.validate_data data
|
16
|
+
raise ArgumentError unless data.respond_to?(:each) && data.first.respond_to?(:each)
|
17
|
+
raise MissingCheeseError unless data.any? { |rows| rows.any? {|paths| paths[:cheese]} }
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
def initialize(data)
|
21
|
+
Labyrinth.validate_data data
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
@height = data.size
|
24
|
+
@width = data.first.size
|
25
|
+
@position = Point.new(0, 0)
|
26
|
+
@nodes = data.collect { |row| row.collect { |cell| Node.new(cell) } }
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
29
|
+
def go direction
|
30
|
+
raise InvalidMoveError, "Attempted to move #{direction}" unless open? direction
|
31
|
+
|
32
|
+
case direction
|
33
|
+
when :up
|
34
|
+
@position.y -= 1
|
35
|
+
when :down
|
36
|
+
@position.y += 1
|
37
|
+
when :left
|
38
|
+
@position.x -= 1
|
39
|
+
when :right
|
40
|
+
@position.x += 1
|
41
|
+
end
|
40
42
|
end
|
41
|
-
end
|
42
43
|
|
43
|
-
|
44
|
+
private
|
44
45
|
|
45
|
-
|
46
|
-
|
46
|
+
def current_node
|
47
|
+
@nodes[@position.y][@position.x]
|
48
|
+
end
|
47
49
|
end
|
48
|
-
end
|
50
|
+
end
|
@@ -1,23 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Class to represent a single labyrinth square or node
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module LabyrinthSolver
|
5
|
+
class Node
|
6
|
+
def initialize(paths)
|
7
|
+
raise MissingPathsError if paths[:up].nil? || paths[:down].nil? || paths[:left].nil? || paths[:right].nil?
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
@paths = paths
|
10
|
+
@cheese = paths[:cheese] || false
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def open? direction
|
14
|
+
@paths[direction]
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def cheese?
|
18
|
+
@cheese
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
def close direction
|
22
|
+
@paths[direction] = false
|
23
|
+
end
|
22
24
|
end
|
23
|
-
end
|
25
|
+
end
|
@@ -1,41 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
|
+
module LabyrinthSolver
|
5
|
+
# Takes a labyrinth and attempts to solve it saving the path taken
|
6
|
+
class Solver
|
7
|
+
attr_reader :path
|
4
8
|
|
5
|
-
|
6
|
-
class Solver
|
7
|
-
attr_reader :path
|
9
|
+
extend Forwardable
|
8
10
|
|
9
|
-
|
11
|
+
OPPOSITE_DIRECTIONS = { up: :down, right: :left, down: :up, left: :right }.freeze
|
10
12
|
|
11
|
-
|
13
|
+
def_delegators :@labyrinth, :position, :cheese?, :go, :close
|
12
14
|
|
13
|
-
|
15
|
+
def initialize labyrinth
|
16
|
+
raise ArgumentError unless labyrinth.instance_of? Labyrinth
|
14
17
|
|
15
|
-
|
16
|
-
|
18
|
+
@labyrinth = labyrinth
|
19
|
+
@path = []
|
20
|
+
end
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def go_next
|
23
|
-
next_dir = OPPOSITE_DIRECTIONS.find { |dir, opp| @labyrinth.open?(dir) && opp != @path.last }
|
24
|
-
return dead_end unless next_dir
|
22
|
+
def go_next
|
23
|
+
next_dir = OPPOSITE_DIRECTIONS.find { |dir, opp| @labyrinth.open?(dir) && opp != @path.last }
|
24
|
+
return dead_end unless next_dir
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
go(next_dir.first)
|
27
|
+
@path.push(next_dir.first)
|
28
|
+
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
def solve
|
31
|
+
go_next until cheese?
|
32
|
+
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
private
|
35
|
+
|
36
|
+
def dead_end
|
37
|
+
to_close = path.pop
|
38
|
+
go(OPPOSITE_DIRECTIONS[to_close])
|
39
|
+
close(to_close)
|
40
|
+
end
|
40
41
|
end
|
41
|
-
end
|
42
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: subparry_labyrinth_solver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrian Parry
|
@@ -94,7 +94,7 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0.16'
|
97
|
-
description: This gem allows to find path
|
97
|
+
description: This gem allows to find path to cheese in a labyrinth
|
98
98
|
email:
|
99
99
|
- subparry@gmail.com
|
100
100
|
executables: []
|
@@ -109,7 +109,6 @@ files:
|
|
109
109
|
- Rakefile
|
110
110
|
- labyrinth.gemspec
|
111
111
|
- lib/subparry_labyrinth_solver.rb
|
112
|
-
- lib/subparry_labyrinth_solver/exceptions.rb
|
113
112
|
- lib/subparry_labyrinth_solver/labyrinth.rb
|
114
113
|
- lib/subparry_labyrinth_solver/node.rb
|
115
114
|
- lib/subparry_labyrinth_solver/solver.rb
|