Pregunta

Necesito tener un mapa de 2 radios extraído de la habitación actual del jugador en un MUD que estoy construyendo en Python (o más, si es posible).Las habitaciones están configuradas como contenedores con un self.exits = {'west':1, 'north':2} donde la clave es la dirección en la que se encuentra el valor (UID de la habitación adyacente).Las habitaciones están conectadas únicamente de esta manera.Un jugador con una ubicación propia de 0 podría escribir 'n' y su ubicación, según la variable anterior, sería 2 y el contenido de esa sala tendría el UID del jugador adjunto a su contenido.

Entonces, me gustaría que se muestre un mapa similar al siguiente, basado en la variable anterior, donde 'u' es la ubicación actual del jugador.

    [ ]
     |
[ ]-[u]

he logrado este porción, ya que esto es solo un radio de 1.Aquí hay un pequeño fragmento (muy modificado para publicarlo aquí) de cómo hice esto, y verás por qué lo estoy publicando, ya que es un código deficiente.

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)

En mi locura, logré hacer que esto funcionara con las 8 direcciones diferentes (diagonales, sin incluir arriba ni abajo).Pero luego tengo que bucle las habitaciones que acabo de analizar con mi primer bucle, y luego dibujar esos, y luego espaciar todo, y luego tener en cuenta la superposición de los espacios (espacios) como '\' o '|' si hay caminos que se cruzan entre sí.Esta pequeña tarea se convirtió en una pesadilla de inmediato y hasta 200 líneas antes de que terminara.

Otro obstáculo es que sólo puedo imprimir línea por línea.Entonces, si el mapa tiene 50 caracteres de alto, tengo que tener player.hear() en 50 líneas, a lo que no me opongo.Solo tenlo en cuenta antes de publicar una respuesta.

Tampoco soy exigente con el formato.Simplemente quiero un "mapa de un vistazo" para ayudar a los jugadores mientras viajan por el mundo.

Gracias chicos.Espero haber proporcionado suficiente información.Avísame, si no.(Aquí hay un enlace al módulo completo (sin terminar y HORRIBLE) al que me refiero. Mapa.py

¿Fue útil?

Solución

Este código está en serios problemas.Empecemos a diseñar desde cero.Es de esperar que esto sirva como una buena lección sobre cómo diseñar y construir clases y estructuras de datos.

Para empezar, debes organizar tu código en torno a un Map class, que luego representa sus habitaciones como una cuadrícula.No deberías pensar en "habitación 1", "habitación 2", etc. (lo cual es muy difícil de seguir en un mapa), sino más bien pensar en las habitaciones en términos de coordenadas.

Ahora, hay algunas características posibles que estamos ignorando al principio, incluyendo que el jugador solo vea las habitaciones en las que ha estado, que el jugador permanezca en el centro del mapa y caminos diagonales.Si los desea, puede instalarlos más tarde, una vez que funcione la funcionalidad básica.Por ahora, nuestro objetivo es algo que se parezca un poco a esto:

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

Es decir, lo representamos como una cuadrícula donde algunas habitaciones están conectadas y otras no.Hagamos que cada habitación tenga un par de coordenadas, un poco como esto:

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

Sea x la parte superior y y el lado.La parte superior izquierda es (0, 0), la que tiene [u] en él está (0, 1).

Ahora bien, ¿cuáles son los componentes de nuestro Map ¿clase?

  1. altura del mapa:entero

  2. ancho del mapa:entero)

  3. jugador_x, jugador_y:coordenadas del jugador

  4. caminos posibles:una lista de pares de habitaciones entre las que podemos movernos.El mapa anterior se representaría como:

    [((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))]
    

Observe que ordené cada par de manera que la tupla más grande vaya primero (eso es importante más adelante).

Ahora que tenemos nuestro diseño, escribamos eso. Map ¡clase!

Puedo pensar en cuatro métodos que queremos: print_map, move, y un inicializador.La inicialización es simple:simplemente configure los cuatro atributos que enumeramos anteriormente:

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

Ahora, move es bastante simple.Dada una dirección 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

El move La función para "norte" simplemente verifica si hay un camino a la habitación encima de la que estamos.

Ahora viene la parte más interesante:imprimiendo el mapa.Para ello, recorra las filas (0 a self.height) y sobre las columnas (0 a self.width). (Nota:no puedes usar print en esta situación, ya que automáticamente coloca una nueva línea o un espacio después de la cadena.En lugar de eso usamos sys.stdout.escribir.

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

Ahora, juntemos todo y probémoslo.Aquí está el código:

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)

Observe que agregué una sección en la parte inferior que crea un mapa y permite al jugador moverse por él.Así es como se ve cuando se ejecuta:

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] [ ] 
     |          
[ ]-[ ]-[ ] [ ] 
 |              
[ ]-[ ]-[ ]-[ ] 

Hay muchas mejoras que se pueden hacer a este código (en particular, el move El método es repetitivo), pero es un buen comienzo.Intente hacer el mapa de 20x20 y verá que se expande muy bien.

Hora estimada de llegada:debo notar que print_map podría reescribirse en una forma mucho más corta como algo como:

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)])

Pero esto es un poco más intenso.

Otros consejos

Hice esto como un ejercicio en el que las habitaciones y la cuadrícula se "imprimen solas".Agrego esto a la discusión ya que puede ser más fácil de implementar con una eventual cuadrícula más grande.

El mapa ASCII

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

Cada celda de esta cuadrícula es un conjunto de signos '+' de tres por tres.Se implementan cuatro salas con valores de identificación del 1 al 4.Las conexiones entre las habitaciones se representan como barras, barras invertidas y tuberías.

El código

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

La rutina de movimiento no está implementada y, para que esta representación funcione, solo se mostrarán correctamente los identificadores de un solo carácter.

Las ventajas de este sistema:

  • fácil de agregar habitaciones y eliminarlas
  • la definición de una habitación es legible por humanos
  • las funciones de salida se sobrecargan __str__ y, por lo tanto, las habitaciones y la cuadrícula se "imprimen solas" y esto podría resultar útil para futuras depuraciones o adaptaciones a formatos futuros, p.como celdas en una tabla HTML.

Los mapas basados ​​en coordenadas tienen muchas ventajas, pero considerando que muchos lodos de calidad utilizan un mundo tradicional basado en habitaciones, y la gente ha creado mapas automáticos para muchos lodos y clientes de lodos, no está descartado hacer un mapa automático para un lodo sin coordenadas. .Simplemente tendrás que lidiar con los conflictos caso por caso.

Sin embargo, aún puedes usar la respuesta de @ david-robinson.Lo que quieres hacer es mantener un minimapa aproximadamente centrado en el jugador y actualizarlo dinámicamente usando los datos de salida.No intentes mantener almacenado un mapa de toda el área;Al actualizar dinámicamente evitarás algunos conflictos geográficos.

Para escribir el mapa en un cliente de barro, todo lo que necesita hacer es escribir la línea del mapa con el espacio adecuado y terminarla con una nueva línea.Colocas todas las líneas del mapa en una lista para que se envíe como un solo grupo de líneas (no deseas que se inserte ninguna otra línea entre las líneas del mapa, por ejemplo, cuando se envía por el socket), y cualquier cliente de barro lo imprimirá. correctamente (con una fuente monoespaciada, por supuesto).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top