multifarious 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.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ # The MIT License
2
+
3
+ Copyright (c) 2011 Kristján Pétursson
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,20 @@
1
+ ### 1-2-3 Go
2
+
3
+ require 'rubygems'
4
+ require 'multifarious'
5
+ mosaic = Mosaic.new(10, 10)
6
+ mosaic.generate!
7
+ mosaic.draw
8
+
9
+ ------------
10
+ |bx..myheq~|
11
+ |zp..$$$&&v|
12
+ |l!!A$$$&&s|
13
+ |;!!u$$$r::|
14
+ |wkBCDd++::|
15
+ |Efa_@@++'=|
16
+ |/###@@tn^^|
17
+ |F###%%%G^^|
18
+ |H###%%%I**|
19
+ |gojc%%%i**|
20
+ ------------
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "multifarious"
8
+ gem.summary = %Q{A multi-sized tile layer}
9
+ gem.description = %Q{Multifarious makes generating a solid block of variously-sized tiles simple.}
10
+ gem.email = "kristjan@gmail.com"
11
+ gem.homepage = "http://github.com/kristjan/multifarious"
12
+ gem.authors = ["Kristján Pétursson"]
13
+ gem.add_development_dependency "rspec"
14
+ gem.version = "0.1.1"
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION')
40
+ version = File.read('VERSION')
41
+ else
42
+ version = ""
43
+ end
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "multifarious #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
@@ -0,0 +1,99 @@
1
+ class Mosaic
2
+ attr_reader :rows, :cols, :grid, :tiles
3
+
4
+ DuplicateTile = Class.new(RuntimeError)
5
+
6
+ def initialize(rows, cols)
7
+ @rows, @cols = rows, cols
8
+ @grid = Array.new(@rows) { Array.new(@cols) {:empty} }
9
+ @tiles = []
10
+ end
11
+
12
+ def area
13
+ @rows * @cols
14
+ end
15
+
16
+ def available_area
17
+ count = 0
18
+ (0...@rows).each do |row|
19
+ (0...@cols).each do |col|
20
+ count += 1 if available?(row, col)
21
+ end
22
+ end
23
+ count
24
+ end
25
+
26
+ def available?(row, col)
27
+ @grid[row] && @grid[row][col] == :empty
28
+ end
29
+
30
+ def covered_area
31
+ area - available_area
32
+ end
33
+
34
+ DRAW_CHARS = %[!@#\$%^&*_=+\/\:;'.~abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ].split('')
35
+ def draw
36
+ charmap = Hash[@tiles.zip(DRAW_CHARS.first(@tiles.size))]
37
+ charmap[:empty] = ' '
38
+ horizontal = '-'*(@cols + 2)
39
+ puts horizontal
40
+ puts(@grid.map do |row|
41
+ "|#{row.map{|tile| charmap[tile]}.join}|"
42
+ end.join("\n"))
43
+ puts horizontal
44
+ end
45
+
46
+ def can_place?(tile, row, col)
47
+ (row...(row + tile.rows)).each do |r|
48
+ (col...(col + tile.cols)).each do |c|
49
+ return false unless available?(r, c)
50
+ end
51
+ end
52
+ true
53
+ end
54
+
55
+ def generate!
56
+ candidates = Tile.rand_covering(area)
57
+ until candidates.empty? || candidates.all?{|tile| tile.is_a?(Tile::Small) }
58
+ tile = candidates.shift
59
+ placed = false
60
+ 5.times {
61
+ row, col = random_location(tile)
62
+ placed = place!(tile, row, col)
63
+ break if placed
64
+ }
65
+ candidates.concat tile.shatter unless placed
66
+ end
67
+ # Fill in missed spaces
68
+ (0...@rows).each do |row|
69
+ (0...@cols).each do |col|
70
+ place!(Tile::Small.new, row, col) if available?(row, col)
71
+ end
72
+ end
73
+ true
74
+ end
75
+
76
+ def has_tile?(tile)
77
+ !!@tiles.find {|t| t.equal? tile}
78
+ end
79
+
80
+ def place!(tile, row, col)
81
+ raise DuplicateTile if has_tile?(tile)
82
+ return false unless can_place?(tile, row, col)
83
+ @tiles << tile
84
+ (row...(row + tile.rows)).each do |r|
85
+ (col...(col + tile.cols)).each do |c|
86
+ @grid[r][c] = tile
87
+ end
88
+ end
89
+ true
90
+ end
91
+
92
+ def random_location(tile)
93
+ [rand(rows - tile.rows + 1), rand(cols - tile.cols + 1)]
94
+ end
95
+
96
+ def space_available?
97
+ covered_area < area
98
+ end
99
+ end
@@ -0,0 +1,61 @@
1
+ class Tile
2
+ attr_reader :rows, :cols
3
+
4
+ class Large < Tile
5
+ def initialize
6
+ @rows = @cols = 3
7
+ end
8
+ end
9
+
10
+ class Medium < Tile
11
+ def initialize
12
+ @rows = @cols = 2
13
+ end
14
+ end
15
+
16
+ class Small < Tile
17
+ def initialize
18
+ @rows = @cols = 1
19
+ end
20
+
21
+ def shatter
22
+ []
23
+ end
24
+ end
25
+
26
+ def self.rand(max_area=nil)
27
+ tiles = [Large, Medium, Small].map(&:new).select do |tile|
28
+ max_area.nil? || tile.area <= max_area
29
+ end
30
+ tiles[Kernel.rand(tiles.size)]
31
+ end
32
+
33
+ def self.rand_covering(area)
34
+ tiles = []
35
+ while self.area(tiles) < area
36
+ tiles << Tile.rand(area - self.area(tiles))
37
+ end
38
+ tiles
39
+ end
40
+
41
+ def self.area(tiles)
42
+ tiles.inject(0) {|sum, tile| sum + tile.area}
43
+ end
44
+
45
+ def initialize(rows, cols)
46
+ @rows, @cols = rows, cols
47
+ end
48
+
49
+ def ==(other)
50
+ return false unless other.is_a?(Tile)
51
+ self.rows == other.rows && self.cols == other.cols
52
+ end
53
+
54
+ def area
55
+ @rows * @cols
56
+ end
57
+
58
+ def shatter
59
+ Tile.rand_covering(area)
60
+ end
61
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift 'lib'
2
+
3
+ require 'tile'
4
+ require 'mosaic'
@@ -0,0 +1,124 @@
1
+ require "spec_helper"
2
+
3
+ describe Mosaic do
4
+ describe "initializing" do
5
+ it "stores the rows" do
6
+ Mosaic.new(1,2).rows.should == 1
7
+ end
8
+
9
+ it "stores the columns" do
10
+ Mosaic.new(1,2).cols.should == 2
11
+ end
12
+ end
13
+
14
+ it "knows its area" do
15
+ Mosaic.new(1,1).area.should == 1
16
+ Mosaic.new(2,2).area.should == 4
17
+ Mosaic.new(2,3).area.should == 6
18
+ end
19
+
20
+ it "knows its covered area" do
21
+ m = Mosaic.new(4,4)
22
+ m.covered_area.should == 0
23
+ m.place!(Tile::Small.new, 0, 0)
24
+ m.covered_area.should == 1
25
+ m.place!(Tile::Large.new, 1, 1)
26
+ m.covered_area.should == 10
27
+ end
28
+
29
+ it "knows its available area" do
30
+ m = Mosaic.new(4,4)
31
+ m.available_area.should == 16
32
+ m.place!(Tile::Small.new, 0, 0)
33
+ m.available_area.should == 15
34
+ m.place!(Tile::Large.new, 1, 1)
35
+ m.available_area.should == 6
36
+ end
37
+
38
+ describe "available spaces" do
39
+ before :each do
40
+ @mosaic = Mosaic.new(1,2)
41
+ @mosaic.place!(Tile::Small.new, 0, 1)
42
+ end
43
+
44
+ it "knows when a space is available" do
45
+ @mosaic.available?(0,0).should be_true
46
+ end
47
+
48
+ it "knows when a space is unavailable" do
49
+ @mosaic.available?(0,1).should be_false
50
+ end
51
+
52
+ it "considers out-of-bounds unavailable" do
53
+ @mosaic.available?(3,3).should be_false
54
+ end
55
+ end
56
+
57
+ describe "determining whether a tile can be placed" do
58
+ before :each do
59
+ @mosaic = Mosaic.new(4, 4)
60
+ @tile = Tile::Medium.new
61
+ @mosaic.place!(@tile, 2, 2)
62
+ end
63
+
64
+ it "returns true when all spaces are clear" do
65
+ @mosaic.can_place?(@tile, 0, 0).should be_true
66
+ end
67
+
68
+ it "returns false when there is overlap" do
69
+ @mosaic.can_place?(@tile, 1, 1).should be_false
70
+ end
71
+
72
+ it "returns false when the tile is out of bounds" do
73
+ @mosaic.can_place?(@tile, 0, 3).should be_false
74
+ end
75
+ end
76
+
77
+ describe "placing a tile" do
78
+ before(:each) do
79
+ @mosaic = Mosaic.new(4, 4)
80
+ @mosaic.place!(Tile::Medium.new, 2, 2)
81
+ @tile = Tile::Medium.new
82
+ end
83
+
84
+ it "sets every grid space to the tile" do
85
+ @mosaic.place!(@tile, 0, 0)
86
+ [0, 1].each do |row|
87
+ [0, 1].each do |col|
88
+ @mosaic.grid[row][col].should == @tile
89
+ end
90
+ end
91
+ end
92
+
93
+ it "adds the tile to the tile list" do
94
+ @mosaic.place!(@tile, 0, 0)
95
+ @mosaic.tiles.should include(@tile)
96
+ end
97
+
98
+ it "returns true when the tile is placed" do
99
+ @mosaic.place!(@tile, 0, 0).should be_true
100
+ end
101
+
102
+ it "returns false if placement fails" do
103
+ @mosaic.place!(@tile, 1, 1).should be_false
104
+ end
105
+
106
+ it "doesn't let you place the same tile twice" do
107
+ @mosaic.place!(@tile, 0, 0)
108
+ expect {
109
+ @mosaic.place!(@tile, 0, 0)
110
+ }.to raise_error(Mosaic::DuplicateTile)
111
+ end
112
+ end
113
+
114
+ it "knows when it contains a tile" do
115
+ mosaic = Mosaic.new(4,4)
116
+ tile = Tile::Medium.new
117
+ other_tile = Tile::Medium.new
118
+ mosaic.should_not have_tile(tile)
119
+ mosaic.place!(tile, 0, 0)
120
+ mosaic.should have_tile(tile)
121
+ mosaic.should_not have_tile(other_tile)
122
+ end
123
+
124
+ end
@@ -0,0 +1,57 @@
1
+ require "spec_helper"
2
+
3
+ describe Tile do
4
+ describe "initializing" do
5
+ it "stores the rows" do
6
+ Tile.new(1,2).rows.should == 1
7
+ end
8
+
9
+ it "stores the columns" do
10
+ Tile.new(1,2).cols.should == 2
11
+ end
12
+ end
13
+
14
+ describe "comparison" do
15
+ it "is == to another tile of the same dimensions" do
16
+ Tile.new(1,2).should == Tile.new(1,2)
17
+ end
18
+
19
+ it "is not == to a tile with a different width" do
20
+ Tile.new(1,1).should_not == Tile.new(2,1)
21
+ end
22
+
23
+ it "is not == to a tile with a different height" do
24
+ Tile.new(1,1).should_not == Tile.new(1,2)
25
+ end
26
+
27
+ it "is #equal to itself" do
28
+ tile = Tile.new(1,1)
29
+ tile.should be_equal(tile)
30
+ end
31
+
32
+ it "is not #equal to another tile of the same dimensions" do
33
+ Tile.new(1,1).should_not be_equal(Tile.new(1,1))
34
+ end
35
+ end
36
+
37
+ it "knows its area" do
38
+ Tile.new(1,1).area.should == 1
39
+ Tile.new(2,2).area.should == 4
40
+ Tile.new(2,3).area.should == 6
41
+ end
42
+
43
+ it "knows the area of many tiles" do
44
+ tiles = 1.upto(3).map{|i| Tile.new(i, i) }
45
+ Tile.area(tiles).should == 14
46
+ end
47
+
48
+ describe "picking a random tile" do
49
+ it "returns some tile when there is no area limit" do
50
+ Tile.rand.should be_a(Tile)
51
+ end
52
+
53
+ it "returns a tile of a maximum area" do
54
+ Tile.rand(3).should be_a(Tile::Small)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ Dir['lib/**/*.rb'].each {|f| require f}
5
+
6
+ RSpec.configure do |config|
7
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multifarious
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - "Kristj\xC3\xA1n P\xC3\xA9tursson"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-10 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: Multifarious makes generating a solid block of variously-sized tiles simple.
36
+ email: kristjan@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.md
44
+ files:
45
+ - .rspec
46
+ - LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - lib/mosaic.rb
50
+ - lib/tile.rb
51
+ - multifarious.rb
52
+ - spec/lib/mosaic_spec.rb
53
+ - spec/lib/tile_spec.rb
54
+ - spec/spec_helper.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/kristjan/multifarious
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.3.7
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: A multi-sized tile layer
89
+ test_files: []
90
+