Putting the 'role' back in role-playing games since 2002.
Donate to Codex
Good Old Games
  • Welcome to rpgcodex.net, a site dedicated to discussing computer based role-playing games in a free and open fashion. We're less strict than other forums, but please refer to the rules.

    "This message is awaiting moderator approval": All new users must pass through our moderation queue before they will be able to post normally. Until your account has "passed" your posts will only be visible to yourself (and moderators) until they are approved. Give us a week to get around to approving / deleting / ignoring your mundane opinion on crap before hassling us about it. Once you have passed the moderation period (think of it as a test), you will be able to post normally, just like all the other retards.

Can Python 3 handle showing the map of a dungeon crawler?

Caim

Arcane
Joined
Aug 1, 2013
Messages
15,468
Location
Dutchland
I'm currently working on teaching myself Python. As part of this I am working on a small dungeon crawler that runs in the command prompt. I've been using this example to be able to design a dungeon, which works fairly well so far though it's quite a bit of manual work to get it all going.

However, due to the nature of how it is visualized in the command prompt you'll get easily lost. So the solution would be the addition of a map to the game. Since the command prompt does not allow for extensive graphics I'm going with something like this:

ocF6kin.png


Lines are usable connections between rooms, the E is the entrance/exit, the * is the player, the S can be something of interest, the Os are explored rooms and the ? are unexplored rooms. I think that I'm able to get such a map drawn in the prompt, but it's the moving part that's gonna be different. I think I'll have to somehow link the player's presence in a room (all of them are numbered behind the scenes) and somehow draw that room differently. But how would I go about doing this? Or should I do this in a completely different way?
 

7h30n

Augur
Joined
Aug 14, 2012
Messages
311
Yep, you will probably want to find a library for it (like curses) so that you don't have to mess around with color codes and terminal emulators across Linux/Windows/Mac OS.
 

Niektory

one of some
Patron
Joined
Mar 15, 2005
Messages
808
Location
the great potato in the sky
Not sure if I understood your question, but I'll try to answer.

We can store our map in a list of lists like this:
Code:
map = [
    list("  O-O  "),
    list("  | |  "),
    list("  O O  ")
]
and print it with:
Code:
for row in map:
    print("".join(row))
Of course, we want to update the map before printing. For that the program needs to know which room corresponds to what coordinates on the map. We can add that information to the dictionary with the rooms:
Code:
    1 : {  "name"  : "Hall" ,
           "east"  : 2,
           "south" : 3,
           "map_x" : 2,
           "map_y" : 0 }  ,
and so on for each room.
Now it's just a matter of iterating through all rooms in the dictionary and updating all the rooms on the map with the correct symbols. For example:
Code:
for room_no, room in rooms.items():
    if currentRoom == room_no:
        map[room["map_y"]][room["map_x"]] = "*"
    elif "item" in room:
        map[room["map_y"]][room["map_x"]] = "S"
    else:
        map[room["map_y"]][room["map_x"]] = "O"

It's not the best method of doing this since it requires manually drawing the map and entering the coordinates, but it's a good start.
 
Last edited:

Caim

Arcane
Joined
Aug 1, 2013
Messages
15,468
Location
Dutchland
Yeah, that's a pretty good answer. I didn't think of making it an X/Y coordinate map, I was thinking of going with the whole map number thing and drawing from there, but the coordinates sounds like a saner alternative. I think I'll go with predefining a map size like 10/10 and numbering from there, so that coordinate 0,0 is room 1, 1,0 is room 2, 2,0 is room 3 and so on. This makes it easier to determine which number individual rooms are for editing purposes.
 

vean

Scholar
Joined
Jan 3, 2016
Messages
296
Ha ha, making data structures in a language without pointers. How cute.
 

Niektory

