Tue 09 October 2018

Procedural Villages

My inspiration for any tool or project has always been the thought that it could be done better, either that or I'd feel like I would benefit a lot from a missing feature.

In my first embarked I attempted to create a dungeon crawler, full of all the creatures and characters I thought were missing from traditional games.

It was through brute-force of the Roguebasin tutorial where I learnt how to code.

I must have created the same game 4 or 5 times before I decided to scrap the libtcod library and create a game without an interface.

Armed with stdout and turn-based printing, I implemented feature after feature. One of the features I was quite proud of was the randomly generated villages I'd spawn my player into.

Objects

I was aiming for something simple, just populate the map with houses that the player can interact with, for this I deconstructed a house into a rectangle.

class Rect:
    def __init__(self, x, y, h, v):
        self.x1 = x
        self.y1 = y
        self.x2 = x + h
        self.y2 = y + v

    def center(self):
        center_x = (self.x1 + self.x2) / 2
        center_y = (self.y1 + self.y2) / 2
        return (center_x, center_y)

    def internal(self, x, y):
        """
        [ ][ ][ ][ ]
        [ ][X][X][ ]
        [ ][X][X][ ]
        [ ][X][X][ ]
        [ ][ ][ ][ ]
        """
        ...
        return bool()

    def edges(self, x, y):
        """
        [X][X][X][X]
        [X][ ][ ][X]
        [X][ ][ ][X]
        [X][ ][ ][X]
        [X][X][X][X]
        """
        ...
        return bool()

    def sides(self, x, y):
        """
        [ ][X][X][ ]
        [X][ ][ ][X]
        [X][ ][ ][X]
        [X][ ][ ][X]
        [ ][X][X][ ]
        """
        ...
        return bool()

The poorly defined object above could now provide boolean confirmation to a co-ordinates existence in appropriate sections of a rectangle.

From the above Rect class I can place a door on a Rect.sides() and I can fill the area defined by Rect.internal() with items.

Empty space: | . |
Wall:        | # |
Monster:     | m |
Door:        | + |

| . | . | . | . | . | . |
| . | # | # | + | # | . |
| . | # | m | . | # | . |
| . | # | . | . | # | . |
| . | # | . | . | # | . |
| . | # | # | # | # | . |
| . | . | . | . | . | . |

Throwing in some size variations and randomising the house position on the map, (making sure there are no houses intersecting each other). Allowed me to generate maps that looked like these:

Example 1

| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | # | # | # | # | # | # | . | # | # | # | # | . | . | . |
| . | # | u | . | . | @ | # | . | # | . | m | # | . | . | . |
| . | + | . | . | . | . | # | . | # | . | . | + | . | . | . |
| . | # | # | # | # | # | # | . | # | # | # | # | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | > | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | # | # | + | # | # | # | . | . | . | . | . | . | . | . |
| . | # | . | . | r | . | # | . | . | . | . | . | . | . | . |
| . | # | . | . | . | . | # | . | . | . | . | . | . | . | . |
| . | # | # | # | # | # | # | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |

Example 2

| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | # | # | + | # | . | . | . | . |
| . | . | . | . | . | . | . | # | . | . | # | . | . | . | . |
| . | . | . | . | . | . | . | # | . | . | # | . | . | . | . |
| . | . | . | . | . | . | . | # | m | . | # | . | . | . | . |
| . | . | . | . | . | . | . | # | # | # | # | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
| . | . | . | . | . | . | . | . | . | . | . | # | # | # | # |
| . | # | # | # | # | # | # | # | . | . | . | # | . | . | # |
| . | # | . | . | . | . | u | # | . | . | . | + | . | . | # |
| . | + | . | . | . | . | . | # | . | . | . | # | > | . | # |
| . | # | X | . | . | . | . | # | . | . | . | # | . | r | # |
| . | # | # | # | # | # | # | # | . | . | . | # | # | # | # |
| . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |

Unfortunately my code was not beautiful back then. I still think it was a neat idea