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 )