Rubyでローグライクダンジョン生成

Rubyで書いてみたかったのと興味のあるアルゴリズムだったので.

参考元:http://racanhack.sourceforge.jp/rhdoc/index.html

#地図情報定義
MAX_MAP_X = 50
MAX_MAP_Y = 50

#地図情報定義
MAP_KABE = 0
MAP_AKI	= 1
MAP_KUGIRI = 2
MAP_MITI = 3

PRINT_MAP_KABE = " "
PRINT_MAP_AKI = "."
#PRINT_MAP_KUGIRI = "#"	#for debugging
PRINT_MAP_KUGIRI = " "
PRINT_MAP_MITI = "@"


# 地図情報配列の初期化
arrayMap = Array.new(MAX_MAP_Y) {Array.new(MAX_MAP_X, MAP_KABE)}

# 区画情報配列の初期化
arrayRegion = Array.new( 1, Hash["start",Hash["x",0, "y",0], "end",Hash["x",MAX_MAP_X, "y",MAX_MAP_Y] ] )
arrayKugirareRegion = Array.new( 1, Hash["atama",false, "asi",false, "hidari",false, "migi",false] )

# 部屋情報配列の初期化
arrayRoom = Array.new

#関数:ダンジョンを表示する
def mapPrint( arrayMap ) 
	arrayMap.each do |i|
		i.each do |map|
			if map==MAP_KABE then
				print PRINT_MAP_KABE
			elsif map==MAP_AKI then
				print PRINT_MAP_AKI
			elsif map==MAP_KUGIRI then
				print PRINT_MAP_KUGIRI
			elsif map==MAP_MITI then
				print PRINT_MAP_MITI
			else
				print "error"
				return
			end
		end
		print "\n"
	end
	print "\n"
end

#関数:区画に区切る
def mapKugiru(arrayMap, arrayRegion, arrayKugirareRegion, dir, idxBundan, idxRegion)
	# 地図情報の更新
	# --分断列(分断行)を全てtrueに書き換える
	if dir == "tate" then
		#指定された区画がidxで分断できない場合をはじく
		if not idxBundan > arrayRegion[idxRegion]["start"]["x"] && idxBundan < arrayRegion[idxRegion]["end"]["x"]-1 then
			p "idxRegion is invalid"
			p dir
			return
		end

		#区画を見つける
		idxStart = arrayRegion[idxRegion]["start"]["y"]
		idxEnd   = arrayRegion[idxRegion]["end"]["y"]-1

		#分断--値を埋める
		arrayMap[idxStart..idxEnd].each do |i|
			i[idxBundan] = MAP_KUGIRI
		end
	elsif dir == "yoko" then
		#指定された区画がidxで分断できない場合をはじく
		if not idxBundan > arrayRegion[idxRegion]["start"]["y"] && idxBundan < arrayRegion[idxRegion]["end"]["y"]-1 then
			p "idxRegion is invalid"
			p dir
			return
		end

		#区画を見つける
		idxStart = arrayRegion[idxRegion]["start"]["x"]
		idxEnd   = arrayRegion[idxRegion]["end"]["x"]-1

		#分断--値を埋める
		arrayMap[idxBundan].fill(MAP_KUGIRI, idxStart..idxEnd)
	else	#エラー
		puts "error"
		return
	end

	# 区画情報と区画被分断情報の更新
	# --分断された区画情報を消して、分断後の区画情報を両方追加する
	if dir == "tate" then
		#2回目に間を分断したときに1回目の部屋とつなぐため
		migiTmp = false
		migiTmp = true if arrayKugirareRegion[idxRegion]["migi"] == true

		regionAppendStart = Hash["x",idxBundan+1, "y",arrayRegion[idxRegion]["start"]["y"]]
		regionAppendEnd = arrayRegion[idxRegion]["end"].clone
		arrayRegion.insert( idxRegion+1, Hash["start",regionAppendStart, "end",regionAppendEnd] )
		arrayRegion[idxRegion]["end"]["x"] = idxBundan
		#arrayRegion[idxRegion]["end"]["y"] = arrayRegion[idxRegion]["end"]["y"]	#等価なので省略

		#被分断情報の更新
		arrayKugirareRegion[idxRegion]["migi"] = true
		arrayKugirareRegion.insert( idxRegion+1, Hash["atama",false, "asi",false, "hidari",true, "migi",migiTmp] )
	elsif dir == "yoko" then
		#2回目に間を分断したときに1回目の部屋とつなぐため
		asiTmp = false
		asiTmp = true if arrayKugirareRegion[idxRegion]["asi"] == true

		regionAppendStart = Hash["x",arrayRegion[idxRegion]["start"]["x"], "y",idxBundan+1]
		regionAppendEnd = arrayRegion[idxRegion]["end"].clone
		arrayRegion.insert( idxRegion+1, Hash["start",regionAppendStart, "end",regionAppendEnd] )
		#arrayRegion[idxRegion]["end"]["x"] = arrayRegion[idxRegion]["end"]["x"]	#等価なので省略
		arrayRegion[idxRegion]["end"]["y"] = idxBundan

		#被分断情報の更新
		arrayKugirareRegion[idxRegion]["asi"] = true
		arrayKugirareRegion.insert( idxRegion+1, Hash["atama",true, "asi",asiTmp, "hidari",false, "migi",false] )
	else	#エラー
		puts "error"
		return
	end
