Projecting MUD maps

BJörn Lindqvist bjourne at gmail.com
Mon Nov 6 07:25:57 EST 2006


On 11/5/06, Diez B. Roggisch <deets at nospam.web.de> wrote:
> BJörn Lindqvist schrieb:
> > Hello, I'm looking for an algorithm to project "MUD maps" such as the
> > following map: http://www.aww-mud.org/maps/MUD_Maps/Caerin-colour.jpg
> >
> > MUD:s consists of rooms, each rooms has up to four orthogonal edges
> > (north, east, west and south) that connects it to another room. So it
> > is very easy to model a MUD as a directed graph. But projecting the
> > graph on a surface is very complicated. For example, Room A:s North
> > edge may connect it to Room B. But Room B:s South edge may connect it
> > to Room C. Or another tricky example: Room A:s East edge connects it
> > to Room B, whose East edge connect it to Room C, whose North edge
> > connects it to Room D, whose West edge connects it to Room E, whose
> > South edge connects it to Room A. In that example, there would be a
> > "hole" between Room D and E.
>
>
> try graphviz. You can also annotate some compass information ther, I
> guess it should make for a better placement.

Sorry, I think everyone misunderstood what my problem is. Which is not
very strange since I see now that I didn't explain it very well.

In the code below, I have created a simple Room class. It has up to
four edges; north, east, west and south. The Room class also has the
method get_projection() which recursively traverses through all
reachable rooms in the graphs and gives them x,y coordinates and
returns a dict containing x,y-coordinates and rooms. The function
project_dict() simply plots the dict on stdout.

The main() function demonstrates how a map consisting of nine rooms
should be plotted. All well so far. But if there had been an east-west
edge from G to I, then that edge would have overlapped C:s position.
That would have been wrong and I want the algorithm in
get_projection() to detect such overlaps and automatically fix them.
In this case, the fix would have been to add 2 to G and I:s
y-coordinate. Then the east-west edge from G to I wouldn't overlap any
room.

So I wonder if there is any algorithm that can do what I'm asking for?

----------------------- mudgraph.py -----------------------
# -*- coding: utf-8 -*-
import sys

class Dir:
    dirs = ['n', 'e', 's', 'w']
    @classmethod
    def opposite(cls, dir):
        """
        Return the opposite direction, i.e Dir.opposite('n') => 's'.
        """
        opp_idx = (Dir.dirs.index(dir) + 2) % 4
        return Dir.dirs[opp_idx]

class Room:
    """
    A Room is a node in a mud map. Each room can have up to four
    connections (edges) to other rooms in the cardinal directions;
    north, east, south and west.
    """
    def __init__(self, name = None):
        self.name = name or "+"
        self.n = None
        self.s = None
        self.e = None
        self.w = None

    def connect(self, dir, room):
        """
        Create an edge dir from self to room. Also create an edge in
        the opposite direction from room to self.
        """
        setattr(self, dir, room)
        setattr(room, Dir.opposite(dir), self)

    def north(self, room):
        self.connect('n', room)

    def east(self, room):
        self.connect('e', room)

    def west(self, room):
        self.connect('w', room)

    def south(self, room):
        self.connect('s', room)

    def get_projection(self, x = 0, y = 0,
                       proj_dict = None,
                       visited = None):
        """
        Return a dictionary containing all Rooms in the map as
        values. Each key is the projected x and y position of the
        room.
        """
        if proj_dict is None:
            proj_dict = {}
        if visited is None:
            visited = set()
        visited.add(self)
        proj_dict[x, y] = self
        if self.n and not self.n in visited:
            self.n.get_projection(x, y - 1, proj_dict, visited)
        if self.e and not self.e in visited:
            self.e.get_projection(x + 1, y, proj_dict, visited)
        if self.w and not self.w in visited:
            self.w.get_projection(x - 1, y, proj_dict, visited)
        if self.s and not self.s in visited:
            self.s.get_projection(x, y + 1, proj_dict, visited)
        return proj_dict

def project_dict(dict):
    coords = dict.keys()

    max_x = 0
    max_y = 0
    min_x = 999
    min_y = 999
    for x, y in coords:
        if x > max_x:
            max_x = x
        elif x < min_x:
            min_x = x
        if y > max_y:
            max_y = y
        elif y < min_y:
            min_y = y

    max_x += 1
    max_y += 1
    for y in range(min_y, max_y):
        x_range = range(min_x, max_x)
        for x in x_range:
            if dict.has_key((x, y)) and dict[x, y].n:
                sys.stdout.write(" | ")
            else:
                sys.stdout.write("   ")
        sys.stdout.write("\n")
        for x in x_range:
            if dict.has_key((x, y)):
                room = dict[x, y]
                if room.w:
                    sys.stdout.write("-")
                else:
                    sys.stdout.write(" ")
                sys.stdout.write(room.name)
                if room.e:
                    sys.stdout.write("-")
                else:
                    sys.stdout.write(" ")
            else:
                sys.stdout.write("   ")
        sys.stdout.write("\n")
        for x in x_range:
            if dict.has_key((x, y)) and dict[x, y].s:
                sys.stdout.write(" | ")
            else:
                sys.stdout.write("   ")
        sys.stdout.write("\n")

def main():
    a = Room('A')
    b = Room('B')
    c = Room('C')
    d = Room('D')
    e = Room('E')
    f = Room('F')
    g = Room('G')
    h = Room('H')
    i = Room('I')

    a.east(b)
    b.south(c)
    b.east(h)
    c.south(d)
    a.north(e)
    a.west(f)
    f.south(g)
    i.north(h)


    dict = a.get_projection()

    project_dict(dict)

if __name__ == "__main__":
    main()


-- 
mvh Björn



More information about the Python-list mailing list