1

I'm using Python 2.7 libtcod to make a Roguelike, and currently trying to get a function to create an individual room based on a given floor number, x1, x2, y1, and y2 set of coordinates. The expected behavior is that I should get a room bounded with walls and otherwise filled with emptiness. For instance, makeRoom(0, 1, 1, 10, 10) should give:

----------
|********|
|********|
|********|
|********|
|********|
|********|
|********|
|********|
----------

Instead, however, I get a room that's as wide as the dungeon floor, respecting only the height, and the walls on the side never arrive. So instead, I get an arrangement more like this:

---------------------------
***************************
***************************
***************************
***************************
***************************
***************************
***************************
***************************
---------------------------

Changing the total available width of the dungeon floor causes the room to shrink or grow horizontally regardless of what size I ask it to have. The side walls never appear no matter how I tweak the ranges, where I tweak them, or what I comment or uncomment. In short, only the height and top/bottom is ever obeyed, no matter what I attempt.

base.py (main game loop):

import sys
import libtcodpy as libtcod
import entity
import dungeon

SCREEN_WIDTH = 80
SCREEN_HEIGHT = 50
LIMIT_FPS = 20

libtcod.console_set_custom_font('../dejavu10x10_gs_tc.png', libtcod.FONT_TYPE_GRAYSCALE | libtcod.FONT_LAYOUT_TCOD)
libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'MadNet Zero', False)
libtcod.console_set_foreground_color(0, libtcod.white)

# The buggy functions
saturnSeven = dungeon.Dungeon(5)
saturnSeven.makeRoom(0, 1, 1, 10, 10)

player = entity.Player(25,25,3,4,0)
libtcod.console_print_left(0, player.X, player.Y, libtcod.BKGND_NONE, '@')
libtcod.console_flush()

while not libtcod.console_is_window_closed():
    libtcod.console_set_foreground_color(0, libtcod.white)
    #clear the player's old position
    libtcod.console_print_left(0, player.X, player.Y, libtcod.BKGND_NONE, ' ')

    #print terrain
    for y in range(len(saturnSeven.floor[player.Z][0])):
        for x in range(len(saturnSeven.floor[player.Z])):
            symbol = saturnSeven.getTerrainSymbol(x,y,player.Z)
            libtcod.console_print_left(0, x, y, libtcod.BKGND_NONE, symbol)
    libtcod.console_print_left(0, player.X, player.Y, libtcod.BKGND_NONE, player.char)
    libtcod.console_flush()

dungeon.py (does the meat of the work):

# Define terrain types as a pseudo-enum
wall, empty, solid = range(3)
class Cell(object):
    def __init__(self, terrain, items):
        self.terrain = terrain
        self.items = items
    def isPassable(self):
        if self.terrain != wall:
            return True
        else:
            return False

class Dungeon(object):
    def __init__(self, numfloors):
        self.MAXFLOORS = numfloors
        self.floor = [None]*numfloors
        for i in range(numfloors):
            self.floor[i] = makeFloor(20,20)

    def makeRoom(self, floorNum, Xmin, Ymin, Xmax, Ymax):
        #inner area
        for x in range(Xmin+1, Xmax):
            for y in range(Ymin+1, Ymax):
                self.floor[floorNum][x][y] = Cell(empty, None)
        #top/bottom wall
        for x in range(Xmin,Xmax+1):
            self.floor[floorNum][x][Ymin] = Cell(wall, None)
            self.floor[floorNum][x][Ymax] = Cell(wall, None)
        #left/right wall
        for y in range(Ymin, Ymax+1):
            self.floor[floorNum][Xmin][y] = Cell(wall, None)
            self.floor[floorNum][Xmax][y] = Cell(wall, None)

    def getTerrainSymbol(self, X, Y, Z):
        if self.floor[Z][X][Y] == None:
            return ' '
        if self.floor[Z][X][Y].terrain == wall:
            return '#'
        if self.floor[Z][X][Y].terrain == solid:
            return ' '
        if self.floor[Z][X][Y].terrain == empty:
            return '.'


def makeFloor(Xmax, Ymax):
    return [[Cell(solid, None)]*Xmax]*Ymax

I've eliminated the graphics as being a problem; the dungeon itself is definitely what's wrong, not how it's being displayed on screen. I can't find any reason the algorithm should be wrong in this way, though.

EDIT: The original version of the code that gave the diagram above had the "inner area" portion underneath the part that made the walls; putting it on top, as I did, results in the entire room being made up of walls, but still too wide. So far as I can tell they should give equivalent results; they do not.

tacoman
  • 93
  • 4
  • Did you try to look at self.floor before and after each of the blocks that create the room? – Nobody moving away from SE Feb 03 '12 at 17:08
  • I had the loop give me the coordinates where it should have been writing individual wall segments, and I've commented/uncommented each part of the function in turn to see the different results. Even if I narrow it down to one row, that row still winds up being as wide as it physically can, rather than as wide as it's set to be. – tacoman Feb 03 '12 at 17:11

1 Answers1

3

Your problem (or at least one of them) is the definition of makeFloor. Try this instead:

def makeFloor(Xmax, Ymax):
    return [[Cell(solid,None) for i in range(Xmax)]
            for j in range(Ymax)]

Explanation: If you do [x]*10, then you get a list containing 10 elements, each of which is x -- the same x. If you change one, then the others will change as well. Similarly, if you do [[x]]*10, then you get a list containing 10 sublists, each of which is the same sublist -- again, changing one will change them all.

Edward Loper
  • 15,374
  • 7
  • 43
  • 52