Python Curses Example of Dungeon-Building Algorithm
Jump to navigation
Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
import curses
import random
# Please note that curses uses a y,x coordination system.
# In the code you see here, I'll be using x,y
# If any of the code seems inconsistent, it's because I took
# some of the things from my current project out and replaced
# for the purposes of this article
# Also, some of this was poorly converted. If you want to clean
# it up, go for it. Otherwise, I might get around to it eventually ;)
# I have not included placing up/down stairs or any of that business
# TODO: COMMENT THINGS BETTER!
# This is just the basic dungeon tile. It holds a shape.
class dungeon_tile:
_shape = ''
def __init__(self, shape):
self._shape = shape
def get_shape(self):
return self._shape
def set_shape(self, shape):
self._shape = shape
# Our randomly generated dungeon
class dungeon:
_map_size = (0, 0)
_tiles = []
# max_features - how many rooms/corridors will be generated at the most
# room_chance - the % chance that the generated feature will be a room
def __init__(self, map_size_x, map_size_y, max_features, room_chance):
# seed the psuedo-random number generator
random.seed()
# set the map size
self._map_size = (map_size_x, map_size_y)
# fill the map with blank tiles
for x in range (0, self._map_size[0]):
self._tiles.append([])
for y in range (0, self._map_size[1]):
self._tiles[x].append(dungeon_tile(''))
current_features = 1
self._make_room(self._map_size[0]/2, self._map_size[1]/2, 5, 5, random.randint(0, 3))
for i in range (0, 1000):
if current_features == max_features: break
newx = 0
xmod = 0
newy = 0
ymod = 0
direction = -1
for j in range (0, 1000):
newx = random.randint(1, self._map_size[0]-2)
newy = random.randint(1, self._map_size[1]-2)
direction = -1
shape = self._tiles[newx][newy].get_shape()
if shape == '#' or shape == 'c':
if self._tiles[newx][newy+1].get_shape() == '.' or self._tiles[newx][newy+1].get_shape() == 'c':
xmod = 0
ymod = -1
direction = 0
elif self._tiles[newx-1][newy].get_shape() == '.' or self._tiles[newx-1][newy].get_shape() == 'c':
xmod = 1
ymod = 0
direction = 1
elif self._tiles[newx][newy-1].get_shape() == '.' or self._tiles[newx][newy-1].get_shape() == 'c':
xmod = 0
ymod = 1
direction = 2
elif self._tiles[newx+1][newy].get_shape() == '.' or self._tiles[newx+1][newy].get_shape() == 'c':
xmod = -1
ymod = 0
direction = 3
if direction > -1: break
if direction > -1:
feature = random.randint(0, 100)
if feature <= room_chance:
if self._make_room(newx+xmod, newy+ymod, 10, 10, direction):
current_features += 1
self._tiles[newx][newy].set_shape('+')
self._tiles[newx+xmod][newy+ymod].set_shape('.')
elif feature > room_chance:
if self._make_corridor(newx+xmod, newy+ymod, 6, direction):
current_features += 1
self._tiles[newx][newy].set_shape('+')
# this shows the map. we're not doing a scrolling map for this article, so it's easier
def draw(self, stdscr):
for x in range (0, self._map_size[0]):
for y in range (0, self._map_size[1]):
stdscr.addstr(y, x, self._tiles[x][y].get_shape())
stdscr.refresh()
# this makes a corridor at x,y in a direction
def _make_corridor(self, x, y, length, direction):
len = random.randint(2, length)
dir = 0
if direction > 0 and direction < 4: dir = direction
if dir == 0:
if x < 0 or x > self._map_size[0]-1: return False
for i in range (y, y-len, -1):
if i < 0 or i > self._map_size[1]-1: return False
if self._tiles[x][i].get_shape() != '':return False
for i in range (y, y-len, -1):
self._tiles[x][i].set_shape('c')
elif dir == 1:
if y < 0 or y > self._map_size[1]-1: return False
for i in range (x, x+len):
if i < 0 or i > self._map_size[1]-1: return False
if self._tiles[i][y].get_shape() != '': return False
for i in range (x, x+len):
self._tiles[i][y].set_shape('c')
elif dir == 2:
if x < 0 or x > self._map_size[0]-1: return False
for i in range (y, y+len):
if i < 0 or i > self._map_size[1]-1: return False
if self._tiles[x][i].get_shape() != '': return False
for i in range (y, y+len):
self._tiles[x][i].set_shape('c')
elif dir == 3:
if y < 0 or y > self._map_size[1]-1: return False
for i in range (x, x-len, -1):
if i < 0 or i > self._map_size[1]-1: return False
if self._tiles[i][y].get_shape() != '': return False
for i in range (x, x-len, -1):
self._tiles[i][y].set_shape('c')
return True
# this makes a room at x,y with the width,height and in a direction
def _make_room(self, x, y, width, height, direction):
rand_width = random.randint(4, width)
rand_height = random.randint(4, height)
dir = 0
if direction > 0 and direction < 4: dir = direction
if dir == 0:
for i in range (y, y-rand_height, -1):
if i < 0 or i > self._map_size[1]-1: return False
for j in range (x-rand_width/2, (x+(rand_width+1)/2)):
if j < 0 or j > self._map_size[0]-1: return False
if self._tiles[j][i].get_shape() != '': return False
for i in range (y, y-rand_height, -1):
for j in range (x-rand_width/2, (x+(rand_width+1)/2)):
if j == x-rand_width/2: self._tiles[j][i].set_shape('#')
elif j == x+(rand_width-1)/2: self._tiles[j][i].set_shape('#')
elif i == y: self._tiles[j][i].set_shape('#')
elif i == y-rand_height+1: self._tiles[j][i].set_shape('#')
else: self._tiles[j][i].set_shape('.')
elif dir == 1:
for i in range (y-rand_height/2, y+(rand_height+1)/2):
if i < 0 or i > self._map_size[1]-1: return False
for j in range (x, x+rand_width):
if j < 0 or j > self._map_size[0]-1: return False
if self._tiles[j][i].get_shape() != '': return False
for i in range (y-rand_height/2, y+(rand_height+1)/2):
for j in range (x, x+rand_width):
if j == x: self._tiles[j][i].set_shape('#')
elif j == x+(rand_width-1): self._tiles[j][i].set_shape('#')
elif i == y-rand_height/2: self._tiles[j][i].set_shape('#')
elif i == y+(rand_height-1)/2: self._tiles[j][i].set_shape('#')
else: self._tiles[j][i].set_shape('.')
elif dir == 2:
for i in range (y, y+rand_height):
if i < 0 or i > self._map_size[1]-1: return False
for j in range (x-rand_width/2, x+(rand_width+1)/2):
if j < 0 or j > self._map_size[0]-1: return False
if self._tiles[j][i].get_shape() != '': return False
for i in range (y, y+rand_height):
for j in range (x-rand_width/2, x+(rand_width+1)/2):
if j == x-rand_width/2: self._tiles[j][i].set_shape('#')
elif j == x+(rand_width-1)/2: self._tiles[j][i].set_shape('#')
elif i == y: self._tiles[j][i].set_shape('#')
elif i == y+(rand_height-1): self._tiles[j][i].set_shape('#')
else: self._tiles[j][i].set_shape('.')
elif dir == 3:
for i in range (y-rand_height/2, y+(rand_height+1)/2):
if i < 0 or i > self._map_size[1]-1: return False
for j in range (x, x-rand_width, -1):
if j < 0 or j > self._map_size[0]-1: return False
if self._tiles[j][i].get_shape() != '': return False
for i in range (y-rand_height/2, y+(rand_height+1)/2):
for j in range (x, x-rand_width-1, -1):
if j == x: self._tiles[j][i].set_shape('#')
elif j == x-rand_width: self._tiles[j][i].set_shape('#')
elif i == y-rand_height/2: self._tiles[j][i].set_shape('#')
elif i == y+(rand_height-1)/2: self._tiles[j][i].set_shape('#')
else: self._tiles[j][i].set_shape('.')
return True
def main(stdscr):
screen_y, screen_x = stdscr.getmaxyx()
this_dungeon = dungeon(screen_x, screen_y, 5, 50)
this_dungeon.draw(stdscr)
stdscr.getch()
if __name__ == '__main__':
curses.wrapper(main)