end

#関数:各区画に部屋を作る
def placeRoom( arrayMap, arrayRegion, arrayRoom )

	arrayRegion.each do |region|

		# 区画情報を更新
		roomStart = region["start"].clone()
		roomStart["x"] += 3	#todo
		roomStart["y"] += 2	#todo
		roomEnd = region["end"].clone()
		roomEnd["x"] -= 4	#todo
		roomEnd["y"] -= 2	#todo
		arrayRoom.push Hash["start",roomStart, "end",roomEnd]

		# マップを更新
		# --壁をくりぬく
		arrayMap[roomStart["y"]..roomEnd["y"]].each do |yokoMap|
			yokoMap.fill(MAP_AKI, roomStart["x"]..roomEnd["x"])
		end
	end
end

#関数:通路を作る
def placePath( arrayMap, arrayRoom, arrayKugirareRegion )

	# 部屋から区画線までの通路を作る
	arrayPath = Array.new
	idxRoom = 0

	arrayRoom.each do |room|
		if arrayKugirareRegion[idxRoom]["atama"] then
			entranceX = ( room["start"]["x"] + room["end"]["x"] ) / 2
			entranceY = room["start"]["y"]

			while arrayMap[entranceY][entranceX] != MAP_KUGIRI
				arrayMap[entranceY][entranceX] = MAP_MITI

				entranceY -= 1
				if entranceY <= 0 then break end
			end

			# 区画線までの通路同士をつなげる
			# --最近の"asi"がtrueの部屋とつなげる
			idxRoomConnect = idxRoom - 1
			while !arrayKugirareRegion[idxRoomConnect]["asi"]
				idxRoomConnect -= 1
				if idxRoomConnect < 0 then	
					p "error"
					break
				end
			end

			roomAnother = arrayRoom[idxRoomConnect]
			anotherEntranceX = ( roomAnother["start"]["x"] + roomAnother["end"]["x"] ) / 2

			if entranceX < anotherEntranceX
				arrayMap[entranceY].fill( MAP_MITI, entranceX..anotherEntranceX )
			else
				arrayMap[entranceY].fill( MAP_MITI, anotherEntranceX..entranceX )
			end
		end
		if arrayKugirareRegion[idxRoom]["asi"] then
			entranceX = ( room["start"]["x"] + room["end"]["x"] ) / 2
			entranceY = room["end"]["y"]

			while arrayMap[entranceY][entranceX] != MAP_KUGIRI
				arrayMap[entranceY][entranceX] = MAP_MITI

				entranceY += 1
				if entranceY >= MAX_MAP_Y then break end
			end
		end
		if arrayKugirareRegion[idxRoom]["hidari"] then
			entranceX = room["start"]["x"]
			entranceY = ( room["start"]["y"] + room["end"]["y"] ) / 2

			pathStart = Hash["x",entranceX, "y",entranceY]

			while arrayMap[entranceY][entranceX] != MAP_KUGIRI
				arrayMap[entranceY][entranceX] = MAP_MITI

				entranceX -= 1
				if entranceX <= 0 then break end
			end

			# 区画線までの通路同士をつなげる
			# --最近の"migi"がtrueの部屋とつなげる
			idxRoomConnect = idxRoom - 1
			while !arrayKugirareRegion[idxRoomConnect]["migi"]
				idxRoomConnect -= 1
				if idxRoomConnect < 0 then	#エラー対応
					p "error"
					break
				end
			end

			roomAnother = arrayRoom[idxRoomConnect]
			anotherEntranceY = ( roomAnother["start"]["y"] + roomAnother["end"]["y"] ) / 2

			if entranceY < anotherEntranceY
				arrayMap[entranceY..anotherEntranceY].each do |array|
					array[entranceX] = MAP_MITI
				end
			else
				arrayMap[anotherEntranceY..entranceY].each do |array|
					array[entranceX] = MAP_MITI
				end
			end
		end
		if arrayKugirareRegion[idxRoom]["migi"] then
			entranceX = room["end"]["x"]
			entranceY = ( room["start"]["y"] + room["end"]["y"] ) / 2

			while arrayMap[entranceY][entranceX] != MAP_KUGIRI
				arrayMap[entranceY][entranceX] = MAP_MITI
				
				entranceX += 1
				if entranceX >= MAX_MAP_X then break end
			end
		end

		idxRoom += 1
	end
end

#(1)マップを区画に分ける
#ケース1
mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "tate", 20, 0 )
mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "yoko", 15, 1 )
mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "tate", 40, 1 )
mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "yoko", 30, 3 )

##ケース2
#mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "yoko", 15, 0 )
#mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "tate", 40, 0 )
#mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "yoko", 30, 2 )
#mapKugiru( arrayMap, arrayRegion, arrayKugirareRegion, "tate", 20, 0 )

#mapPrint( arrayMap )    #for debugging

#(2)区画ごとに部屋を作る
placeRoom( arrayMap, arrayRegion, arrayRoom )
#mapPrint( arrayMap )    #for debugging

#(3)通路を作る
placePath( arrayMap, arrayRoom, arrayKugirareRegion )

#(4)出来上がり
mapPrint( arrayMap )

http://generation1986.g.hatena.ne.jp/shom5w/20091123