one of some
Patron
Joined
Mar 15, 2005
Messages
808
Location
the great potato in the sky
Yeah, that's a pretty good answer. I didn't think of making it an X/Y coordinate map, I was thinking of going with the whole map number thing and drawing from there, but the coordinates sounds like a saner alternative. I think I'll go with predefining a map size like 10/10 and numbering from there, so that coordinate 0,0 is room 1, 1,0 is room 2, 2,0 is room 3 and so on. This makes it easier to determine which number individual rooms are for editing purposes.
Nah, my approach is not particularly sane, too much tedious and error-prone manual data entry. Ideally, the map should be generated algorithmically from the room data. I just wanted to give you something simple to get you started. Once you get it working, you can and should improve it.

EDIT:
Getting rid of storing the coordinates and calculating them from the room number is actually a pretty good idea. It's also possible to do it the other way around, get rid of room numbers and use coordinate pairs as dictionary keys, like this:
Code:
  (2,0) : {  "name"  : "Hall" ,
  "east"  : (4,0),
  "south" : (2,2) }  ,
You could also forgo storing the numbers/coordinates of adjacent rooms, just store the directions you can move in from each room and calculate the destination on the fly by adding/subtracting the coordinates.
 
Last edited:

Caim

Arcane
Joined
Aug 1, 2013
Messages
15,468
Location
Dutchland
Well, I got a basic map system working now that shows whether or not the player's there. Downside is, well...

def mapLocation1():
if rooms[1]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
return(icons["PlayerLocation"])
else:
return(icons["EmptyRoom"])

def mapLocation2():
if rooms[2]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
return(icons["PlayerLocation"])
else:
return(icons["EmptyRoom"])

def mapLocation3():
if rooms[3]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
return(icons["PlayerLocation"])
else:
return(icons["EmptyRoom"])

def mapLocation4():
if rooms[4]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
return(icons["PlayerLocation"])
else:
return(icons["EmptyRoom"])

def showStatus():
print("---------------------------")
print("You are in the " + rooms[currentRoom]["name"])
if "item" in rooms[currentRoom]:
print("You see a " + rooms[currentRoom]["item"])
if "monster" in rooms[currentRoom]:
print("You see a " + rooms[currentRoom]["monster"])
print(mapLocation1(), "-", mapLocation2(), sep="")
print("| |")
print(mapLocation3(), " ", mapLocation4(), sep="")
print("---------------------------")

rooms = {
1 : { "name" : "Wide tunnel. Go east or south?",
"east" : 2,
"south" : 3,
"mapLoc": 1,
"monster" : "orc"
},

2 : { "name" : "Main tunnel. Go west or south?",
"west" : 1,
"south" : 4,
"mapLoc": 2,
"item" : "sword"
},

3 : { "name" : "Lair. Go north?",
"north" : 1,
"mapLoc": 3,
"monster" : "minotaur"
},

4 : { "name" : "Entrance. Go north?",
"north" : 2,
"mapLoc": 4
}
}

icons = {
"PlayerLocation" : "X",
"EmptyRoom" : "0",
"Items" : "I"
}

currentRoom = 1
And I haven't even managed to get items or monsters to show. I'm probably going to split the monsters and items into their own library as well.

I also have to figure out how to get Pyton to read from different files in the same folder instead of slapping everything into a single document that ends up being tens of thousands of lines of code long. How do I do that?
 

racofer

Thread Incliner
Joined
Apr 5, 2008
Messages
25,577
Location
Your ignore list.
First question: why Python? It becomes a mess as your program gets larger; being untyped only makes your code more confusing and prone to runtime errors that a (pre)compiled language wouldn't have; your future self will have trouble understanding your own code.

Also, because you're reading a guide made by pythonfags, they love to throw around fancy stuff like dictionaries for everything when they're quite unsuitable for solving your problem. Modeling a 2D text based game with simple objects is much simpler, elegant, and easier to manage as your game gets larger. For example, each object knows how to draw itself on the terminal depending on its internal state, such as items or player position.

Try to model your game world as a two dimensional array of "cell" objects that hold the stuff you need, e.g.:

Code:
Cell myGameWorld[][];
Cell currentPosition;

enum Movements
{
    North, South, East, West
}

