Skip to content

Instantly share code, notes, and snippets.

@laserbat
Created February 2, 2012 08:52
Show Gist options
  • Save laserbat/1722426 to your computer and use it in GitHub Desktop.
Save laserbat/1722426 to your computer and use it in GitHub Desktop.
def createCaveLevel(self, w, h):
"""
Simple CA cave generator, sometimes generates not-so-cavelike
levels :(
Uses algorithm from:
roguebasin.roguelikedevelopment.org/index.php?title=
Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels
"""
cave = [] # Level
cave2 = [] # Used for CA
for x in range(0, w + 1): # Create empty level
cave.append([])
cave2.append([WALL] * (h + 1)) # Fill cave2 with walls
for y in range(0, h + 1):
# And cave with random cells
if libtcod.random_get_int(self.rand, 0, CA_FILL_MAXRAND) \
<= CA_FILL_PROB:
cave[x].append(WALL)
else:
cave[x].append(FLOOR)
for iterations in range(len(CA_ITERATIONS)): # The CA itself
cutoff = CA_CUTOFF[iterations]
for m in range(CA_ITERATIONS[iterations]):
for x in range(2, w - 1):
for y in range(2, h - 1):
c = c2 = 0
for x2 in range(-1, 2): # All adjacent cells
for y2 in range(-1, 2):
if cave[x + x2][y + y2] != FLOOR:
c += 1
for x2 in range(x - 2, x + 3): # And cells that only
for y2 in range(y - 2, y + 3): # two tiles far.
if abs(x2 - x) == 2 and abs(y - y2) == 2:
continue
if x2 < 0 or y2 < 0 or x2 >= w or y2 >= h:
continue
if cave[x2][y2] != FLOOR:
c2 += 1
if c >= cutoff[0] or c2 <= cutoff[1]:
cave2[x][y] = WALL
else:
cave2[x][y] = FLOOR
for x in range(0, w + 1):
for y in range(0, h + 1):
cave[x][y] = cave2[x][y]
x = y = 0 # Find a seed for floodfill
i = 0
while not cave[x][y]:
x = libtcod.random_get_int(self.rand, 0, w)
y = libtcod.random_get_int(self.rand, 0, h)
i += 1
if i > MAX_CELLS:
raise NoFloorCellException
cave = self.rFloodFill(cave) # Floodfill it
if cave == False:
return self.createCaveLevel(w, h)
# Create cells in self.matrix
for x in range(0, w + 1):
self.matrix.append([])
for y in range(0, h + 1):
if self.levelRange((x, y)):
self.matrix[x].append(Cell( # Create cell
cave[x][y],
cave[x][y],
(x, y),
self.noise, self
)
)
self.update((x,y))
else: # Create permanent wall
self.matrix[x].append(PermaWall((x, y), self.noise, self))
self.update((x,y))
x = y = 0 # Create a test monster
i = 0
while not self[x][y].isWalkable():
x = libtcod.random_get_int(self.rand, 0, w)
y = libtcod.random_get_int(self.rand, 0, h)
i += 1
if i > MAX_CELLS:
raise NoFloorCellException
# Test monster
self[x][y].mob = Mob((x,y),(self.noise,self.noise1d),self)
# And find free cell for player
self.px = self.py = 0
i = 0
while not self[self.px][self.py].isWalkable():
self.px = libtcod.random_get_int(self.rand, 0, w)
self.py = libtcod.random_get_int(self.rand, 0, h)
i += 1
if i > MAX_CELLS:
raise NoFloorCellException
# This code is useful for debugging level generator
"""
for y in range(0, h + 1):
for x in range(0, w + 1):
if self[x][y].isTransparent():
sys.stdout.write(".")
else:
sys.stdout.write("#")
print
"""
def rFloodFill(self,fmap):
""" Split level into regions
uses floodfill function """
sys.setrecursionlimit(REC_LIMIT) # It sometimes hits default
# recursion limit
# Mark every floor tile in every disconnected area
i = 2
for x in range(len(fmap)):
for y in range(len(fmap[0])):
if fmap[x][y] == FLOOR: # FLOOR = 1
# So, it wouldn't floodfill already marked areas
fmap = self.floodFill((x,y), (1,i), fmap) # Save result
# of floodfill
i += 1 # And increase number of disconected areas
areas = [0] * (i - 2) # Set every area size to 0
for x in range(len(fmap)):
for y in range(len(fmap[0])):
if fmap[x][y]:
areas[fmap[x][y] - 2] += 1 # And add one to it's size
# for every cell marked with this number.
l = 0 # Largest area
for u in range(len(areas)):
if areas[u] > areas[l]:
l = u
# print l, areas
fmap2 = [] # New map
if areas[l] <= (len(fmap) * len(fmap[0])) / 3:
return False
l += 2
for x in range(len(fmap)):
fmap2.append([])
for y in range(len(fmap[0])):
if fmap[x][y] == l:
fmap2[x].append(FLOOR)
else:
fmap2[x].append(WALL)
"""
for x in range(len(fmap2[0])):
for y in range(len(fmap2)):
sys.stdout.write(str(fmap[y][x]))
print
print
"""
sys.setrecursionlimit(REC_LIMIT_DEFAULT)
return fmap2
def floodFill(self, coords, colors, fmap):
"""
Recursive floodfill function.
"""
(x, y) = coords # Seed
(old, new) = colors
if fmap[x][y] != old:
return
fmap[x][y] = new
for x2 in range(-1,2): # Recursive calls
for y2 in range(-1,2):
self.floodFill((x + x2, y + y2), colors, fmap)
return fmap # Return result of floodfill
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment