maze_solver 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dce2968fbc1b982e1bc809b3c04721d97a5b011a
4
+ data.tar.gz: bdb569186748d7521587601d64f95440794d886d
5
+ SHA512:
6
+ metadata.gz: 6315411ca831fdd12408374e8a515ad11653c854bba934934bf308da8a49474e1f345f7689268cac5a6db7eb1a3a0a76a8fdf51926c82422d6a1f7adda478aeb
7
+ data.tar.gz: 609cef963ec700f6aadd4bf0fb0819315b1e3edfcb405f44d7c4d38ed1134345855785d386a3750b4ab00d48d794ba86a7839d5f2a253d0b0922e902580ba31d
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.12.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in maze_solver.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # MazeSolver
2
+
3
+ This is a Maze Solver for labyrinths with specific format
4
+ _ : PATH
5
+ X : WALL
6
+ S : START
7
+ G : GOAL (EXIT)
8
+
9
+ It finds the shortest Path using A* Algorithm
10
+
11
+ ## Use
12
+
13
+ ```Ruby
14
+ require 'maze_solver'
15
+
16
+ MazeSolver.to_image('file_name')
17
+
18
+ MazeSolver.to_json('file_name')
19
+ ```
20
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ task :console do
9
+ exec "irb -r mygem -I ./lib"
10
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "maze_solver"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,73 @@
1
+ require "maze_solver/version"
2
+ require "maze_solver/position"
3
+ require "maze_solver/element"
4
+ require "maze_solver/maze"
5
+ require "json"
6
+
7
+ module MazeSolver
8
+
9
+ ## Better to implemented on a class after thoughts......Anyway..mabe later!!!
10
+
11
+ def to_json(file_name)
12
+
13
+ maze_array = from_file(file_name)
14
+ maze_list = to_maze(maze_array)
15
+ maze = Maze.new(maze_list)
16
+ solution = maze.solve
17
+
18
+ solution_array = solution.map do |element|
19
+ { value: element.value, position: "(#{element.position.x}, #{element.position.y})" }
20
+ end
21
+
22
+ JSON.generate(solution_array)
23
+ end
24
+
25
+ def to_image(file_name)
26
+ ## Print solution to console
27
+ maze_array = from_file(file_name)
28
+ maze_list = to_maze(maze_array)
29
+ maze = Maze.new(maze_list)
30
+ solution = maze.solve
31
+
32
+ maze_list.each do |element|
33
+ maze_array[element.position.y][element.position.x] = '*' if solution.find { |se| se == element }
34
+ end
35
+
36
+ puts '!!!!!!!SOLUTIOM!!!!!!!'
37
+ print maze_array.join('')
38
+
39
+ nil
40
+ end
41
+
42
+ def from_file(file_name)
43
+ ## Read file and export it to a 2D array
44
+ ## Throw error if file doesnt exist
45
+
46
+ begin
47
+ File.readlines(file_name).map { |line| line.split('') }
48
+ rescue abort("can't open #{file_name}" )
49
+ end
50
+ end
51
+
52
+ def to_maze(maze_array)
53
+ maze = []
54
+ maze_array.each_with_index do |line, line_index|
55
+ line.each_with_index do |char, col_index|
56
+ case char
57
+ when '_'
58
+ then maze << Element.new(char, Position.new(col_index, line_index,), 'PATH')
59
+ when 'X'
60
+ then maze << Element.new(char, Position.new(col_index, line_index,), 'WALL')
61
+ when 'S'
62
+ then maze << Element.new(char, Position.new(col_index, line_index,), 'START')
63
+ when 'G'
64
+ then maze << Element.new(char, Position.new(col_index, line_index,), 'GOAL' )
65
+ end
66
+ end
67
+ end
68
+ maze
69
+ end
70
+
71
+ module_function :to_json, :to_image, :from_file, :to_maze
72
+ end
73
+
@@ -0,0 +1,47 @@
1
+ module MazeSolver
2
+ class Element
3
+ ## Represent an element in the Maze, defined by his position and value
4
+ ## There are also some Astar factors like g, h, f functions
5
+ ## Also the precedent element, (parent)
6
+ ## visited is a flag that aknowledge that the element had been visited
7
+
8
+ attr_accessor :g, :h, :f, :parent
9
+ attr_reader :value, :position, :type
10
+
11
+ def to_s
12
+ ## Represent the class
13
+ "(#{@position}), #{@value}"
14
+ end
15
+
16
+ def ==(element)
17
+ ## Compare two elements by its position
18
+ return true if ( @position == element.position && @value == element.value && @type == element.type )
19
+ return false
20
+ end
21
+
22
+ def initialize(value, position, type)
23
+ @value = value
24
+ @position = position
25
+ @type = type
26
+
27
+ ## Default Astar values
28
+ @g = 0
29
+ @h = 0
30
+ @f = 0
31
+ @parent = nil
32
+ end
33
+
34
+ def is_goal?
35
+ ## is the end point?
36
+ return true if @type == 'GOAL'
37
+ false
38
+ end
39
+
40
+ def is_path?
41
+ ## Can be traversed?
42
+ return true if ( @type == 'PATH' || @type == 'START' || @type == 'GOAL' )
43
+ false
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,128 @@
1
+ require "maze_solver/tree"
2
+
3
+ module MazeSolver
4
+ class Maze
5
+ ## Class which Represent the maze
6
+
7
+ attr_reader :maze
8
+
9
+ def initialize(maze)
10
+ @maze = maze
11
+ @closed = [] # Already proccessed
12
+
13
+ set_start_and_goal
14
+ end
15
+
16
+ def solve
17
+ ## Begin process !!
18
+ tree = Tree.new # Instanciate a tree with start point as first pushing
19
+ tree.push(@start, @start.f)
20
+
21
+ reverse_path( algorithm(tree) ) # RUUUN
22
+ end
23
+
24
+ private
25
+
26
+ def algorithm(tree)
27
+ ## Core of this gem
28
+ ## Recurcive implementetion
29
+
30
+ element = tree.next
31
+ @closed.push(element)
32
+
33
+ return element if element.is_goal? ## Found EXIT!!!!
34
+
35
+ neighbors(element).each do |neighbor|
36
+ if !in_closed?(neighbor)
37
+
38
+ if tree.in?(neighbor, neighbor.f)
39
+ if neighbor.g > element.g + 1
40
+ update_neighbor(neighbor, element)
41
+ end
42
+ else
43
+
44
+ update_neighbor(neighbor, element)
45
+ tree.push(neighbor, neighbor.f)
46
+ end
47
+ end
48
+ end
49
+
50
+ algorithm(tree)
51
+ end
52
+
53
+ def set_start_and_goal
54
+ ## Check and set if there is start and goal point
55
+ start = []
56
+ goal = []
57
+
58
+ @maze.each do |elem|
59
+ start << elem if elem.type == 'START'
60
+ goal << elem if elem.type == 'GOAL'
61
+ end
62
+
63
+ abort('Check if there is exactly
64
+ one start and one end point') if (start.length != 1 || goal.length != 1 )
65
+
66
+ @start = start.pop
67
+ @goal = goal.pop
68
+ end
69
+
70
+ def reverse_path(element, acc=[])
71
+ ## Recurcively find path
72
+ return acc << element if element == @start
73
+
74
+ acc << element
75
+ reverse_path(element.parent, acc)
76
+ end
77
+
78
+ def in_closed?(element)
79
+ ## Is the element proccessed?
80
+ @closed.find { |ce| ce == element }
81
+ end
82
+
83
+ def h(element)
84
+ ## heuristic function (Guessing distance to goal)
85
+ ## I implement it this way can be implement it with another way
86
+ ( @goal.position.x - element.position.x ).abs +
87
+ ( @goal.position.y - element.position.y ).abs
88
+ end
89
+
90
+ def valid_neighbor(position)
91
+ ## Check if neighbor is a valid path (_, G, S )
92
+ neighbor = @maze.find { |elem| elem.position == position }
93
+
94
+ if (neighbor)
95
+ return neighbor if neighbor.is_path?
96
+ end
97
+ nil
98
+ end
99
+
100
+ def neighbors(element)
101
+ ## Adjacent Elements !!Only valid ones!!
102
+ neighbors = []
103
+
104
+ directions = [
105
+ element.position.north,
106
+ element.position.east,
107
+ element.position.south,
108
+ element.position.west
109
+ ]
110
+
111
+ directions.each do |d|
112
+ neighbor = valid_neighbor(d)
113
+ neighbors << neighbor if neighbor
114
+ end
115
+ neighbors
116
+ end
117
+
118
+ def update_neighbor(neighbor, element)
119
+ ## process neighbor
120
+
121
+ neighbor.g = element.g + 1
122
+ neighbor.h = h(neighbor)
123
+ neighbor.f = neighbor.g + neighbor.h
124
+ neighbor.parent = element
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,38 @@
1
+ module MazeSolver
2
+ class Position
3
+ ## Position Of Element in the maze (Cartesians)##
4
+ attr_reader :x, :y # column, row position respectively
5
+
6
+ def initialize(x, y)
7
+ @x = x
8
+ @y = y
9
+ end
10
+
11
+ def ==(position)
12
+ ## Compare two positions ##
13
+ @x == position.x && @y == position.y
14
+ end
15
+
16
+ def to_s
17
+ ## Represent a Position ##
18
+ "(#{@x}, #{@y})"
19
+ end
20
+
21
+ ## Adjacent position (In a Cartesian way) ##
22
+ def north
23
+ Position.new(@x, @y-1)
24
+ end
25
+
26
+ def east
27
+ Position.new(@x+1, @y)
28
+ end
29
+
30
+ def south
31
+ Position.new(@x, @y+1)
32
+ end
33
+
34
+ def west
35
+ Position.new(@x-1, @y)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,48 @@
1
+ module MazeSolver
2
+ class Tree
3
+ ## Binary Tree Interprentation for Apress Open List
4
+ def initialize
5
+ ## Tree in hash form with priority as key and array
6
+ ## of elements as value
7
+ @tree = Hash.new { |hash, key| hash[key] = [] }
8
+ end
9
+
10
+ def to_s
11
+ ## Representation of Tree
12
+ o = '{'
13
+ @tree.each do |k, v|
14
+ o += ":#{k} => [\n"
15
+ v.each do |el|
16
+ o += "#{el} \n"
17
+ end
18
+ o+="\n]\n"
19
+ end
20
+ o += "}"
21
+ end
22
+
23
+ def push(element, priority)
24
+ ## push an element in the stack
25
+ @tree[priority] << element
26
+ end
27
+
28
+ def in?(element, priority)
29
+ ## check if elements is in the stack
30
+ if @tree.has_key?(priority)
31
+ return true if @tree[priority].find { |elem| elem == element }
32
+ end
33
+ false
34
+ end
35
+
36
+ def next
37
+ ## Poping an element
38
+
39
+ return nil if @tree.empty?
40
+
41
+ priority, element = *@tree.min # get the min priority
42
+ result = element.shift # get the first element
43
+ @tree.delete(priority) if element.empty? # if branch with this priority is empty delete it
44
+
45
+ return result
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module MazeSolver
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'maze_solver/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "maze_solver"
8
+ spec.version = MazeSolver::VERSION
9
+ spec.authors = ["Ioannis-Andreas Philippas"]
10
+ spec.email = ["ioannis.philippas@gmail.com"]
11
+
12
+ spec.summary = 'Solve maze with astar and simple algorithm'
13
+ spec.description = 'Solve maze with astar and simple algorithm'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.12"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec", "~> 3.0"
23
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maze_solver
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Ioannis-Andreas Philippas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Solve maze with astar and simple algorithm
56
+ email:
57
+ - ioannis.philippas@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - lib/maze_solver.rb
71
+ - lib/maze_solver/element.rb
72
+ - lib/maze_solver/maze.rb
73
+ - lib/maze_solver/position.rb
74
+ - lib/maze_solver/tree.rb
75
+ - lib/maze_solver/version.rb
76
+ - maze_solver.gemspec
77
+ homepage:
78
+ licenses: []
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.5.1
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Solve maze with astar and simple algorithm
100
+ test_files: []