class Cell
{
    Item cellItem; //there could even be a list of items in each cell
    Movements cellConnections[4]; //one for each direction to where this cell connects to others (there are probably simpler ways to do this)
    
    // returns a list of the possible movements from this cell so that your external movement logic can list player movement actions from this cell
    List<Movements> getMovements()
    {
        return cellConnections;
    }

    // Draws the cell. The idea is that each cell knows how to draw itself on the game map.
    void draw(Cell currentPosition)
    {
        if(currentPosition == this)
            print("*");
        else if(item == null)
            print("o");
        else
            print(cellItem.icon);
    }
}

class Item
{
    string description;
    char icon;
}

And so on. Model the problem first, then start coding. If C++ is out of the question due to cross compatibility issues or it being too low level for your tastes, then go Java. It's a very well documented language that supports everything you will need and will also get the job done, plus cross compatibility just as in Python.
 

DakaSha II

Prospernaut
Joined
Mar 26, 2016
Messages
209
Too many people are going to tell him that python is perfect and a godsend. It wont work. Give up.
 

Piety

Shitpostin'
Joined
May 22, 2009
Messages
1,777
Location
Chicago
Codex 2012 Codex 2016 - The Age of Grimoire Torment: Tides of Numenera
Python is completely fine for a project like this you pompous asses. If he keeps at it he'll likely grow out of it, but it'll be great to learn with.

Caim, you can (and should) try an object oriented approach like racofer suggested, and you can do it in python. See here.

At some point, you may also benefit from looking this over (though I could not find a tutorial, which you might want at this stage of your journey). Using a library like this will get you into the "making a game" part of the project quicker and give you less time floundering in the "jacking around trying to get something that makes sense to appear on the screen" portion. Though that portion, floundering while trying to build the really basic shit, can be extremely instructive and a valuable learning experience. Whether you succeed or not, you'll learn a lot about python and programming in general trying to hack something like this together.
 

Niektory

one of some
Patron
Joined
Mar 15, 2005
Messages
808
Location
the great potato in the sky
Code:
def mapLocation1():
  if rooms[1]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
  return(icons["PlayerLocation"])
  else:
  return(icons["EmptyRoom"])

def mapLocation2():
  if rooms[2]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
  return(icons["PlayerLocation"])
  else:
  return(icons["EmptyRoom"])

def mapLocation3():
  if rooms[3]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
  return(icons["PlayerLocation"])
  else:
  return(icons["EmptyRoom"])

def mapLocation4():
  if rooms[4]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
  return(icons["PlayerLocation"])
  else:
  return(icons["EmptyRoom"])
Okay, this is ugly. You don't need a separate function for each room, just pass the room's number to the function as an argument.
Code:
def mapLocation(room_number):
  if rooms[room_number]["mapLoc"] == rooms[currentRoom]["mapLoc"]:
  return(icons["PlayerLocation"])
  else:
  return(icons["EmptyRoom"])
Now you just use mapLocation(1) to get the 1st room's symbol and so on.

More info about functions:
https://docs.python.org/3/tutorial/controlflow.html#defining-functions

In general, if you see yourself repeating similar code a lot, it's a sign there's probably a better way to do it.

I also have to figure out how to get Pyton to read from different files in the same folder instead of slapping everything into a single document that ends up being tens of thousands of lines of code long. How do I do that?
Just make another .py file, for example some_module.py, put some definitions there, then at the top of your main file put import some_module. Now you'll be able to use stuff from that module like this: some_module.some_function()

More info about modules:
https://docs.python.org/3/tutorial/modules.html
 

Caim

Arcane
Joined
Aug 1, 2013
Messages
15,468
Location
Dutchland
Thanks, that works perfectly! I've also figured out how to laod things from another file, which is just a matter of using from and import *. For showing other icons based on what's in that room I use this:

code said:
elif "item" in rooms[room_number]:
return(icons["ItemLocation"])

Currently working on expanding the health system, so far I've got this:

code said:
OwnHealth = 100
OwnHealthMax = 150
OwnMagic = 100
OwnMagicMax = 150
OwnMagicLimit = 120

