meiro 0.0.1

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