Tuesday, January 18

More Fun With Dungeon Generation

Much improved. Doesn't guarantee interconnection, though,


############ #### ## ### ##################
############ #### # ## ##################
#### ##############
### ## # #### # ##############
### ######## # ####### # ##############
######## ## ###### ##############
############# # #### ### ##############
########### # # #### ## ##############
# ########### # ### ################ ##############
# # ################ ##############
# ######### ##### ######################################
######### ## ######################################
# ######### ## # ########################################
# ########### ## ## ###################################
# # ########### ### ###################################
########## ######## ###################################
#### ###################### ###################################
##### ###################### ###################################
##### ###################### ###################################
##### ###################### ###################################
###################### ###################################
# ########################## ###################################
# ##################### ###################################
# ##################### #####################################
# ######################## #####################################
# ##### ############# #####################################
# ##### #### ###################################
# ##### #### # ######## ###################################
# ###################################
#### ##########################################
######## # ######################
# #### #### ######### ################# ######################
#### ############################# ######################
# ###### ################### ######################
# ################ ## ################### ######################
################ ## ################### ######################
################# ## ################### ######################
############## ## ################### ######################
### ############## ################### ####################
### ################ ##################### ####################
### ################ ##################### ####################
### ################ ##################### ####################
### ################ #### ############### ####################
### ################ #### ############### ####################
### ################ ## ############### ####################
### ################# ## ######### ####################
### ################# ##### ######### ##########################
### ################# ##### ##########################
### ###################### ### ### ##########################
### ###################### ### ### ##########################
# ###################### ### ##########################
######################## ### ####################
##################### ### ############ ####### ###########
## ##################### ### ############ ###########
# # ##################### ### ############ ## ## ##########
## # ##################### ############ ## ## ### ##########
## ##################### ############### ## ## ### ##########
################ ########### ## ## ### ##########
######### ### ####### ########### ## # ##########
######### ### ####### ########### ## ## ##########
######### # ####### ########### ## ## ########
######### ########## # ########## ## ## ########
######### ## # ########## ###### ## ########
######### ## ############# #################################






import Numeric
import whrandom
import math

class Burrower:
def __init__(self, x, y, lifetime = 0, dx = 0, dy = 0):
self.data = [ x, y, lifetime, dx, dy ]


def burrower_is_dead(self):
return self.data[2] == -1

def kill_burrower(self):
self.data[2] = -1

def tick_burrower(self,b):
self.data[2] = self.data[2] + 1

def pick_random_motion(self, allow_diagonals):
motion = whrandom.randrange(0,3)
if motion == 0:
dx = - 1
dy = self.data[4]
if not allow_diagonals:
dy = 0
elif motion == 1:
dx = 1
if not allow_diagonals:
dy = 0
elif motion == 2:
dy = -1
if not allow_diagonals:
dx = 0
elif motion == 3:
dy = 1
if not allow_diagonals:
dx = 0
# don't double back on yourself
if dx == -self.data[3] and dy == self.data[4]:
dy = dx
dx = self.data[4]
if dy == -self.data[4] and dx == self.data[3]:
dx = dy
dy = self.data[3]
self.data[3] = dx
self.data[4] = dy

def clip_burrower(self, minx, miny, maxx, maxy):
if self.data[0] < minx:
self.data[0] = minx
if self.data[0] > maxx:
self.data[0] = maxx
if self.data[1] < miny:
self.data[1] = miny
if self.data[1] > maxy:
self.data[1] = miny

def un_move_burrower(self, minx, miny, maxx, maxy, allow_diagonals):
self.data[0] = self.data[0] - self.data[3]
self.data[1] = self.data[1] - self.data[4]
self.clip_burrower(minx, miny, maxx, maxy)
self.pick_random_motion(allow_diagonals)

# move the burrower one cell, carving as you go along
# to do rather than randomly pick a direction, randomly *change* a direction (more control of twistiness, etc)
def move_burrower(self, minx, miny, maxx, maxy, changeprob, allow_diagonals):
if whrandom.random() < changeprob:
self.pick_random_motion(allow_diagonals)
# am i dead ? if so I should be concenrating on a nice firm rigor mortis
if self.burrower_is_dead():
return
self.data[0] = self.data[0] + self.data[3]
self.data[1] = self.data[1] + self.data[4]
if self.data[0] < minx:
self.data[0] = 0
self.data[3] = 1
if self.data[1] < miny:
self.data[1] = 0
self.data[4] = 1
if self.data[0] > maxx:
self.data[0] = maxx
self.data[3] = -1
if self.data[1] > maxy:
self.data[1] = maxy
self.data[4] = -1
return