print("Health: ", OwnHealth, "/", OwnHealthMax," Magic: ", OwnMagic,"/",OwnMagicLimit,sep="")

while True:
action = input("Do you want to heal? (yes/no) ").lower().split()
if action[0] == ("Yes") or action[0] == ("yes"):
print("You are healed!")
OwnHealth = OwnHealthMax
OwnMagic = OwnMagicLimit
print("Health: ", OwnHealth, "/", OwnHealthMax," Magic: ", OwnMagic,"/",OwnMagicLimit,sep="")
break
elif action[0] == ("No") or action[0] == ("no"):
print("You are not healed.")
print("Health: ", OwnHealth, "/", OwnHealthMax," Magic: ", OwnMagic,"/",OwnMagicLimit,sep="")
break
else:
print("Try again")

I feel that this is too bloated right now, so I'm trying to cut down the code by adding functions, but when I tried to print with those I get everything between double quotation marks, commas and spaces. Is there a way around this?

Also, I realized that if I do not enter anything and press enter I get an index error. Hw can I go about fixing that?
 

Niektory

one of some
Patron
Joined
Mar 15, 2005
Messages
808
Location
the great potato in the sky
I feel that this is too bloated right now, so I'm trying to cut down the code by adding functions, but when I tried to print with those I get everything between double quotation marks, commas and spaces. Is there a way around this?
I'm not sure what you mean. Post the code. And use [ code ] tags to preserve indentation.

Also, I realized that if I do not enter anything and press enter I get an index error. Hw can I go about fixing that?
Index error? Always check the docs first for details:
https://docs.python.org/3/library/exceptions.html#IndexError
exception IndexError
Raised when a sequence subscript is out of range.
In this case you're trying to get the first element of a sequence that contains zero elements. To prevent that error you can check how many elements the sequence has before accessing them. The len() function can help you with that.
https://docs.python.org/3/library/functions.html#len

Another way is to catch the exception but let's not go there yet.
 

Caim

Arcane
Joined
Aug 1, 2013
Messages
15,468
Location
Dutchland
I feel that this is too bloated right now, so I'm trying to cut down the code by adding functions, but when I tried to print with those I get everything between double quotation marks, commas and spaces. Is there a way around this?
I'm not sure what you mean. Post the code. And use [ code ] tags to preserve indentation.

Also, I realized that if I do not enter anything and press enter I get an index error. Hw can I go about fixing that?
Index error? Always check the docs first for details:
https://docs.python.org/3/library/exceptions.html#IndexError
exception IndexError
Raised when a sequence subscript is out of range.
In this case you're trying to get the first element of a sequence that contains zero elements. To prevent that error you can check how many elements the sequence has before accessing them. The len() function can help you with that.
https://docs.python.org/3/library/functions.html#len

Another way is to catch the exception but let's not go there yet.
I mean the whole print line, which looks like this:

Code:
print("Health: ", OwnHealth, "/", OwnHealthMax," Magic: ", OwnMagic,"/",OwnMagicLimit,sep="")

I've tried something like this with len(), which looks like this but doesn't seem to work:

Code:
  if len(action) == 0:
     print("Please try again.")
 

Niektory

one of some
Patron
Joined
Mar 15, 2005
Messages
808
Location
the great potato in the sky
I mean the whole print line, which looks like this:

Code:
print("Health: ", OwnHealth, "/", OwnHealthMax," Magic: ", OwnMagic,"/",OwnMagicLimit,sep="")
Well, it's the code that works as expected, isn't it? If you don't post the one that doesn't I can't know what your mistake is.

I've tried something like this with len(), which looks like this but doesn't seem to work:

Code:
  if len(action) == 0:
     print("Please try again.")
So you tell the user to try again, but do you give him a chance to do so? Here's how it should be done:
http://stackoverflow.com/questions/...er-for-input-until-they-give-a-valid-response
 

As an Amazon Associate, rpgcodex.net earns from qualifying purchases.
Back
Top Bottom