Jump to content


Photo

Creating rooms given only relative positions


  • Please log in to reply
4 replies to this topic

#1 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • Retired Staff
  • 9260 posts
  • Version:Unknown

Posted 25 April 2012 - 06:31 PM

I'm practicing for the Jam (in case I actually have time to create something for it!) by tackling arbitrary programming challenges. The one I'm working on now is a type of procedural generation that creates a number of rectangular rooms in a building (all assumed to be the same size, 32x32, for now) and generates the building's layout. It does this by connecting rooms together at their edges, and also adding new floors at random (and connecting the new floors to old ones with stairwells).

The problem I'm having is that the generated buildings aren't correct when I actually place the room instances. They lack stairwells (which should be created whenever a new floor is added) and more rooms are generated than I've actually specified (I run the room addition code 20 times, but get arbitrarily more rooms in the final result).

You'll need a bit of information about the structure of the layout, I suppose.

  • Firstly, each floor is a different value in a map, where the key is the floor number. I figured this would make it more efficient since I'd only need one floor at a time, and finding a floor in a map is O(log n).
  • The floors themselves are stored as ds_lists, with each entry in the list representing either a room or a stairwell.
  • Rooms and stairwells are ds_maps. They have a key called "type" that identifies whether it's a room or a stairwell.
  • Rooms have a key called "floor" that indicates which floor the room is on, and a key called "connections" that is a 4-element ds_list specifying the other rooms and stairwells it's connected to. More details about this below. They also have a list of "invalid" rooms, that is, rooms which it can physically not connect to at any time in the future (though this is irrelevant to the problem at hand).
  • Stairwells also have "floor" and "connections" keys. The "floor" is the lower floor it's connected to. The "connections" is a ds_list with only two elements, which are the room it's connected to on the lower floor and the room it's connected to on the higher floor, respectively.

A little more detail about the "connections" list of a room: a room can only have at most 1 room or stairwell connected to each side, meaning 4 connections maximum. The connections list holds the ds_map ID of the connected rooms to the North, East, South, and West (up, right, down, left) respectively. Where there is no connected room, a -1 is used instead of an ID.

Whew. Now that all that information has been explained, here's the problem: given all that structure, how would I go about drawing the layout of the building? For simplicity, let's just assume I'm only drawing one floor at a time. How can I figure out the relative positions of the rooms given their connections?

I first tried a basic depth-first search, placing a room object whenever a room was popped off the stack. While that showed some nice room layouts, it also showed some problems. Firstly, even though I used repeat(20) to add exactly 20 rooms, I'm somehow showing more rooms than that being generated (in arbitrary numbers; one time I had over 50, another time I had 38). Secondly, stairs don't exist. I know the stairwell creation code is being executed, yet when the room objects are created, they all have type ROOM and none have type STAIRS (even though there are several floors to the generated buildings).

I don't know if the problem is in the way I'm doing my DFS or if it's actually in the underlying data structures, so any help would be nice.

Here's all the code I'm using:

InitRooms() - Initializes variables used for the building generation
Spoiler


AddStairs(lowFloor, highFloor, room) - Adds a stairwell to connect the two floors, always connected to the given room. Returns "false" if there is no place for a stairwell between these floors.
Spoiler


Connect(room, dir) - Attempts to connect the new room "room" to the existing building in direction "dir". Dir indicates which wall to connect; 0=north, 1=east, 2=south, 3=west. Returns false if no rooms on the same floor have a place to connect to it from that side.
Spoiler


Concat(list1, list2) - Concatenates list2's unique values into list1, basically converting list1 into the union of the two lists. This is used as a helper function to build up the "invalid room connections" lists of each new room from the connections of the rooms it's connected to; this prevents physically impossible room layouts
Spoiler


And, the piece de resistance:

AddRoom() - Attempts to add a new room to the existing building. Returns false if that's not possible (i.e. all walls of all existing rooms are occupied; should never happen)
Spoiler


And lastly, I'm displaying the final layouts with this DFS script:

MakeFloor(floor) - Reads the data for the specified floor and places roomObj instances where the rooms are. Also sets the roomObj instances' "mytype" variable to the node's type, either TY_ROOM or TY_STAIRS.
Spoiler


Wow. That's quite a bit of code. Mind you, I'm not getting any explicit errors, it's just an algorithmic logic problem somewhere. I know it's a lot to look through, but...can anyone help? Please?

-IMP
  • 0

#2 torigara

torigara

    GMC Member

  • GMC Member
  • 6483 posts

Posted 26 April 2012 - 05:17 AM

At the top of script AddStairs:

