subparry_labyrinth_solver 1.0.2 → 1.0.4
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/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
|