Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialcirclewave
8,232 PointsJust Another Dungeon Game - Review/Tips Appreciated.
Posting my code for the dungeon game.
Interested to see if there are any glaring issues in the way I'm writing code as this is my first time learning programming. Anything obvious that I could do more efficiently, etc? Any tips or recommendations is much appreciated!
Thank you Kenneth Love for a great course so far, 2 courses into the Learn Python track and really digging it.
#-----------------------------------------------------------------------
# IMPORT
#-----------------------------------------------------------------------
import random
#-----------------------------------------------------------------------
# FUNCTIONS
#-----------------------------------------------------------------------
def set_map_size():
try:
size = int(input("> "))
if size > 2 and size < 21:
return(size)
else:
print("\nPlease choose an integer between 3 and 20")
return(set_map_size())
except:
print("\nPlease choose an integer between 3 and 20")
return(set_map_size())
def generate_map_grid(size):
new_map = []
for y in range(size):
for x in range(size):
new_map.append((y,x))
return(new_map)
def generate_locations():
door_loc = random.choice(map_grid)
monster_loc = random.choice(map_grid)
while monster_loc == door_loc:
monster_loc = random.choice(map_grid)
player_loc = random.choice(map_grid)
while player_loc == door_loc or player_loc == monster_loc:
player_loc = random.choice(map_grid)
return(door_loc, monster_loc, player_loc)
def generate_map_dict(map_grid):
map_dict = {}
for location in map_grid:
if location == door_loc:
map_dict[location] = 'door'
elif location == monster_loc:
map_dict[location] = 'monster'
elif location == player_loc:
map_dict[location] = 'player'
else:
map_dict[location] = 'empty'
return(map_dict)
def render_live_map():
x_count = 0
print('')
for location in map_grid:
if map_dict[location] == 'empty':
print ("[ ]", end='')
elif map_dict[location] == 'breadcrumb':
print("[~]", end='')
elif map_dict[location] == 'monster':
if playing_game == False:
print("[X]", end='')
else:
print("[ ]", end='')
elif map_dict[location] == 'door':
if playing_game == False:
print("[@]", end='')
else:
print("[ ]", end='')
elif map_dict[location] == 'player':
print("[P]", end='')
if x_count == map_size - 1:
print("\n", end='')
x_count = 0
else:
x_count += 1
def move_player(direction, current_player_loc):
player_y, player_x = current_player_loc
moved = True
if direction == "L" and not player_x == 0:
player_x -= 1
elif direction == "R" and not player_x == map_size - 1:
player_x += 1
elif direction == "U" and not player_y == 0:
player_y -= 1
elif direction == "D" and not player_y == map_size -1:
player_y += 1
else:
print("\n*** You can't move that way, you're at the edge of the map.")
moved = False
new_player_loc = player_y, player_x
return(new_player_loc, current_player_loc, moved)
def show_legend():
print("\nLEGEND:\n[ ] = room\n[P] = player\n[~] = breadcrumb\n[@] = door (hidden until you win, lose, or quit)\n[X] = monster (hidden until you win, lose, or quit)")
def show_help():
print("\nHELP:\n- Move by typing L, R, U, or D.\n- Also type map, legend, help, or quit.")
#-----------------------------------------------------------------------
# START GAME: GENERATE MAP GRID, SET LOCATIONS, GENERATE LIVE MAP DICT
#-----------------------------------------------------------------------
print("\nSET MAP SIZE\n(3 = 3x3, 4 = 4x4, etc)\nStart with something small like 3 or 4. Min is 3, max is 20.")
map_size = set_map_size()
map_grid = generate_map_grid(map_size)
door_loc, monster_loc, player_loc = generate_locations()
map_dict = generate_map_dict(map_grid)
print("\n{}*{} map successfuly generated.".format(map_size,map_size))
show_legend()
show_help()
print("\nARE YOU READY TO ENTER THE DUNGEON?\nType Y if yes, or anything else to quit.")
if input("> ").upper() == 'Y':
playing_game = True
render_live_map()
else:
playing_game = False
print("\nGood bye.")
#-----------------------------------------------------------------------
# MAIN GAME LOOP
#-----------------------------------------------------------------------
while playing_game == True:
move = input("\n> ").upper()
render_map = True
if move == "QUIT":
playing_game = False
quit = True
render_live_map()
print("\nOK. You quit. Bye...")
elif move == "HELP":
show_help()
continue
elif move == "LEGEND":
show_legend()
continue
elif move == "MAP":
render_live_map()
continue
elif move == "L" or move == "R" or move == "U" or move == "D":
player_loc, old_player_loc, moved = move_player(move, player_loc)
if moved == True:
map_dict[old_player_loc] = 'breadcrumb'
map_dict[player_loc] = 'player'
else:
render_map = False
if player_loc == door_loc:
playing_game = False
map_dict[player_loc] = 'door' #to show location of door during final map render instead of P
render_live_map()
print("\nYOU FOUND THE DOOR! YOU WIN!")
elif player_loc == monster_loc:
playing_game = False
map_dict[player_loc] = 'monster' #to show location of monster during final map render instead of P
render_live_map()
print("\nGAME OVER. THE MONSTER GOT YOU.")
elif render_map == True:
render_live_map()
else:
print("\n*** Invalid selection. Type help to see commands.")
continue
#-----------------------------------------------------------------------
# PLAY AGAIN?
#-----------------------------------------------------------------------
if playing_game == False and not quit == True:
print("\nPlay Again?\nType Y if yes, or anything else to quit.")
if input("> ").upper() == "Y":
playing_game = True
print("\nSET MAP SIZE\n(3 = 3x3, 4 = 4x4, etc)\nMin is 3, max is 20.")
map_size = set_map_size()
map_grid = generate_map_grid(map_size)
door_loc, monster_loc, player_loc = generate_locations()
map_dict = generate_map_dict(map_grid)
print("\n{}*{} map successfuly generated.".format(map_size,map_size))
render_live_map()
else:
print("Ok, bye.")
1 Answer
Iain Simmons
Treehouse Moderator 32,305 PointsWorks great!
You could do a few things to clean up the code (like using the PEP 8 style guide for Python coding), and shorten/combine some things:
-
Use a list comprehension to generate the map grid and return in one line:
### before ### def generate_map_grid(size): new_map = [] for y in range(size): for x in range(size): new_map.append((y,x)) return(new_map) ### after ### def generate_map_grid(size): return [(y,x) for y in range(size) for x in range(size)]
-
Shorten all the conditional checks that are comparing to booleans, by changing
== False
to usenot
before the variable, and removing== True
:### before ### if map_dict[location] == 'empty': print ("[ ]", end='') elif map_dict[location] == 'breadcrumb': print("[~]", end='') elif map_dict[location] == 'monster': if playing_game == False: print("[X]", end='') else: print("[ ]", end='') elif map_dict[location] == 'door': if playing_game == False: print("[@]", end='') else: print("[ ]", end='') elif map_dict[location] == 'player': print("[P]", end='') ### after ### if map_dict[location] == 'empty': print ("[ ]", end='') elif map_dict[location] == 'breadcrumb': print("[~]", end='') elif map_dict[location] == 'monster': if not playing_game: print("[X]", end='') else: print("[ ]", end='') elif map_dict[location] == 'door': if not playing_game: print("[@]", end='') else: print("[ ]", end='') elif map_dict[location] == 'player': print("[P]", end='')
### before ### if playing_game == False and not quit == True: ### after ### if not playing_game and not quit:
### before ### while playing_game == True: ### after ### while playing_game:
### before ### if moved == True: map_dict[old_player_loc] = 'breadcrumb' map_dict[player_loc] = 'player' else: render_map = False if player_loc == door_loc: playing_game = False map_dict[player_loc] = 'door' #to show location of door during final map render instead of P render_live_map() print("\nYOU FOUND THE DOOR! YOU WIN!") elif player_loc == monster_loc: playing_game = False map_dict[player_loc] = 'monster' #to show location of monster during final map render instead of P render_live_map() print("\nGAME OVER. THE MONSTER GOT YOU.") elif render_map == True: render_live_map() ### after ### if moved: map_dict[old_player_loc] = 'breadcrumb' map_dict[player_loc] = 'player' else: render_map = False if player_loc == door_loc: playing_game = False map_dict[player_loc] = 'door' #to show location of door during final map render instead of P render_live_map() print("\nYOU FOUND THE DOOR! YOU WIN!") elif player_loc == monster_loc: playing_game = False map_dict[player_loc] = 'monster' #to show location of monster during final map render instead of P render_live_map() print("\nGAME OVER. THE MONSTER GOT YOU.") elif render_map: render_live_map()
If you don't like the look of that code, because you feel it makes it less clear, then maybe you could rename the variables to suit:
### from ### if moved: map_dict[old_player_loc] = 'breadcrumb' map_dict[player_loc] = 'player' else: render_map = False ### to ### if has_moved: map_dict[old_player_loc] = 'breadcrumb' map_dict[player_loc] = 'player' else: should_render_map = False
-
In your main game loop, you're using
continue
within theif/elif/else
conditional statements. You don't need that, since it will exit theif/elif/else
after it completes the matching block, and loop again once it reaches the end of thewhile
block:### before ### while playing_game: move = input("\n> ").upper() render_map = True if move == "QUIT": playing_game = False quit = True render_live_map() print("\nOK. You quit. Bye...") elif move == "HELP": show_help() continue elif move == "LEGEND": show_legend() continue elif move == "MAP": render_live_map() continue elif move == "L" or move == "R" or move == "U" or move == "D": player_loc, old_player_loc, moved = move_player(move, player_loc) if moved: map_dict[old_player_loc] = 'breadcrumb' map_dict[player_loc] = 'player' else: render_map = False if player_loc == door_loc: playing_game = False map_dict[player_loc] = 'door' #to show location of door during final map render instead of P render_live_map() print("\nYOU FOUND THE DOOR! YOU WIN!") elif player_loc == monster_loc: playing_game = False map_dict[player_loc] = 'monster' #to show location of monster during final map render instead of P render_live_map() print("\nGAME OVER. THE MONSTER GOT YOU.") elif render_map: render_live_map() else: print("\n*** Invalid selection. Type help to see commands.") continue ### after ### while playing_game: move = input("\n> ").upper() render_map = True if move == "QUIT": playing_game = False quit = True render_live_map() print("\nOK. You quit. Bye...") elif move == "HELP": show_help() elif move == "LEGEND": show_legend() elif move == "MAP": render_live_map() elif move == "L" or move == "R" or move == "U" or move == "D": player_loc, old_player_loc, moved = move_player(move, player_loc) if moved: map_dict[old_player_loc] = 'breadcrumb' map_dict[player_loc] = 'player' else: render_map = False if player_loc == door_loc: playing_game = False map_dict[player_loc] = 'door' #to show location of door during final map render instead of P render_live_map() print("\nYOU FOUND THE DOOR! YOU WIN!") elif player_loc == monster_loc: playing_game = False map_dict[player_loc] = 'monster' #to show location of monster during final map render instead of P render_live_map() print("\nGAME OVER. THE MONSTER GOT YOU.") elif render_map: render_live_map() else: print("\n*** Invalid selection. Type help to see commands.")
-
There's a few places where you're using
print
just before you useinput
. You could just combine the two and only use theinput
, to save another function call. If you're worried about the argument toinput
being too large, move the prompt message to a variable, which would also make it easier to change later:### before ### print("\nARE YOU READY TO ENTER THE DUNGEON?\nType Y if yes, or anything else to quit.") if input("> ").upper() == 'Y': ### after ### if input("\nARE YOU READY TO ENTER THE DUNGEON?\nType Y if yes, or anything else to quit.\n> ").upper() == 'Y': ### or ### ready_message = "\nARE YOU READY TO ENTER THE DUNGEON?\nType Y if yes, or anything else to quit.\n> " if input(ready_message).upper() == 'Y':
-
If you're printing the same variable twice using the
format
function, you can use the index twice in the string you're formatting:### before ### print("\n{}*{} map successfuly generated.".format(map_size,map_size)) ### after ### print("\n{0}*{0} map successfully generated.".format(map_size))
It looks like you repeat a lot of code when setting up a new game. Maybe move that to a new function? It'd mean changing a whole lot of other stuff so that you're passing in parameters to your other functions for things like the
map_grid
andmap_dict
. This would be a much bigger change of course.It seems a bit strange to me to have the grid as a list and then a dict to hold information about particular locations within that grid and what is in those places. Why not have either a nested list with the inner lists containing the coordinates and what is in that cell, or use a dict within each cell, with, say keys
x
andy
for the coordinates andoccupant
or something to describe what is there. Then you would just always access the one list.You could change to a class-based system, which I think is covered in another Python course.
Abdillah Hasny
Courses Plus Student 3,886 Pointsthe greatest comment i've ever seen in treehouse forum (Y) well done bro great suggestion
Iain Simmons
Treehouse Moderator 32,305 PointsThanks Alfred E Bence! It was a good experience/practice to review someone else's code and think about how I might have done things differently.
Treehouse really has a great community, and that's the main thing that keeps bringing me back here from any other web development learning solution.
Thomas Katalenas
11,033 PointsThomas Katalenas
11,033 PointsWow!! That is so COOL! Thank you, it brings a tear to my eye..