Python Drawing an ASCII Map

I need to have a two-radius map drawn from the current player room in the MUD, which I create in python (or, if possible, more). Numbers are created as containers with self.exits = {'west':1, 'north':2} , where the key is the direction in which the value is located (UID of the neighboring room). The numbers are only related in this way. A player with self.location of 0 can enter "n", and their location based on the above variable will be 2, and the player’s UID will be added to this content in the room’s contents.

So, I would like to display a map that looks like this, based on the above variable, where "u" is the current location of the player.

  [ ] | [ ]-[u] 

I have reached this part, since it is only radius 1. Here is a small (greatly modified for publication here) fragment of how I did it, and you will understand why I am sending a message, as this is bad code.

 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 screen player.hear(' '+mn) player.hear(' '+sp) player.hear(mw+mloc) 

In my madness, I managed to do this work with all 8 different directions (diagonals, not up or down). But then I need to loop into the rooms that I just analyzed with my first loop, and then draw them and then touch it all and then consider the match (sp) of aces such as' \ 'or' | "if there is paths that cross each other.This small task immediately became a nightmare, and up to 200 lines before I finished.

Another hurdle is that I can only print line by line. Therefore, if the card has 50 characters, I should have player.hear() on 50 lines, and I don't mind. Just keep this in mind before posting a response.

I, too, are not picky about formatting. I just want the map at a glance to help players travel around the world.

Thanks guys. Hope I have provided enough information. Let me know if not. (Here is a link to the entire (incomplete and HORRIBLE) module I am referencing. Map.py

+6
source share
3 answers

This code has serious problems. Let me start the design from scratch. This, I hope, will serve as a good lesson in the design and creation of classes and data structures.

To get started, you need to organize your code around the Map class, which then presents your rooms as a grid. You should not think of “room 1,” “room 2,” etc. (Which is very difficult to track on a map) and, rather, think about rooms in terms of coordinates.

Now there are several possible functions that we ignore at the beginning, including the player who sees only the rooms he was in, the player who remains in the center of the map, and the diagonal paths. If you want them, you can put them later as soon as the core functionality works. So far, we are aiming for something that looks something like this:

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

That is, we present it as a grid, where some rooms are connected, while others are not. Let each room have a coordinate pair, something like this:

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

Let x be along the vertex, and let y be along the side. The top left is (0, 0), the one that has [u] in it (0, 1).

Now, what are the components of our Map class?

  • map height: integer

  • card width: integer)

  • player_x, player_y: player coordinates

  • possible paths: a list of pairs of rooms that we can move between them. The above card will be presented as:

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

Note that I ordered each pair in such a way that the first tuple goes first (which is important later).

So, now that we have our design, write that Map class!

I can imagine the four methods we want: print_map , move and initializer. The initialization is simple: just set the four attributes listed above:

 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 

Now move is simple enough. Given the direction 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 

The move function for the "north" simply checks if there is a path to the room above the one we are in.

Now for the most interesting part: map printing. You do this by self.height through the rows (from 0 to self.height ) and column by column (from 0 to self.width ). (Note: you cannot use print in this situation, since it automatically places a new line or a space after the line. Instead, we use 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 room else: sys.stdout.write("[ ]") # empty room # now see whether there 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 

Now, let everyone try it together. Here is the code:

 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 room else: sys.stdout.write("[ ]") # empty room # now see whether there 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) 

Please note that I have added a section below, which creates a map and allows the player to move around it. Here's what it looks like when it starts:

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

There are many improvements that can be made to this code (in particular, the move method is repeated), but this is a good start. Try making a 20x20 card and you will see that it is just expanding.

ETA: I have to point out that print_map can be rewritten in a much shorter form, something like:

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

But this is a bit more intense.

+11
source

I did this as an exercise when the rooms and the grid were "printed." I add this to the discussion, as it may be easier to implement with a possibly larger grid.

ASCII Map

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

Each cell in this grid represents three to three sets of "+" signs. Four rooms are made with identifiers from 1 to 4. Connections between rooms are represented as slashes, backslashes, and pipes.

The code

 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 

The move procedure is not implemented, and in order for this representation to work, only unique identifiers of characters will be displayed well.

The advantages of this system:

  • Easily add numbers and delete them.
  • the definition of a room is read by a person.
  • the __str__ output overload function and, therefore, the rooms and the grid "print themselves out", and this may come in handy for future debugging or adaptation to future formats, for example. like cells in an HTML table.
+4
source

Maps based on coordinates have many advantages, but given that many high-quality muds use the traditional room-based world, and people made machines for many clients of dirt and dirt, it is possible that a dirt machine is without coordinates. You just have to deal with conflicts in each case.

However, you can still use @ david-robinson's answer. What you want to do is keep the minimap centered on the player and dynamically update it using the output data. Do not try to save a map of the entire saved area; By dynamically updating you will avoid some geographic conflicts.

To write a map to a dirt client, all you have to do is write your line of cards correctly spaced and end it with a new line. You put all the lines of the map into a list so that it is sent as one group of lines (you do not want any other line inserted between the lines of the map, for example, when it sends a socket), and any dirt client will print it (with in monospace, of course).

0
source

Source: https://habr.com/ru/post/921574/


All Articles