class Dungeon:
TileIdNumber = { "granite" : 0 , "room" : 1, "corridor" : 2 }

def __init__(self, width, depth):
self.tiles = Numeric.zeros((width, depth))
self.width = width
self.depth = depth
self.room_centres = [ ]

def room_too_close(self, x, y, min_room_distance):
for r in self.room_centres:
dx = r[0] - x
dy = r[1] - y
dist = math.sqrt(x * x + y * y)
if (dist < min_room_distance):
return True
return False

def burrower_room_create(self, b, broom_prob, max_room_width, max_room_depth, min_room_distance, burrower_index):
# we have a room
if whrandom.random() > broom_prob:
return 0
room_width = whrandom.randrange(max_room_width / 2) + 1
room_depth = whrandom.randrange(max_room_depth / 2) + 1
if self.room_too_close(b.data[0], b.data[1], min_room_distance):
return 0
self.room_centres.append((b.data[0], b.data[1]))
for x in range (b.data[0] - room_width, b.data[0] + room_width):
for y in range (b.data[1] - room_depth, b.data[1] + room_depth):
if (x >= 0) and (x < self.width) and (y >= 0) and (y < self.depth):
self.tiles[x][y] = burrower_index
return 1

def burrower_birth(self,bbirth_prob):
return whrandom.random() < bbirth_prob

def baby_burrower(self, b):
return Burrower(b.data[0] + whrandom.randrange(2), b.data[1] + whrandom.randrange(2), 0, 0, 0)

def print_dungeon(self):
for y in range(0,self.depth):
s = ""
for x in range(0,self.width):
if self.tiles[x][y] == Dungeon.TileIdNumber["granite"]:
s = s + "#"
else:
s = s + " "
print s
#
# burrowers start on one side of the level, on each turn they can either split (birth prob),
# create a room (die), have a max lifetime, and the algo terminates when room_count is reached
#
def generate(self, burrower_total, room_total, birth_prob, turn_prob, room_prob, lifetime, max_room_width, max_room_depth, min_room_distance, allow_diagonals):
# set up burrowers
burrowers = []
for i in range(0,burrower_total):
side = whrandom.randrange(0,4)
if side == 0:
burrower = Burrower( 0, whrandom.randrange(0, self.depth) , 0, 1, 0 )
elif side == 1:
burrower = Burrower( self.width-1, whrandom.randrange(0, self.depth) , -1, 0, 0 )
elif side == 2:
burrower = Burrower( whrandom.randrange(0,self.width), 0, 0, 0, 1 )
else:
burrower = Burrower( whrandom.randrange(0,self.width), self.depth-1 , 0, 0, -1 )
burrowers.append(burrower)
room_count = 0
burrower_alive = True
i = 0
while burrower_alive:
print "Generation " , i
print "Rooms # ", room_count
print "Burrowers ", len(burrowers)
self.print_dungeon()
print
burrower_alive = False
birth_list = []
burrower_index = 1
for b in burrowers:
if not b.burrower_is_dead():
burrower_alive = True
b.move_burrower(0, 0, self.width-1, self.depth-1, turn_prob, allow_diagonals)
# if the burrower reaches another corridor, kill it
burrower_tile = self.tiles[b.data[0]][b.data[1]]
if (burrower_tile != Dungeon.TileIdNumber["granite"]) and (burrower_tile != burrower_index):
b.un_move_burrower(0,0, self.width-1, self.depth-1, allow_diagonals)
b.data[2] = b.data[2] + 1
if b.data[2] > lifetime:
b.kill_burrower()
self.tiles[b.data[0]][b.data[1]] = burrower_index
if self.burrower_birth(birth_prob):
birth_list.append(self.baby_burrower(b))
room_count = room_count + self.burrower_room_create(b, room_prob, max_room_width, max_room_depth, min_room_distance, burrower_index)
burrower_index = burrower_index + 1
if birth_list:
burrowers.extend(birth_list)
if room_count >= room_total:
birth_prob = 0.0
room_prob = 0.0
i = i + 1
return

if __name__=="__main__":
dungeon = Dungeon(64,64)
dungeon.generate(16, # burrower total
12, # room total
0.009, # birth probability
0.25, # turn probability
0.03, # room probability
2, # lifetime (max non self - hits allowwd)
9, # max_room_depth
9, # max room width
6, # min room distance
False)


No comments: