knossos 0.1.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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: af6ea5967ce320c641b22ccc126afd464d79546ab900100a35e2399958c2abbc
4
+ data.tar.gz: e08581a4b99d792c5187ea79d4d4a685e0837657aa9c45626e7dc6980f8040fc
5
+ SHA512:
6
+ metadata.gz: a4a1164a115ea289648b802486cacae6b1d839f915f81f522d7e429b09c3ed0a82951591ce49990897d88d95f5a98e1b380c3761702596c44c13044c600aa9ab
7
+ data.tar.gz: 412bedde05650f80014e68342dc6c56d72bb07f891d098cb71d28658ef1a5827dad8f11b4e9dd39bb33acd5cd23db9365b0ab2e40191d1e4095225c12d01c667
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ This project uses [Semantic Versioning][sv].
5
+
6
+ ## [Unreleased][new]
7
+
8
+ ## [0.1.0][0.1.0] — 2020-01-07
9
+ ### Added
10
+ - Create an `Algorithm` module to hold the maze generators.
11
+ - Create a `Renderer` module to hold the methods that produce graphical
12
+ representations of a maze.
13
+ - Create `Renderer::Text` to produce ASCII text representations.
14
+ ### Changed
15
+ - Move `AldousBroder` to the `Algorithm` module
16
+ - Move `BinaryTree` to the `Algorithm` module
17
+ - Move `RecursiveBacktracker` to the `Algorithm` module
18
+ - Move `Sidewinder` to the `Algorithm` module
19
+ - Move `Wilsons` to the `Algorithm` module
20
+ - Move and rename `Display` to `Renderer::Image`
21
+ - Rename `Display.to_png` to `Image.render`
22
+ ### Fixed
23
+ - Correctly scope private class methods in `Sidewinder`
24
+ - Algorithms now uniformly require a `grid:` keyword argument to `.carve`
25
+ ### Removed
26
+ - `Grid` no longer has a custom `to_s` method to produce a graphical maze.
27
+ Use `Renderer::Text.render` instead.
28
+
29
+ ## [0.0.0][0.0.0] — 2020-01-02
30
+ ### Added
31
+ - Restructure the project as a Ruby gem.
32
+
33
+ ---
34
+ _This file is composed with [GitHub Flavored Markdown][gfm]._
35
+
36
+ [gfm]: https://github.github.com/gfm/
37
+ [sv]: https://semver.org
38
+
39
+ [new]: https://github.com/petejh/knossos/compare/HEAD..v0.1.0
40
+ [0.1.0]: https://github.com/petejh/knossos/releases/tag/v0.1.0
41
+ [0.0.0]: https://github.com/petejh/knossos/releases/tag/v0.0.0
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Peter J. Hinckley
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,74 @@
1
+ # Knossos
2
+ A library for generating mazes inspired by [_Mazes for Programmers_][mfp] by
3
+ Jamis Buck and [_Think Labyrinth!_][tlab] by Walter Pullen.
4
+
5
+ ## Installation
6
+ Add the following line to your application's Gemfile:
7
+ ```bash
8
+ gem 'knossos'
9
+ ```
10
+ Or install the library globally with:
11
+ ```bash
12
+ ~$ gem install knossos
13
+ ```
14
+
15
+ ## Usage
16
+ ```ruby
17
+ #!/usr/bin/env ruby
18
+
19
+ require 'knossos'
20
+
21
+ grid = Knossos::Grid.new({rows: 5, columns: 5})
22
+ Algorithm::BinaryTree.carve(grid: grid)
23
+
24
+ text_renderer = Renderer::Text.new
25
+ puts text_renderer.render(grid: grid)
26
+
27
+ image_renderer = Renderer::Image.new(grid)
28
+ png = image_renderer.render
29
+ png.save("maze.png")
30
+ ```
31
+
32
+ ## Contributing
33
+ Bug reports and pull requests are welcome on [GitHub][orig]. Knossos provides
34
+ a safe, welcoming space for collaboration. Everyone contributing to our
35
+ project—including the codebase, issue trackers, chat, email, social media and
36
+ the like—is expected to uphold our [Code of Conduct][coc].
37
+
38
+ ### Setting Up
39
+ [Fork the project][fork] on GitHub and make a local clone. Install dependencies,
40
+ and run the tests:
41
+ ```bash
42
+ ~/knossos$ bin/setup
43
+ ~/knossos$ bundle exec rake rspec
44
+ ```
45
+
46
+ ### Running the Code
47
+ You can experiment with the code interactively using:
48
+ ```bash
49
+ ~/knossos$ bin/console
50
+ ```
51
+
52
+ ### Publishing
53
+ To release a new version of the library, first increment the version number in
54
+ `lib/knossos/version.rb` following [Semantic Versioning][semv] policy, and
55
+ update `CHANGELOG.md`. Commit your work, and finally, run:
56
+ ```bash
57
+ # Create a git tag, push commits and tags, and publish to rubygems.org
58
+ ~/knossos$ bundle exec rake release
59
+ ```
60
+
61
+ ## License
62
+ This gem is available as open source under the terms of the [MIT License][mit].
63
+
64
+ ---
65
+ _This file is composed with [GitHub Flavored Markdown][gfm]._
66
+
67
+ [coc]: https://github.com/petejh/knossos/blob/master/CODE_OF_CONDUCT.md
68
+ [fork]: https://help.github.co://help.github.com/en/github/getting-started-with-github/fork-a-repo
69
+ [gfm]: https://github.github.com/gfm/
70
+ [orig]: https://github.com/petejh/knossos
71
+ [mfp]: https://pragprog.com/book/jbmaze/mazes-for-programmers
72
+ [mit]: https://github.com/petejh/knossos/blob/master/LICENSE.txt
73
+ [semv]: https://semver.org
74
+ [tlab]: http://astrolog.org/labyrnth.htm
@@ -0,0 +1,9 @@
1
+ require 'knossos/algorithm'
2
+ require 'knossos/cell'
3
+ require 'knossos/error'
4
+ require 'knossos/grid'
5
+ require 'knossos/renderer'
6
+ require 'knossos/version'
7
+
8
+ module Knossos
9
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'algorithm/aldous_broder'
2
+ require_relative 'algorithm/binary_tree'
3
+ require_relative 'algorithm/recursive_backtracker'
4
+ require_relative 'algorithm/sidewinder'
5
+ require_relative 'algorithm/wilsons'
6
+
7
+ module Knossos
8
+ module Algorithm
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module Algorithm
2
+ class AldousBroder
3
+ def self.carve(grid:)
4
+ cell = grid.random_cell
5
+ unvisited = grid.cell_count - 1
6
+
7
+ while unvisited > 0
8
+ neighbor = grid.neighborhood(cell).sample
9
+
10
+ if neighbor.links.empty?
11
+ grid.build_passage(cell, neighbor)
12
+ unvisited -= 1
13
+ end
14
+
15
+ cell = neighbor
16
+ end
17
+
18
+ grid
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module Algorithm
2
+ class BinaryTree
3
+ def self.carve(grid:)
4
+ grid.each_cell do |cell|
5
+ neighbors = []
6
+
7
+ north = grid.north(cell)
8
+ neighbors << north if north
9
+
10
+ east = grid.east(cell)
11
+ neighbors << east if east
12
+
13
+ neighbor = neighbors.sample
14
+ grid.build_passage(cell, neighbor) if neighbor
15
+ end
16
+
17
+ grid
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module Algorithm
2
+ class RecursiveBacktracker
3
+ def self.carve(grid:, start_at: grid.random_cell)
4
+ stack = []
5
+ stack.push start_at
6
+
7
+ while stack.any?
8
+ current = stack.last
9
+ neighbors = grid.neighborhood(current).select { |n| n.links.empty? }
10
+
11
+ if neighbors.empty?
12
+ stack.pop
13
+ else
14
+ neighbor = neighbors.sample
15
+ grid.build_passage(current, neighbor)
16
+ stack.push(neighbor)
17
+ end
18
+ end
19
+
20
+ grid
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,42 @@
1
+ module Algorithm
2
+ class Sidewinder
3
+ def self.carve(grid:)
4
+ grid.each_row do |row|
5
+ run = []
6
+ row.each do |cell|
7
+ run << cell
8
+
9
+ if close_out_run?(grid, cell)
10
+ member = run.sample
11
+
12
+ north = grid.north(member)
13
+ grid.build_passage(member, north) if north
14
+
15
+ run.clear
16
+ else
17
+ east = grid.east(cell)
18
+ grid.build_passage(cell, east)
19
+ end
20
+ end
21
+ end
22
+
23
+ grid
24
+ end
25
+
26
+ class << self
27
+ private
28
+
29
+ def close_out_run?(grid, cell)
30
+ at_east_border?(grid, cell) || (!at_north_border?(grid, cell) && rand(2) == 0)
31
+ end
32
+
33
+ def at_east_border?(grid, cell)
34
+ grid.east(cell).nil?
35
+ end
36
+
37
+ def at_north_border?(grid, cell)
38
+ grid.north(cell).nil?
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ module Algorithm
2
+ class Wilsons
3
+ def self.carve(grid:)
4
+ unvisited = []
5
+ grid.each_cell { |cell| unvisited << cell }
6
+
7
+ unvisited.delete(unvisited.sample)
8
+ while unvisited.any?
9
+ cell = unvisited.sample
10
+ path = [cell]
11
+
12
+ while unvisited.include?(cell)
13
+ cell = grid.neighborhood(cell).sample
14
+
15
+ position = path.index(cell)
16
+ if position
17
+ path = path[0..position]
18
+ else
19
+ path << cell
20
+ end
21
+ end
22
+
23
+ 0.upto(path.length - 2) do |index|
24
+ grid.build_passage(path[index], path[index + 1])
25
+ unvisited.delete(path[index])
26
+ end
27
+ end
28
+
29
+ grid
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ module Knossos
2
+ class Cell
3
+ attr_reader :row, :column, :links
4
+ attr_accessor :contents
5
+
6
+ def initialize(args = {})
7
+ args = defaults.merge(args)
8
+
9
+ @row = args[:row]
10
+ @column = args[:column]
11
+ @contents = args[:contents]
12
+
13
+ @links = []
14
+ end
15
+
16
+ def link_to(cell)
17
+ @links << cell
18
+ end
19
+
20
+ def linked?(cell)
21
+ @links.include? cell
22
+ end
23
+
24
+ private
25
+
26
+ def defaults
27
+ {}
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Knossos
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,96 @@
1
+ module Knossos
2
+ class Grid
3
+ attr_reader :rows, :columns
4
+
5
+ def initialize(args = {})
6
+ args = defaults.merge(args)
7
+
8
+ @rows = args[:rows]
9
+ @columns = args[:columns]
10
+
11
+ @grid = assemble_grid
12
+ end
13
+
14
+ def [](row, column)
15
+ return nil unless row_is_valid?(row)
16
+ return nil unless column_is_valid?(row, column)
17
+
18
+ grid[row][column]
19
+ end
20
+
21
+ def each_row
22
+ grid.each_with_index { |cells, row| yield(cells, row) }
23
+ end
24
+
25
+ def each_cell
26
+ each_row do |cells, row|
27
+ cells.each_with_index { |cell, column| yield(cell, row, column) }
28
+ end
29
+ end
30
+
31
+ def random_cell
32
+ row = rand(rows)
33
+ column = rand(columns_for(row))
34
+
35
+ self[row, column]
36
+ end
37
+
38
+ def cell_count
39
+ grid.reduce(0) { |acc, e| acc += e.count }
40
+ end
41
+
42
+ def north(cell)
43
+ self[cell.row - 1, cell.column]
44
+ end
45
+
46
+ def east(cell)
47
+ self[cell.row, cell.column + 1]
48
+ end
49
+
50
+ def south(cell)
51
+ self[cell.row + 1, cell.column]
52
+ end
53
+
54
+ def west(cell)
55
+ self[cell.row, cell.column - 1]
56
+ end
57
+
58
+ def neighborhood(cell)
59
+ [:north, :east, :south, :west].map { |dir| self.send(dir, cell) }.compact
60
+ end
61
+
62
+ def build_passage(cell, other)
63
+ cell.link_to(other)
64
+ other.link_to(cell)
65
+ end
66
+
67
+ private
68
+ attr_accessor :grid
69
+
70
+ def defaults
71
+ { :rows => 10,
72
+ :columns => 10
73
+ }
74
+ end
75
+
76
+ def assemble_grid
77
+ Array.new(@rows) do |row|
78
+ Array.new(@columns) do |column|
79
+ Cell.new({ :row => row, :column => column })
80
+ end
81
+ end
82
+ end
83
+
84
+ def row_is_valid?(row)
85
+ row.between?(0, @rows - 1)
86
+ end
87
+
88
+ def column_is_valid?(row, column)
89
+ column.between?(0, columns_for(row) - 1)
90
+ end
91
+
92
+ def columns_for(row)
93
+ grid[row].count
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'renderer/image'
2
+ require_relative 'renderer/png_adapter'
3
+ require_relative 'renderer/text'
4
+
5
+ module Knossos
6
+ module Renderer
7
+ end
8
+ end
@@ -0,0 +1,56 @@
1
+ module Renderer
2
+ class Image
3
+ def initialize(grid, **options)
4
+ options = defaults.merge(options)
5
+
6
+ @grid = grid
7
+ @cell_size = options[:cell_size]
8
+ @background_color = options[:background_color]
9
+ @wall_color = options[:wall_color]
10
+
11
+ @image = create_canvas
12
+ end
13
+
14
+ def render
15
+ grid.each_cell do |cell, row, column|
16
+ x1, y1, x2, y2 = coordinates_for(row, column)
17
+
18
+ image.line(x1, y1, x2, y1, wall_color) unless grid.north(cell)
19
+ image.line(x1, y1, x1, y2, wall_color) unless grid.west(cell)
20
+
21
+ image.line(x2, y1, x2, y2, wall_color) unless cell.linked?(grid.east(cell))
22
+ image.line(x1, y2, x2, y2, wall_color) unless cell.linked?(grid.south(cell))
23
+ end
24
+
25
+ image
26
+ end
27
+
28
+ private
29
+ attr_reader :grid, :cell_size, :background_color, :wall_color
30
+ attr_accessor :image
31
+
32
+ def defaults
33
+ { cell_size: 10,
34
+ background_color: PNGAdapter::Color::WHITE,
35
+ wall_color: PNGAdapter::Color::BLACK
36
+ }
37
+ end
38
+
39
+ def create_canvas
40
+ width = grid.columns * cell_size
41
+ height = grid.rows * cell_size
42
+
43
+ PNGAdapter::Image.new(width + 1, height + 1, background_color)
44
+ end
45
+
46
+ def coordinates_for(row, column)
47
+ x1 = cell_size * column
48
+ y1 = cell_size * row
49
+
50
+ x2 = cell_size * (column + 1)
51
+ y2 = cell_size * (row + 1)
52
+
53
+ [x1, y1, x2, y2]
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,27 @@
1
+ require 'chunky_png'
2
+
3
+ module Renderer
4
+ module PNGAdapter
5
+ class Image
6
+ def initialize(width, height, color)
7
+ @image = ChunkyPNG::Image.new(width, height, color)
8
+ end
9
+
10
+ def line(x0, y0, x1, y1, color)
11
+ image.line(x0, y0, x1, y1, color)
12
+ end
13
+
14
+ def save(filename)
15
+ image.save(filename)
16
+ end
17
+
18
+ private
19
+ attr_accessor :image
20
+ end
21
+
22
+ module Color
23
+ WHITE = ChunkyPNG::Color::WHITE
24
+ BLACK = ChunkyPNG::Color::BLACK
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module Renderer
2
+ class Text
3
+ def initialize
4
+ # nothing to do
5
+ end
6
+
7
+ def render(grid:)
8
+ output = "+" + "---+" * grid.columns + "\n"
9
+
10
+ grid.each_row do |row|
11
+ body = "|"
12
+ bottom = "+"
13
+
14
+ row.each do |cell|
15
+ east = grid.east(cell)
16
+ east_border = cell.linked?(east) ? " " : "|"
17
+
18
+ south = grid.south(cell)
19
+ south_border = cell.linked?(south) ? " " : "---"
20
+
21
+ body << " " << east_border
22
+ bottom << south_border << "+"
23
+ end
24
+
25
+ output << body << "\n"
26
+ output << bottom << "\n"
27
+ end
28
+
29
+ output
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Knossos
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knossos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter J. Hinckley
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-01-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chunky_png
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: A library for generating mazes inspired by "Mazes for Programmers" by
70
+ Jamis Buck and "Think Labyrinth!" by Walter Pullen.
71
+ email:
72
+ - petejh.code@q.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - CHANGELOG.md
78
+ - LICENSE.txt
79
+ - README.md
80
+ - lib/knossos.rb
81
+ - lib/knossos/algorithm.rb
82
+ - lib/knossos/algorithm/aldous_broder.rb
83
+ - lib/knossos/algorithm/binary_tree.rb
84
+ - lib/knossos/algorithm/recursive_backtracker.rb
85
+ - lib/knossos/algorithm/sidewinder.rb
86
+ - lib/knossos/algorithm/wilsons.rb
87
+ - lib/knossos/cell.rb
88
+ - lib/knossos/error.rb
89
+ - lib/knossos/grid.rb
90
+ - lib/knossos/renderer.rb
91
+ - lib/knossos/renderer/image.rb
92
+ - lib/knossos/renderer/png_adapter.rb
93
+ - lib/knossos/renderer/text.rb
94
+ - lib/knossos/version.rb
95
+ homepage: https://github.com/petejh/knossos
96
+ licenses:
97
+ - MIT
98
+ metadata:
99
+ homepage_uri: https://github.com/petejh/knossos
100
+ source_code_uri: https://github.com/petejh/knossos
101
+ changelog_uri: https://github.com/petejh/knossos/CHANGELOG.md
102
+ bug_tracker_uri: https://github.com/petejh/knossos/issues
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '2.6'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.0.3
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: A library for generating mazes.
122
+ test_files: []