Python绘图ASCII地图

时间:2012-07-28 18:49:34

标签: python python-2.7 mud online-game

我需要在我正在使用python(或更多,如果可能的话)构建的MUD中从玩家的当前房间中抽取2个半径地图。房间设置为self.exits = {'west':1, 'north':2}的容器,其中键是值(相邻房间的UID)所在的方向。客房仅以这种方式相连。 self.location为0的玩家可以输入'n',并且根据上面的变量,他们的位置将为2,并且该房间的内容会将玩家的UID附加到其内容。

所以,我希望根据上面的变量显示如下所示的地图,其中'u'是玩家的当前位置..

    [ ]
     |
[ ]-[u]

我已经实现了这个部分,因为这只是一个半径1.这里有一个小的(经过大量修改,在此处发布)我是如何做到这一点的片段,你会明白为什么我发帖,因为它的代码很差。

mloc = '[u]'
mn = '   '
mw = '   '
spn= ' '
spw= ' '
for Exit in room.exits.keys():
  if Exit == 'north':
    mn = '[ ]'
    spn = '|'
  if Exit == 'west': 
    mw = '[ ]-'
# player.hear() is our function for printing a line to the player's screen
player.hear('    '+mn)
player.hear('     '+sp)
player.hear(mw+mloc)

在我精神错乱的情况下,我设法完成了所有8个不同方向(对角线,不包括向上或向下)的工作。但是我必须循环使用我的第一个for循环解析的房间,然后绘制它们,然后将它全部分开,然后考虑(sp)aces的重叠,例如'\'或' |”如果有相互交叉的路径。这个小小的任务立刻变成了噩梦,在我完成之前已经变成了200行。

另一个障碍是我只能逐行打印。因此,如果地图高50个字符,我必须在50行上有player.hear(),我不反对。在发布答案之前,请记住这一点。

我对格式化也不挑剔。我只想简单地想要一张“地图一览”,以便在世界各地旅行时帮助玩家。

