meiro 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 807c34ccb647483756d96c85136bc6dc68e50826
4
+ data.tar.gz: e0d74e6acf21e69d114a35bcdbaba7a7baa36d46
5
+ SHA512:
6
+ metadata.gz: 430f65cfb0a5213494544f83528d696f6e6c3e51167767d7b5d06fd4e082fcb6e5f5ea94026e44ef883e4e05482b7203bdc56809860899a9d0ee05dac962081c
7
+ data.tar.gz: 7affc64b4ec94c5ddba8fe32a38073cfc7d2a1a8fc2a985e915d7fc786a16503a8a856963f8b0b657cd90e3ebd30f37a835d7ced05c65ef957c6486407d333b4
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in meiro.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Yuki Morohoshi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,97 @@
1
+ # Meiro
2
+
3
+ Meiroはいわゆる「ローグライク」系、「不思議のダンジョン」系といわれる
4
+ ゲームに使われるようなランダムダンジョン生成用のRubyライブラリです。
5
+
6
+ Meiro is Random Dungeon Generator for Ruby.
7
+ It generates maps used for so-called Rogue-like games.
8
+
9
+ ## インストール - Installation
10
+
11
+ TODO:
12
+
13
+ ## 使用方法 - Usage
14
+
15
+ ### 基本的な使い方 - Basic Usage
16
+
17
+ ```ruby
18
+ require 'meiro'
19
+
20
+ options = {
21
+ width: 40,
22
+ height: 25,
23
+ min_room_number: 3,
24
+ max_room_number: 6,
25
+ min_room_width: 5,
26
+ max_room_width: 10,
27
+ min_room_height: 3,
28
+ max_room_height: 5,
29
+ block_split_factor: 3.0,
30
+ }
31
+ dungeon = Meiro.create_dungeon(options)
32
+ floor = dungeon.generate_random_floor
33
+
34
+ floor.classify!(:rogue_like)
35
+ puts floor.to_s
36
+ # =>
37
+ #
38
+ #
39
+ #
40
+ # |-----|
41
+ # |.....|
42
+ # |.....|
43
+ # |.....+########### |----------|
44
+ # |.....| # |..........|
45
+ # |-+---| ######+..........|
46
+ # # |..........|
47
+ # # |-+--------|
48
+ # # #
49
+ # ##### ###
50
+ # # #
51
+ # |-+--------| #
52
+ # |..........| |---+------|
53
+ # |..........| |..........|
54
+ # |..........+###+..........|
55
+ # |..........| |..........|
56
+ # |----------| |----------|
57
+ #
58
+ #
59
+ #
60
+ #
61
+ #
62
+ ```
63
+
64
+ ### 基本構造 - Basic Structure
65
+
66
+ ```
67
+ Dungeon
68
+ |
69
+ +--- Floor
70
+ |
71
+ +--- MapLayer
72
+ | |
73
+ | +--- Tiles
74
+ |
75
+ +--- Block(Root)
76
+ |
77
+ +--- Block
78
+ | |
79
+ | +--- Block --- Room
80
+ | |
81
+ | +--- Block --- Room
82
+ |
83
+ +--- Block
84
+ |
85
+ +--- Block --- Room
86
+ |
87
+ +--- Block --- Room
88
+
89
+ ```
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it ( http://github.com/hoshi-sano/meiro/fork )
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ # RSpec::Core::RakeTask.new(:spec)
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ t.rspec_opts = %w[-c -f progress -r ./spec/spec_helper.rb]
7
+ t.pattern = 'spec/**/*_spec.rb'
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => :spec
@@ -0,0 +1,20 @@
1
+ require "meiro/version"
2
+ require "meiro/options"
3
+ require "meiro/dungeon"
4
+ require "meiro/map_layer"
5
+ require "meiro/floor"
6
+ require "meiro/block"
7
+ require "meiro/partition"
8
+ require "meiro/room"
9
+ require "meiro/tile"
10
+ require "meiro/passage"
11
+
12
+ module Meiro
13
+ module ModuleMethods
14
+ def create_dungeon(options={})
15
+ Dungeon.new(options)
16
+ end
17
+ end
18
+
19
+ extend ModuleMethods
20
+ end
@@ -0,0 +1,239 @@
1
+ module Meiro
2
+ class Block
3
+ class << self
4
+ def set_mixin_room_module(mod)
5
+ @mixin_room_module = mod
6
+ end
7
+
8
+ def remove_mixin_room_module
9
+ @mixin_room_module = nil
10
+ end
11
+ end
12
+
13
+ MIN_WIDTH = FLOOR_MIN_WIDTH * 2 + 1
14
+ MIN_HEIGHT = FLOOR_MIN_HEIGHT * 2 + 1
15
+ MARGIN = 1
16
+
17
+ attr_reader :x, :y, :width, :height,
18
+ :upper_left, :lower_right,
19
+ :partition, :room, :parent
20
+
21
+ def initialize(floor, x, y, width, height, parent=nil)
22
+ @floor = floor
23
+ @x = x
24
+ @y = y
25
+ @width = width
26
+ @height = height
27
+ @parent = parent
28
+ if @width >= @height
29
+ @shape = :horizontal
30
+ else
31
+ @shape = :vertical
32
+ end
33
+ @separated = false
34
+ end
35
+
36
+ def separate
37
+ return false if !separatable?
38
+ if horizontal?
39
+ vertical_separate
40
+ else
41
+ horizontal_separate
42
+ end
43
+ @separated = true
44
+ end
45
+
46
+ def unify
47
+ @upper_left = nil
48
+ @lower_right = nil
49
+ @partition = nil
50
+ @separated = false
51
+ end
52
+
53
+ def separatable?
54
+ # 分割済みのBlockはそれ以上分割できない
55
+ return false if @separated
56
+
57
+ if horizontal?
58
+ (@width / 2) >= MIN_WIDTH
59
+ else
60
+ (@height / 2) >= MIN_HEIGHT
61
+ end
62
+ end
63
+
64
+ def separated?
65
+ @separated
66
+ end
67
+
68
+ # 共通の親(先祖)を返す
69
+ def find_ancestor(other)
70
+ return nil if !self.parent || !other.parent
71
+
72
+ # 世代を揃える
73
+ if self.generation >= other.generation
74
+ younger, older = self, other
75
+ else
76
+ younger, older = other, self
77
+ end
78
+ diff = younger.generation - older.generation
79
+ diff.times { younger = younger.parent }
80
+
81
+ # 親が同一になるまで遡る
82
+ until younger.parent == older.parent
83
+ younger = younger.parent
84
+ older = older.parent
85
+ end
86
+
87
+ younger.parent
88
+ end
89
+
90
+ # 同じ親から分割された片割れを返す。
91
+ # 親がいない場合はnilを返す。
92
+ def brother
93
+ if @parent
94
+ bros = [@parent.upper_left, @parent.lower_right]
95
+ bros.delete(self)
96
+ bros.pop
97
+ else
98
+ nil
99
+ end
100
+ end
101
+
102
+ # 辺を共有するBlockを返す。そのBlockが親(分割済)であった場合は、分
103
+ # 割されたいずれかのうち、辺を共有している方を返す。
104
+ def neighbors
105
+ got = []
106
+ neighborhood_xy.each do |x, y|
107
+ got << @floor.get_block(x, y)
108
+ end
109
+ got.compact.uniq
110
+ end
111
+
112
+ # 指定した座標が自身に含まれるか否かを返す
113
+ def include?(x, y)
114
+ @x <= x && x <= (@x + @width) &&
115
+ @y <= y && y <= (@y + @height)
116
+ end
117
+
118
+ # 辺を共有するBlockを検索するため、辺を共有するBlockがある場合には
119
+ # それに含まれるであろうx座標、y座標を返す
120
+ def neighborhood_xy
121
+ res = []
122
+ c = 2 # Partitionがあるため、端から2マス先を見る必要がある
123
+ if @x - c >= 0
124
+ res << (@y..(@y + @height)).map {|y| [@x - c, y] }
125
+ end
126
+ if @x + @width + c <= @floor.width
127
+ res << (@y..(@y + @height)).map {|y| [@x + @width + c, y] }
128
+ end
129
+ if @y - c >= 0
130
+ res << (@x..(@x + @width)).map {|x| [x, @y - c] }
131
+ end
132
+ if @y + @height + c <= @floor.height
133
+ res << (@x..(@x + @width)).map {|x| [x, @y + @height + c] }
134
+ end
135
+ res.flatten(1)
136
+ end
137
+
138
+ def horizontal?
139
+ @shape == :horizontal ? true : false
140
+ end
141
+
142
+ def vertical?
143
+ @shape == :vertical ? true : false
144
+ end
145
+
146
+ def generation
147
+ @parent ? @parent.generation + 1 : 1
148
+ end
149
+
150
+ def flatten
151
+ res = []
152
+ if separated?
153
+ res << [@upper_left.flatten, @lower_right.flatten]
154
+ else
155
+ res << self
156
+ end
157
+ res.flatten
158
+ end
159
+
160
+ # Block内にRoom(部屋)を配置する。
161
+ # 引数にroomを渡した場合、そのroomが配置される。
162
+ # 引数にroomを渡さない場合、ランダムに生成された部屋が配置される。
163
+ def put_room(randomizer_or_room=nil)
164
+ randomizer_or_room ||= Random.new(Time.now.to_i)
165
+ case randomizer_or_room
166
+ when Room
167
+ room = randomizer_or_room
168
+ return false if !suitable?(room)
169
+ @room = room
170
+ when Random
171
+ randomizer = randomizer_or_room
172
+ min_w = @floor.min_room_width
173
+ min_h = @floor.min_room_height
174
+ max_w = [@floor.max_room_width, (@width - MARGIN * 2)].min
175
+ max_h = [@floor.max_room_height, (@height - MARGIN * 2)].min
176
+ rand_w = randomizer.rand(min_w..max_w)
177
+ rand_h = randomizer.rand(min_h..max_h)
178
+ @room = create_room(rand_w, rand_h)
179
+ else
180
+ return false
181
+ end
182
+ @room.block = self
183
+ @room.set_random_coordinate(randomizer)
184
+ true
185
+ end
186
+
187
+ def has_room?
188
+ !!@room
189
+ end
190
+
191
+ def suitable?(room)
192
+ @width - room.width >= MARGIN * 2 &&
193
+ @height - room.height >= MARGIN * 2
194
+ end
195
+
196
+ private
197
+
198
+ module RoomInitializer
199
+ def initialize
200
+ end
201
+ end
202
+
203
+ def mixin_room_module
204
+ self.class.instance_variable_get(:@mixin_room_module)
205
+ end
206
+
207
+ def create_room(width, height)
208
+ room = Room.new(width, height)
209
+ room.extend(RoomInitializer)
210
+ room.extend(mixin_room_module) if mixin_room_module
211
+ room.instance_eval { initialize }
212
+ room
213
+ end
214
+
215
+ # 横分割 (上下に新しいBlockが生成される)
216
+ def horizontal_separate
217
+ c = @height.even? ? 0 : 1
218
+ block_height = (@height - c) / 2
219
+ @upper_left = self.class.new(@floor, @x, @y,
220
+ @width, block_height, self)
221
+ @lower_right = self.class.new(@floor, @x, @y + 1 + block_height,
222
+ @width, @height - (1 + block_height), self)
223
+ @partition = Partition.new(@x, @y + block_height, @width, :horizontal)
224
+ self
225
+ end
226
+
227
+ # 縦分割 (左右に新しいBlockが生成される)
228
+ def vertical_separate
229
+ c = @width.even? ? 0 : 1
230
+ block_width = (@width - c) / 2
231
+ @upper_left = self.class.new(@floor, @x, @y,
232
+ block_width, @height, self)
233
+ @lower_right = self.class.new(@floor, @x + 1 + block_width, @y,
234
+ @width - (1 + block_width), @height, self)
235
+ @partition = Partition.new(@x + block_width, @y, @height, :vertical)
236
+ self
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,64 @@
1
+ module Meiro
2
+ FLOOR_MIN_WIDTH = 5
3
+ FLOOR_MIN_HEIGHT = FLOOR_MIN_WIDTH
4
+ ROOM_MIN_WIDTH = 3
5
+ ROOM_MIN_HEIGHT = ROOM_MIN_WIDTH
6
+
7
+ class Dungeon
8
+ class Config < Options
9
+ option :width, Integer, 60, lambda {|w,o| w >= FLOOR_MIN_WIDTH }
10
+ option :height, Integer, 40, lambda {|h,o| h >= FLOOR_MIN_HEIGHT }
11
+ option :min_room_number, Integer, 1, lambda {|n,o| n > 0 }
12
+ option :max_room_number, Integer, 6, lambda {|n,o| n >= o[:min_room_number] }
13
+ option :min_room_width, Integer, 8, lambda {|w,o| w >= ROOM_MIN_WIDTH }
14
+ option :min_room_height, Integer, 6, lambda {|h,o| h >= ROOM_MIN_HEIGHT }
15
+ option :max_room_width, Integer, 20, lambda {|w,o| w >= o[:min_room_width] }
16
+ option :max_room_height, Integer, 20, lambda {|h,o| h >= o[:min_room_height] }
17
+ option :block_split_factor, Float, 1.0, lambda {|n,o| n > 0 }
18
+ end
19
+
20
+ module FloorInitializer
21
+ def initialize
22
+ end
23
+ end
24
+
25
+ attr_accessor :width, :height,
26
+ :min_room_number, :max_room_number,
27
+ :min_room_width, :min_room_height,
28
+ :max_room_width, :max_room_height,
29
+ :block_split_factor
30
+
31
+ def initialize(options={})
32
+ config = Config.new(options)
33
+ @width = config.width
34
+ @height = config.height
35
+ @min_room_number = config.min_room_number
36
+ @max_room_number = config.max_room_number
37
+ @min_room_width = config.min_room_width
38
+ @min_room_height = config.min_room_height
39
+ @max_room_width = config.max_room_width
40
+ @max_room_height = config.max_room_height
41
+ @block_split_factor = config.block_split_factor
42
+ @randomizer = Random.new(Time.now.to_i)
43
+ end
44
+
45
+ def create_floor(mixin_floor_module=nil, mixin_room_module=nil)
46
+ args = [self, @width, @height,
47
+ @min_room_width, @min_room_height,
48
+ @max_room_width, @max_room_height]
49
+ f = Floor.new(*args)
50
+ f.extend(FloorInitializer)
51
+ f.extend(mixin_floor_module) if mixin_floor_module
52
+ Block.set_mixin_room_module(mixin_room_module) if mixin_room_module
53
+ f.instance_eval { initialize }
54
+ f
55
+ end
56
+
57
+ def generate_random_floor(mixin_floor_module=nil, mixin_room_module=nil)
58
+ floor = create_floor(mixin_floor_module, mixin_room_module)
59
+ args = [@min_room_number, @max_room_number,
60
+ @block_split_factor, @randomizer]
61
+ floor.generate_random_room(*args)
62
+ end
63
+ end
64
+ end