stairs=ds_map_create();
connections=ds_list_create();
ds_map_add(rm, "type", TY_STAIRS);
ds_map_add(rm, "floor", lower);
ds_map_add(rm, "connections", connections);

Aren't the first argument of those ds_map_add supposed to be "stairs" rather than "rm"? Otherwise, stairs.type will remain undefined (that will let MakeFloor treat it as a room in case the value of TY_ROOM is 0.)

Edited by torigara, 26 April 2012 - 05:20 AM.

  • 0

#3 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • Retired Staff
  • 9260 posts
  • Version:Unknown

Posted 26 April 2012 - 05:36 AM

*Facepalm* Of course. All the time I spent developing that algorithm, and what gets me is a copy/paste issue :lol: .

Well, now I've got the stairs thing sorted out (all I changed were those rm's to stairs's). The only problem is that it's now making fewer than 20 rooms each time. Arbitrarily. That's not counting the stairs; if I count the stairs, it ends up making more than 20. I've checked with a simple show_message(), and it's never returning false, either.

What could possibly cause it to stop short of 20 rooms without returning false at any point?

Note: if this is also caused by a copy/paste error, I'm never pressing CTRL+V again. But don't hold me to that :P .

-IMP
  • 0

#4 torigara

torigara

    GMC Member

  • GMC Member
  • 6483 posts

Posted 26 April 2012 - 06:51 AM

Well, I think there are two possibilities. First, there can be isolated rooms those have no connection.

for (p=0; p<4; p+=1) {
    if (irandom(100)<25) {
      Connect(rm, p);
    }
  }

There is (3/4)^4 = 31% chance that Connect isn't called. (Of course, as Connect doesn't always succeed, there is higher chance that the room get isolated.) And the room won't be drawn unless it is connected to the first room in the floor (either directly or indirectly.)

Second, there can be rooms on top of each other. For example, suppose that three rooms are created, one pair is connected north-south and one connected east-west.
+---+  +---+
| 2 +--+ 3 |
+-+-+  +---+
  |
+-+-+
| 1 |
+---+
Two more rooms are added, connected to the third one.
     to room 3
         |
+---+  +-+-+
| 5 +--+ 4 |
+---+  +---+
When you create room objects out of this structure, two rooms will end up on top of each other, making it look as if there are only four rooms.
+---+  +---+
| 2 +--+ 3 |
+-+-+  +-+-+
  |      |
+-+-+  +-+-+
|1,5+--+ 4 |
+---+  +---+

Edited by torigara, 26 April 2012 - 07:08 AM.

  • 0

#5 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • Retired Staff
  • 9260 posts
  • Version:Unknown

Posted 26 April 2012 - 07:11 PM

Well, I think there are two possibilities. First, there can be isolated rooms those have no connection.

for (p=0; p<4; p+=1) {
    if (irandom(100)<25) {
      Connect(rm, p);
    }
  }

There is (3/4)^4 = 31% chance that Connect isn't called. (Of course, as Connect doesn't always succeed, there is higher chance that the room get isolated.) And the room won't be drawn unless it is connected to the first room in the floor (either directly or indirectly.)

Second, there can be rooms on top of each other. For example, suppose that three rooms are created, one pair is connected north-south and one connected east-west.
+---+  +---+
| 2 +--+ 3 |
+-+-+  +---+
  |
+-+-+
| 1 |
+---+
Two more rooms are added, connected to the third one.
     to room 3
         |
+---+  +-+-+
| 5 +--+ 4 |
+---+  +---+
When you create room objects out of this structure, two rooms will end up on top of each other, making it look as if there are only four rooms.
+---+  +---+
| 2 +--+ 3 |
+-+-+  +-+-+
  |      |
+-+-+  +-+-+
|1,5+--+ 4 |
+---+  +---+

Right...

The first issue (with the chance of an isolated room) is an easy enough fix. But I have no idea how to fix the second problem. Keeping in mind the way these layouts are represented (as nodes with references to their connected nodes), what is an efficient way to determine if two rooms would overlap? There's no connection between (in your example) rooms 1 and 5. Would I have to do some kind of pathfinding process?

I'm trying to think of how I'd detect such a thing, and I'm coming up blank. I figured some sort of BFS that tracks the directionality of each edge could work, but I'm not entirely sure on the details of such an algorithm.

-IMP

*EDIT* Hm. I've also noticed yet another problem -_- . Sometimes, it places 3 stairwells on one floor. This shouldn't be possible, since it only places stairwells if it makes a new floor above or below the current one, right? So that should limit it to a maximum of 2 stairwells per floor? So how is it making 3 sometimes?

Edited by IceMetalPunk, 26 April 2012 - 07:15 PM.

  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users