谢谢你们。我希望我提供了足够的信息。如果没有,请告诉我。 (这是我正在引用的整个(未完成和可怕的)模块的链接。Map.py

3 个答案:

答案 0 :(得分:12)

此代码遇到严重问题。让我们从头开始设计。这有望成为如何设计和构建类和数据结构的一个很好的教训。

首先,您应该围绕Map课程组织代码,然后将您的房间表示为网格。你不应该考虑“房间1”,“房间2”等(这很难在地图上跟踪),而是用坐标来考虑房间。

现在,我们在开始时忽略了一些可能的功能,包括玩家只看到他去过的房间,玩家留在地图的中心,以及对角路径。如果你想要它们,你可以在基本功能工作后将它们放入。目前,我们的目标是看起来像这样:

[ ]-[u] [ ] [ ]
 |
[ ]-[ ]-[ ] [ ]
     |
[ ]-[ ]-[ ] [ ]
 |
[ ]-[ ]-[ ]-[ ]

也就是说,我们将它表示为一个网格,其中一些房间是连接的,而另一些则没有。让每个房间都有一个坐标对,有点像这样:

      0   1   2   3
   0 [ ]-[u] [ ] [ ]
      |
   1 [ ]-[ ]-[ ] [ ]
          |
   2 [ ]-[ ]-[ ] [ ]
      |
   3 [ ]-[ ]-[ ]-[ ]

设x沿顶部,y沿侧面。左上角是(0,0),其中[u]的那个是(0,1)。

现在,Map类的组件是什么?

  1. 地图高度:整数

  2. 地图宽度:整数)

  3. player_x,player_y:玩家坐标

  4. 可能的路径:我们可以在其间移动的一对房间列表。上面的地图将表示为:

    [((0, 0), (1, 0)), ((0, 0), (1, 0)), ((1, 0), (1, 1)), ((1, 1), (2, 1)),
     ((1, 0), (1, 2)), ((0, 2), (1, 2)), ((1, 2), (2, 2)), ((0, 2), (0, 3)),
     ((0, 3), (1, 3)), ((1, 3), (2, 3)), ((2, 3), (3, 3))]
    
  5. 请注意,我订购了每一对,以便更大的元组先行(后来很重要)。

    现在我们有了设计,让我们写下Map类!

    我可以想到我们想要的四种方法:print_mapmove和初始化程序。初始化很简单:只需设置我们上面列出的四个属性:

    class Map:
        def __init__(self, height, width, player_x, player_y, paths):
            self.height = height
            self.width = width
            self.x = player_x
            self.y = player_y
            self.paths = paths
    

    现在,move非常简单。给定方向n / e / s / w:

        def move(self, direction):
            if direction == "n":
                if ((self.x, self.y - 1), (self.x, self.y)) not in self.paths:
                    print "Cannot go north"
                else:
                    self.y -= 1
    

    “北”的move功能只检查房间的路径是否在我们所在的房间之上。

    现在是最有趣的部分:打印地图。您可以通过循环遍历行(0到self.height)和列(0到self.width)来完成此操作。 (注意:在这种情况下你不能使用print,因为它会自动在字符串后面添加换行符或空格。我们使用sys.stdout.write

    def print_map(self):
        for y in range(0, self.height):
            # print the yth row of rooms
            for x in range(0, self.width):
                if self.x == x and self.y == y:
                    sys.stdout.write("[u]")  # this is the player's room
                else:
                    sys.stdout.write("[ ]")  # empty room
                # now see whether there's a path to the next room
                if ((x, y), (x + 1, y)) in self.paths:
                    sys.stdout.write("-")
                else:
                    sys.stdout.write(" ")
            # now that we've written the rooms, draw paths to next row
            print  # newline
            for x in range(0, self.width):
                sys.stdout.write(" ")  # spaces for above room
                if ((x, y), (x, y + 1)) in self.paths:
                    sys.stdout.write("|  ")
                else:
                    sys.stdout.write("   ")
            print
    

    现在,让我们把它们放在一起然后尝试一下。这是代码:

    import sys
    
    class Map:
        def __init__(self, height, width, player_x, player_y, paths):
            self.height = height
            self.width = width
            self.x = player_x
            self.y = player_y
            self.paths = paths
    
        def move(self, direction):
            if direction == "n":
                if ((self.x, self.y - 1), (self.x, self.y)) not in self.paths:
                    print "Cannot go north"
                else:
                    self.y -= 1
            if direction == "s":
                if ((self.x, self.y), (self.x, self.y + 1)) not in self.paths:
                    print "Cannot go south"
                else:
                    self.y += 1
            if direction == "e":
                if ((self.x, self.y), (self.x + 1, self.y)) not in self.paths:
                    print "Cannot go east"
                else:
                    self.x += 1
            if direction == "w":
                if ((self.x - 1, self.y), (self.x, self.y)) not in self.paths:
                    print "Cannot go west"
                else:
                    self.x -= 1
    
        def print_map(self):
            for y in range(0, self.height):
                # print the yth row of rooms
                for x in range(0, self.width):
                    if self.x == x and self.y == y:
                        sys.stdout.write("[u]")  # this is the player's room
                    else:
                        sys.stdout.write("[ ]")  # empty room
                    # now see whether there's a path to the next room
                    if ((x, y), (x + 1, y)) in self.paths:
                        sys.stdout.write("-")
                    else:
                        sys.stdout.write(" ")
                # now that we've written the rooms, draw paths to next row
                print  # newline
                for x in range(0, self.width):
                    sys.stdout.write(" ")  # spaces for above room
                    if ((x, y), (x, y + 1)) in self.paths:
                        sys.stdout.write("|  ")
                    else:
                        sys.stdout.write("   ")
                print
    
    
    paths = [((0, 0), (1, 0)), ((0, 0), (1, 0)), ((1, 0), (1, 1)), ((1, 1),
             (2, 1)), ((1, 1), (1, 2)), ((0, 2), (1, 2)), ((1, 2), (2, 2)),
             ((0, 2), (0, 3)), ((0, 3), (1, 3)), ((1, 3), (2, 3)), ((2, 3),
             (3, 3))]
    m = Map(4, 4, 0, 0, paths)
    
    while True:
        m.print_map()
        direction = raw_input("What direction do you want to move? [n/e/s/w] ")
        m.move(direction)
    

    请注意,我在底部添加了一个创建地图的部分,并允许玩家在其周围移动。以下是它运行时的外观:

    Davids-MacBook-Air:test dgrtwo$ python Map.py 
    [u]-[ ] [ ] [ ] 
         |          
    [ ] [ ]-[ ] [ ] 
         |          
    [ ]-[ ]-[ ] [ ] 
     |              
    [ ]-[ ]-[ ]-[ ] 
    
    What direction do you want to move? [n/e/s/w] e
    [ ]-[u] [ ] [ ] 
         |          
    [ ] [ ]-[ ] [ ] 
         |          
    [ ]-[ ]-[ ] [ ] 
     |              
    [ ]-[ ]-[ ]-[ ] 
    
    What direction do you want to move? [n/e/s/w] s
    [ ]-[ ] [ ] [ ] 
         |          
    [ ] [u]-[ ] [ ] 
         |          
    [ ]-[ ]-[ ] [ ] 
     |              
    [ ]-[ ]-[ ]-[ ] 
    
    What direction do you want to move? [n/e/s/w] w
    Cannot go west
    [ ]-[ ] [ ] [ ] 
         |          
    [ ] [u]-[ ] [ ] 
         |          
    [ ]-[ ]-[ ] [ ] 
     |              
    [ ]-[ ]-[ ]-[ ] 
    
    What direction do you want to move? [n/e/s/w] e
    [ ]-[ ] [ ] [ ] 
         |          
    [ ] [ ]-[u] [ ] 
         |          
    [ ]-[ ]-[ ] [ ] 
     |              
    [ ]-[ ]-[ ]-[ ] 
    

    可以对此代码进行许多改进(特别是move方法是重复的),但这是一个好的开始。尝试制作地图20x20,你会发现它扩展得很好。

    ETA:我应该注意print_map可以用更短的形式重写,如:

    def print_map(self):
        for y in range(0, self.height):
            print "".join(["[%s]%s" %
                        ("u" if self.x == x and self.y == y else " ",
                         "-" if ((x, y), (x + 1, y)) in self.paths else " ")
                            for x in range(0, self.width)])
            print " " + "   ".join(["|" if ((x, y), (x, y + 1)) in self.paths
                                  else " " for x in range(0, self.width)])
    

    但这有点激烈。

答案 1 :(得分:4)

我这样做是为了练习房间和网格“打印自己”。我将此添加到讨论中,因为使用最终更大的网格可能更容易实现。

ASCII地图

+++++++++++++++
+++++++++++++++
+++++++++++++++
++++++   ++++++
++++++ 2 ++++++
++++++/| ++++++
+++  / | ++++++
+++ 3--1 ++++++
+++     \++++++
+++++++++\  +++
+++++++++ 4 +++
+++++++++   +++
+++++++++++++++
+++++++++++++++
+++++++++++++++

此网格中的每个单元格都是三到三组“+”符号。四个房间实现了id值1到4.房间之间的连接用斜线,反斜线和管道表示。

守则

class Room(object):
    def __init__(self, id, loc, exits):
        self.id = id # unique identifier, may be a name
        self.row = loc[0] # loc is tuple of (row, col)
        self.col = loc[1] 
        # exits is a list where 'X' means no exit and 
        # any other value is id of destination
        self.exits = exits 

    def __str__(self):
        directions = '\\|/- -/|\\'
        room = [ e if e == 'X' else ' ' for e in self.exits ]
        for idx in range(len(room)):
            if room[idx] == ' ':
                room[idx] = directions[idx]
            if room[idx] == 'X':
                room[idx] = ' '
        room[4] = self.id[0] # only print first char of id
        return ''.join(room)

class Map(object):
    def __init__(self, rows, cols, rooms):
        self.rows = rows
        self.cols = cols
        self.rooms = rooms

    def __str__(self):
        world = []
        for i in range(self.rows * 3):
            world.append( ['+++'] * self.cols )
        for room in self.rooms:
            ascii = str(room)
            x = room.col
            y = room.row
            for idx in range(0, 3):
                cell = ascii[idx*3:idx*3+3]
                world[y*3+idx][x] = cell
        return '\n'.join( [ ''.join(row) for row in world ] )


if __name__ == '__main__':
    # set up four rooms
    # each room has unique id (string of any length) and coordinates
    # it also has a set of 8 possible exits, represented as a list where
    # 'X' means exit is blocked and valid exits contain the id of the target room
    r1 = Room(id='1', loc=(2,2), exits=['X','2','X',
                                        '3',' ','X',
                                        'X','X','4',])
    r2 = Room(id='2', loc=(1,2), exits=['X','X','X',
                                        'X',' ','X',
                                        '3','1','X',])
    r3 = Room(id='3', loc=(2,1), exits=['X','X','2',
                                        'X',' ','1',
                                        'X','X','X',])
    r4 = Room(id='4', loc=(3,3), exits=['1','X','X',
                                        'X',' ','X',
                                        'X','X','X',])
    # initialize Map with a list of these four rooms
    map = Map(rows = 5, cols=5, rooms=[r1, r2, r3, r4])
    print map

未实现移动例程,并且为了使该表示起作用,只有单个字符ID才能很好地显示。

该系统的优点:

  • 轻松添加房间并将其删除
  • 房间的定义是人类可读的
  • 输出函数重载__str__,因此房间和网格“打印自己”,这可能对将来调试或适应未来格式有用,例如作为HTML表格中的单元格。

答案 2 :(得分:0)

基于坐标的地图有很多优点,但考虑到许多优质泥浆使用传统的基于房间的世界,并且人们已经为许多泥浆和泥浆客户制作了自动化装置,因此制作自动化程序并不是不可能的。泥没有坐标。您只需要根据具体情况处理冲突。

但是,您仍然可以使用@ david-robinson的答案。您要做的是将小地图大致保持在播放器的中心位置,并使用退出数据动态更新它。不要试图保存整个区域的地图;通过动态更新,您将避免一些地理冲突。

要将地图写入泥浆客户端,您需要做的就是正确地间隔地写下地图线并用新线终止它。您将所有地图行放入一个列表中,以便将其作为一组行发送(您不希望在地图行之间插入其他行,例如当它从套接字发出时),并且任何泥浆客户端都会将其打印出来正确(当然使用等宽字体)。