multifarious 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+