Python Python Collections (2016, retired 2019) Dungeon Game Win or Lose

Joseph Yhu
Joseph Yhu
PHP Development Techdegree Student 22,845 Points

Here's my updated version of the dungeon game.

My changes:

  • The map is 10 x 10 instead of 4 x 4.
  • Not only is the player displayed on the map as P, but the monster, the door, and the shrine (explained below) are displayed also as M, D, and S respectively.
  • The player and monster hp are displayed as numbers, and their hp bars are displayed as Xs and Ys respectively which decrease every -20 hp.
  • If the player makes an invalid move, they lose 5 hp.
  • If the player encounters the monster, the game will randomly generate 0 or 1. If the number is 0, the player loses 10 hp. If the number is 1, the monster loses 10 hp unless killed.
  • If the player's hp reaches 0, the player loses. If the monster's hp reaches 0 the monster dies, and the player is prompted to get to the door.
  • The monster moves 1 tile in a random direction every 5 valid moves unless it has been killed.
  • The monster heals itself 5 hp every 10 valid moves unless killed or has maximum hp (100).
  • The player gets healed 5 hp if they move to the shrine unless they have maximum hp (100). If the monster moves to the shrine, the monster does not get healed.
  • If the monster reaches the door, the player loses automatically.
  • If the player reaches the door and the monster has not been killed, the player is prompted to kill the monster first.
  • If the player reaches the door and the monster has been killed, the player wins.
import random

import os

CELLS = [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (10, 0),
     (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1),
     (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), (7, 2), (8, 2), (9, 2), (10, 2),
     (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (5, 3), (6, 3), (7, 3), (8, 3), (9, 3), (10, 3),
     (0, 4), (1, 4), (2, 4), (3, 4), (4, 4), (5, 4), (6, 4), (7, 4), (8, 4), (9, 4), (10, 4),
     (0, 5), (1, 5), (2, 5), (3, 5), (4, 5), (5, 5), (6, 5), (7, 5), (8, 5), (9, 5), (10, 5),
     (0, 6), (1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 6), (7, 6), (8, 6), (9, 6), (10, 6),
     (0, 7), (1, 7), (2, 7), (3, 7), (4, 7), (5, 7), (6, 7), (7, 7), (8, 7), (9, 7), (10, 7),
     (0, 8), (1, 8), (2, 8), (3, 8), (4, 8), (5, 8), (6, 8), (7, 8), (8, 8), (9, 8), (10, 8),
     (0, 9), (1, 9), (2, 9), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9), (9, 9), (10, 9),
     (0, 10), (1, 10), (2, 10), (3, 10), (4, 10), (5, 10), (6, 10), (7, 10), (8, 10), (9, 10), (10, 10)]

def clear_screen():
    os.system('cls' if os.name == 'nt' else 'clear')

def get_locations():
    return random.sample(CELLS, 4)

def move_player(player, move):
    x, y = player
    if move == "LEFT":
        x -= 1
    if move == "RIGHT":
        x += 1
    if move == "UP":
        y -= 1
    if move == "DOWN":
        y += 1
    return x, y

def move_monster(monster, num):
    x, y = monster
    if num == 0:
        if x > 0:
            x -= 1
        elif x == 0:
            x += 1
    elif num == 1:
        if x < 10:
            x += 1
        elif x == 10:
            x -= 1
    elif num == 2:
        if y > 0:
            y -= 1
        elif y == 0:
            y += 1
    elif num == 3:
        if y < 10:
            y += 1
        elif y == 10:
            y -= 1
    return x, y

def get_moves(player):
    moves = ["LEFT", "RIGHT", "UP", "DOWN"]
    x, y = player
    if x == 0:
        moves.remove("LEFT")
    if x == 10:
        moves.remove("RIGHT")
    if y == 0:
        moves.remove("UP")
    if y == 10:
        moves.remove("DOWN")
    return moves

def draw_map(monster, door, player, shrine):
    print(" _" * 11)
    tile = "|{}"
    for cell in CELLS:
        x, y = cell
        if x < 10:
            line_end = ""
            if cell == player:
                output = tile.format("P")
            elif cell == monster:
                output = tile.format("M")
            elif cell == door:
                output = tile.format("D")
            elif cell == shrine:
                output = tile.format("S")
            else:
                output = tile.format("_")
        else:
            line_end = "\n"
            if cell == player:
                output = tile.format("P|")
            elif cell == monster:
                output = tile.format("M|")
            elif cell == door:
                output = tile.format("D|")
            elif cell == shrine:
                output = tile.format("S|")
            else:
                output = tile.format("_|")
        print(output, end = line_end)

def display_player_hp_bar(player_hp):
    if 80 < player_hp <= 100:
        print("X" * 5, player_hp)
    elif 60 < player_hp <= 80:
        print("X" * 4, player_hp)
    elif 40 < player_hp <= 60:
        print("X" * 3, player_hp)
    elif 20 < player_hp <= 40:
        print("X" * 2, player_hp)
    elif 0 < player_hp <= 20:
        print("X", player_hp)
    else:
        return player_hp

def display_monster_hp_bar(monster_hp):
    if 80 < monster_hp <= 100:
        print("Y" * 5, monster_hp)
    elif 60 < monster_hp <= 80:
        print("Y" * 4, monster_hp)
    elif 40 < monster_hp <= 60:
        print("Y" * 3, monster_hp)
    elif 20 < monster_hp <= 40:
        print("Y" * 2, monster_hp)
    elif 0 < monster_hp <= 20:
        print("Y", monster_hp)
    else:
        return monster_hp

def game_loop():
    monster, door, player, shrine = get_locations()
    num_moves = 0
    playing = True
    player_hp = 100
    monster_hp = 100
    monster_dead = False
    while playing:
        draw_map(monster, door, player, shrine)
        valid_moves = get_moves(player)
        print("You're currently in room {}.".format(player))
        print("You can move {}.".format(", ".join(valid_moves)))
        print("Enter QUIT to quit. Enter CLEAR to clear your screen.")
        if num_moves > 0 and num_moves % 5 == 0 and monster_dead == False:
            num = random.randint(0, 3)
            monster = move_monster(monster, num)
        if num_moves > 0 and num_moves % 10 == 0:
            if monster_dead == False and monster_hp < 100:
                print("\n ** The monster has healed itself 5 hp! **\n")
                monster_hp += 5
        if monster == door:
            print("\n ** The monster has escaped! You lose! **\n")
            playing = False
        if display_player_hp_bar(player_hp) == 0:
            print("\n ** You're dead! **\n")
            playing = False
        if display_monster_hp_bar(monster_hp) == 0:
            print("\n ** You've killed the monster! Now get to the door! **\n")
            monster_dead = True
        print("{} moves.".format(num_moves))
        move = input("> ")
        move = move.upper()
        if move == "QUIT":
            print("\n ** See you next time! **\n")
            break
        if move == "CLEAR":
            clear_screen()
            continue
        if move in valid_moves:
            player = move_player(player, move)
            num_moves += 1
            if player == monster:
                number = random.randint(0, 1)
                if number == 0:
                    player_hp -= 10
                    print("\n ** You've been damaged by the monster! You've lost 10 hp! **\n")
                elif number == 1 and monster_dead == False:
                    monster_hp -= 10
                    print("\n ** You've damaged the monster! It's lost 10 hp! **\n")
            if player == shrine and player_hp < 100:
                player_hp += 5
                print("\n ** You've been healed 5 hp! **\n")
            if player == door and monster_dead == True:
                print("\n ** You've escaped! Congratulations! **\n")
                playing = False
            elif player == door and monster_dead == False:
                print("\n ** You must kill the monster before you can escape! **\n")
        else:
            player_hp -= 5
            input("\n ** Walls are hard! Don't run into them! You've lost 5 hp! **\n")
    else:
        game_end()

def game_end():
    play_again = input("Play again? (y/n) ")
    while play_again.lower() != 'y' and play_again.lower() != 'n':
        print("Invalid input.")
        play_again = input("Play again? (y/n) ")
    if play_again.lower() == 'y':
        game_loop()

clear_screen()
print("Welcome to the dungeon!")
input("Press return to start!")
clear_screen()
game_loop()

4 Answers

Steven Parker
Steven Parker
176,543 Points

First off, great job! :+1: Very imaginative extensions.

Bugs (minor):

  • the player gets one more turn after being dead before "Play again?" is shown
  • an unrecognized input is taken as "running into a wall" even in the middle of the grid
  • the grid is actually 11x11 (instead of 10x10)

Play suggestions:

  • allow shorthands for movement (u, d, l, r)
  • conceal locations of door and shrine until each has been found
  • conceal monster location when it moves until it is found again
  • add "wait" (w) as a movement choice to stay in same place (such as in the shrine)

Coding suggestions:

  • use functions to condense the code
  • here's a compact dynamic method to initialize the cells:
CELLS = [(c%11, c//11) for c in range(11**2)]
Joseph Yhu
Joseph Yhu
PHP Development Techdegree Student 22,845 Points

Bugs (minor):

  • the player gets one more turn after being dead before "Play again?" is shown
  • an unrecognized input is taken as "running into a wall" even in the middle of the grid
  • the grid is actually 11x11 (instead of 10x10)
  • Fixed by putting break after playing == False.
  • Removed taking damage by going past the wall feature; now the player cannot move past the wall (a message is displayed saying that a wall is blocking the player's way when they try to move past the wall.). Also, if the player enters any command other than c, q, l, r, u, d, or w, an "invalid move" message is displayed.
  • Fixed by using your code and replacing 11s with 10s (Thanks for the code.). Originally fixed by removing tuples with 10s (Let me know if you don't want me to use your code.).

Play suggestions:

  • allow shorthands for movement (u, d, l, r)
  • conceal locations of door and shrine until each has been found
  • conceal monster location when it moves until it is found again
  • add "wait" (w) as a movement choice to stay in same place (such as in the shrine)
  • Replaced left, right, up, and down commands with l, r, u, and d respectively. Replaced quit and clear commands with q and c respectively.
  • Created a hard difficulty mode where everything except the player is hidden. If the door or the shrine is found, their locations are displayed for the rest of the game. If the monster is found, its current location is displayed until it moves.
  • Added the (w)ait command.
Steven Parker
Steven Parker
176,543 Points

Mods looking good, but I was thinking you could display the shrine and door on the map (instead/with the coordinates), and use a single function for both difficulty levels:

                if cell == player:
                    output = tile.format("P")
                elif cell == door and (difficulty == "N" or door_found):
                    output = tile.format("D")
                elif cell == shrine and (difficulty == "N" or shrine_found):
                    output = tile.format("S")
                else:
                    output = tile.format("_")

Now you have plenty opportunities to practice making code "DRY" and compact. For example, "move_player" is 78 lines, but could be condensed to about 14:

def move_player(player, move):
    x, y = player
    if move in ["W","WAIT"]:
        return x, y
    if move in ["L","LEFT"] and x > 0:
        return x - 1, y
    if move in ["R","RIGHT"] and x < 9:
        return x + 1, y
    if move in ["U","UP"] and y > 0:
        return x, y - 1
    if move in ["D","DOWN"] and y < 9:
        return x, y + 1
    print("\n ** A wall is blocking your way! **\n")
    return x, y

And I also made it work with both the full and shorthand move names, but that needs a little bit of support elsewhere.

Henrik Christensen
Henrik Christensen
Python Web Development Techdegree Student 38,297 Points

Look great! Very impressive! :-)

Steven Parker already said it all :-)

I have a minor suggestion for a change (nitty-gritty thing).

I see that you sometimes says i.e. if monster_dead == False and monster_dead == True - this can be written as if not monster_dead and if monster_dead :-)

Joseph Yhu
Joseph Yhu
PHP Development Techdegree Student 22,845 Points

Version 2.2: Added an easy difficulty mode.

  • Easy difficulty: Everything except the trap is shown on the map. The monster doesn't move (meaning you won't lose automatically since the monster can't get to the door) and doesn't heal itself. The trap does 5 damage.
  • Normal difficulty: Everything except the trap is shown on the map. The monster moves and heals itself. The trap does 10 damage.
  • Hard difficulty: Only the player is shown on the map. The monster moves and heals itself. Finding the shrine or the door will reveal their coordinates to the player for the rest of the game; finding the monster will reveal its current coordinates until it moves. The trap does 20 damage.

Version 2.1: Added a trap which decreases the player's hp by 20 every time its activated (i.e. every time the player stands on it). Always hidden regardless of difficulty.

Version 2.0: Read my comment above for the changes.

import random

import os

CELLS = [(c%10, c//10) for c in range(10**2)]

def clear_screen():
    os.system('cls' if os.name == 'nt' else 'clear')

def get_locations():
    return random.sample(CELLS, 5)

def move_player(player, move):
    x, y = player
    if move == "W":
        x, y = x, y
    if 0 < x < 9 and 0 < y < 9:
        if move == "L":
            x -= 1
        if move == "R":
            x += 1
        if move == "U":
            y -= 1
        if move == "D":
            y += 1
    elif x == 0 and 0 < y < 9:
        if move == "L":
            print("\n ** A wall is blocking your way! **\n")
        if move == "R":
            x += 1
        if move == "U":
            y -= 1
        if move == "D":
            y += 1
    elif x == 9 and 0 < y < 9:
        if move == "L":
            x -= 1
        if move == "R":
            print("\n ** A wall is blocking your way! **\n")
        if move == "U":
            y -= 1
        if move == "D":
            y += 1
    elif 0 < x < 9 and y == 0:
        if move == "L":
            x -= 1
        if move == "R":
            x += 1
        if move == "U":
            print("\n ** A wall is blocking your way! **\n")
        if move == "D":
            y += 1
    elif 0 < x < 9 and y == 9:
        if move == "L":
            x -= 1
        if move == "R":
            x += 1
        if move == "U":
            y -= 1
        if move == "D":
            print("\n ** A wall is blocking your way! **\n")
    elif x == 0 and y == 0:
        if move == "L" or move == "U":
            print("\n ** A wall is blocking your way! **\n")
        if move == "R":
            x += 1
        if move == "D":
            y += 1
    elif x == 9 and y == 9:
        if move == "R" or move == "D":
            print("\n ** A wall is blocking your way! **\n")
        if move == "L":
            x -= 1
        if move == "U":
            y -= 1
    elif x == 0 and y == 9:
        if move == "L" or move == "D":
            print("\n ** A wall is blocking your way! **\n")
        if move == "R":
            x += 1
        if move == "U":
            y -= 1
    elif x == 9 and y == 0:
        if move == "R" or move == "U":
            print("\n ** A wall is blocking your way! **\n")
        if move == "L":
            x -= 1
        if move == "D":
            y += 1
    return x, y

def move_monster(monster, num):
    x, y = monster
    if num == 0:
        if x > 0:
            x -= 1
        elif x == 0:
            x += 1
    elif num == 1:
        if x < 9:
            x += 1
        elif x == 9:
            x -= 1
    elif num == 2:
        if y > 0:
            y -= 1
        elif y == 0:
            y += 1
    elif num == 3:
        if y < 9:
            y += 1
        elif y == 9:
            y -= 1
    return x, y

def get_moves(player):
    moves = ["L", "R", "U", "D", "W"]
    return moves

def draw_map(monster, door, player, shrine):
    print(" _" * 10)
    tile = "|{}"
    for cell in CELLS:
        x, y = cell
        if x < 9:
            line_end = ""
            if cell == player:
                output = tile.format("P")
            elif cell == monster:
                output = tile.format("M")
            elif cell == door:
                output = tile.format("D")
            elif cell == shrine:
                output = tile.format("S")
            else:
                output = tile.format("_")
        else:
            line_end = "\n"
            if cell == player:
                output = tile.format("P|")
            elif cell == monster:
                output = tile.format("M|")
            elif cell == door:
                output = tile.format("D|")
            elif cell == shrine:
                output = tile.format("S|")
            else:
                output = tile.format("_|")
        print(output, end = line_end)

def draw_map_hard_difficulty(player):
        print(" _" * 10)
        tile = "|{}"
        for cell in CELLS:
            x, y = cell
            if x < 9:
                line_end = ""
                if cell == player:
                    output = tile.format("P")
                else:
                    output = tile.format("_")
            else:
                line_end = "\n"
                if cell == player:
                    output = tile.format("P|")
                else:
                    output = tile.format("_|")
            print(output, end = line_end)

def display_player_hp_bar(player_hp):
    if 80 < player_hp <= 100:
        print("Player:", "X" * 5, player_hp)
    elif 60 < player_hp <= 80:
        print("Player:", "X" * 4, player_hp)
    elif 40 < player_hp <= 60:
        print("Player:", "X" * 3, player_hp)
    elif 20 < player_hp <= 40:
        print("Player:", "X" * 2, player_hp)
    elif 0 < player_hp <= 20:
        print("Player:", "X", player_hp)
    return player_hp

def display_monster_hp_bar(monster_hp):
    if 80 < monster_hp <= 100:
        print("Monster:", "Y" * 5, monster_hp)
    elif 60 < monster_hp <= 80:
        print("Monster:", "Y" * 4, monster_hp)
    elif 40 < monster_hp <= 60:
        print("Monster:", "Y" * 3, monster_hp)
    elif 20 < monster_hp <= 40:
        print("Monster:", "Y" * 2, monster_hp)
    elif 0 < monster_hp <= 20:
        print("Monster:", "Y", monster_hp)
    return monster_hp

def display_message(player):
    x, y = player
    if 0 <= x <= 9 and 0 <= y <= 9:
        print("You're currently in room {}.".format(player))
    if 0 < x < 9 and 0 < y < 9:
        print("You can move {}.".format(", ".join(["(L)EFT", "(R)IGHT", "(U)P", "(D)OWN"])), "or you can (W)AIT.")
    elif x == 0 and 0 < y < 9:
        print("You can move {}.".format(", ".join(["(R)IGHT", "(U)P", "(D)OWN"])), "or you can (W)AIT.")
    elif x == 9 and 0 < y < 9:
        print("You can move {}.".format(", ".join(["(L)EFT", "(U)P", "(D)OWN"])), "or you can (W)AIT.")
    elif 0 < x < 9 and y == 0:
        print("You can move {}.".format(", ".join(["(L)EFT", "(R)IGHT", "(D)OWN"])), "or you can (W)AIT.")
    elif 0 < x < 9 and y == 9:
        print("You can move {}.".format(", ".join(["(L)EFT", "(R)IGHT", "(U)P"])), "or you can (W)AIT.")
    elif 0 <= x < 9 and 0 <= y < 9:
        print("You can move {}.".format(", ".join(["(R)IGHT", "(D)OWN"])), "or you can (W)AIT.")
    elif 0 <= x < 9 and 0 < y <= 9:
        print("You can move {}.".format(", ".join(["(R)IGHT", "(U)P"])), "or you can (W)AIT.")
    elif 0 < x <= 9 and 0 <= y < 9:
        print("You can move {}.".format(", ".join(["(L)EFT", "(D)OWN"])), "or you can (W)AIT.")
    elif 0 < x <= 9 and 0 < y <= 9:
        print("You can move {}.".format(", ".join(["(L)EFT", "(U)P"])), "or you can (W)AIT.")
    print("Enter Q to quit. Enter C to clear your screen.")

def game_loop():
    monster, door, player, shrine, trap = get_locations()
    num_moves = 0
    player_hp = 100
    monster_hp = 100
    monster_alive = True
    playing = True
    shrine_found = False
    door_found = False
    monster_found = False
    difficulty = input("Choose difficulty: (E)asy (N)ormal or (H)ard. ")
    while difficulty.upper() != "N" and difficulty.upper() != "H" and difficulty.upper() != "E":
        print("Invalid input.")
        difficulty = input("Choose difficulty: (E)asy, (N)ormal or (H)ard. ")
    difficulty = difficulty.upper()
    while playing:
        if difficulty == "E" or difficulty == "N":
            draw_map(monster, door, player, shrine)
        elif difficulty == "H":
            draw_map_hard_difficulty(player)
            if shrine_found:
                print("The shrine is located at {}.".format(shrine))
            if door_found:
                print("The door is located at {}.".format(door))
            if monster_found:
                print("The monster is located at {}.".format(monster))
        valid_moves = get_moves(player)
        display_message(player)
        if difficulty != "E":
            if num_moves > 0 and num_moves % 5 == 0 and monster_alive:
                num = random.randint(0, 3)
                print("\n ** The monster has moved! **\n")
                monster = move_monster(monster, num)
                monster_found = False
            if num_moves > 0 and num_moves % 10 == 0:
                if monster_alive and monster_hp < 100:
                    monster_hp += 5
                    print("\n ** The monster has healed itself 5 hp! **\n")
        if monster == door:
            print("\n ** The monster has escaped! You lose! **\n")
            playing = False
            break
        if display_player_hp_bar(player_hp) <= 0:
            print("\n ** You're dead! **\n")
            playing = False
            break
        if display_monster_hp_bar(monster_hp) <= 0:
            print("\n ** You've killed the monster! Now get to the door! **\n")
            monster_alive = False
        print("{} moves.".format(num_moves))
        move = input("> ")
        move = move.upper()
        if move == "Q":
            print("\n ** See you next time! **\n")
            break
        if move == "C":
            clear_screen()
            continue
        if move in valid_moves:
            player = move_player(player, move)
            num_moves += 1
            if player == monster:
                monster_found = True
                number = random.randint(0, 1)
                if number == 0:
                    player_hp -= 10
                    print("\n ** You've been damaged by the monster! You've lost 10 hp! **\n")
                elif number == 1 and monster_alive:
                    monster_hp -= 10
                    print("\n ** You've damaged the monster! It's lost 10 hp! **\n")
            elif player == shrine:
                shrine_found = True
                if player_hp < 100:
                    player_hp += 5
                    print("\n ** You've been healed 5 hp! **\n")
                else:
                    print("\n ** You're already at full hp! **\n")
            elif player == trap:
                if difficulty == "E":
                    player_hp -= 5
                    print("\n ** You've activated a trap! You've lost 5 hp! **\n")
                elif difficulty == "N":
                    player_hp -= 10
                    print("\n ** You've activated a trap! You've lost 10 hp! **\n")
                elif difficulty == "H"
                    player_hp -= 20
                    print("\n ** You've activated a trap! You've lost 20 hp! **\n")
            elif player == door:
                door_found = True
                if not monster_alive:
                    print("\n ** You've escaped! Congratulations! **\n")
                    playing = False
                    break
                elif monster_alive:
                    print("\n ** You must kill the monster before you can escape! **\n")
        else:
            print("Invalid move.")
    if playing == False:
        game_end()

def game_end():
    play_again = input("Play again? (y/n) ")
    while play_again.lower() != 'y' and play_again.lower() != 'n':
        print("Invalid input.")
        play_again = input("Play again? (y/n) ")
    if play_again.lower() == 'y':
        game_loop()

clear_screen()
print("Welcome to the dungeon!")
input("Press return to start!")
clear_screen()
game_loop()

Am I allowed to post the game to GitHub?

Steven Parker
Steven Parker
176,543 Points

I added a comment to my answer. And I don't see why posting this here or Github would be significantly different.

Joseph Yhu
Joseph Yhu
PHP Development Techdegree Student 22,845 Points

This originally wasn't my code; I've just made some changes. So I was wondering if I'm allowed to create a GitHub repository of it and add the link to LinkedIn as a project like I had with the number guessing project.

Mods looking good, but I was thinking you could display the shrine and door on the map (instead/with the coordinates), and use a single function for both difficulty levels: Now you have plenty opportunities to practice making code "DRY" and compact. For example, "move_player" is 78 lines, but could be condensed to about 14:

Thanks, I've changed them. I've also made the display_message function more compact.

And I also made it work with both the full and shorthand move names, but that needs a little bit of support elsewhere.

I've decided not to add full move names as valid moves. The player should know, e.g., what the "L" move does even if he doesn't know that L stands for LEFT, the game clearly tells him that it does with the display_message function. Also who would use the full move name if a shorthand move name is available?

import random

import os

CELLS = [(c%10, c//10) for c in range(10**2)]

def clear_screen():
    os.system('cls' if os.name == 'nt' else 'clear')

def get_locations():
    return random.sample(CELLS, 5)

def move_player(player, move):
    x, y = player
    if move == "W":
        x, y = x, y
    elif move == "L" and x > 0:
        x -= 1
    elif move == "R" and x < 9:
        x += 1
    elif move == "U" and y > 0:
        y -= 1
    elif move == "D" and y < 9:
        y += 1
    else:
        print("\n ** A wall is blocking your way! ** \n")
    return x, y

def move_monster(monster, num):
    x, y = monster
    if num == 0:
        if x > 0:
            x -= 1
        elif x == 0:
            x += 1
    elif num == 1:
        if x < 9:
            x += 1
        elif x == 9:
            x -= 1
    elif num == 2:
        if y > 0:
            y -= 1
        elif y == 0:
            y += 1
    elif num == 3:
        if y < 9:
            y += 1
        elif y == 9:
            y -= 1
    return x, y

def get_moves(player):
    moves = ["L", "R", "U", "D", "W"]
    return moves

def draw_map(monster, door, player, shrine, difficulty, monster_found, door_found, shrine_found):
    print(" _" * 10)
    tile = "|{}"
    for cell in CELLS:
        x, y = cell
        if x < 9:
            line_end = ""
            if cell == player:
                output = tile.format("P")
            elif cell == monster and (difficulty != "H" or monster_found):
                output = tile.format("M")
            elif cell == door and (difficulty != "H" or door_found):
                output = tile.format("D")
            elif cell == shrine and (difficulty != "H" or shrine_found):
                output = tile.format("S")
            else:
                output = tile.format("_")
        else:
            line_end = "\n"
            if cell == player:
                output = tile.format("P|")
            elif cell == monster and (difficulty != "H" or monster_found):
                output = tile.format("M|")
            elif cell == door and (difficulty != "H" or door_found):
                output = tile.format("D|")
            elif cell == shrine and (difficulty != "H" or shrine_found):
                output = tile.format("S|")
            else:
                output = tile.format("_|")
        print(output, end = line_end)

def display_player_hp_bar(player_hp):
    if 80 < player_hp <= 100:
        print("Player:", "X" * 5, player_hp)
    elif 60 < player_hp <= 80:
        print("Player:", "X" * 4, player_hp)
    elif 40 < player_hp <= 60:
        print("Player:", "X" * 3, player_hp)
    elif 20 < player_hp <= 40:
        print("Player:", "X" * 2, player_hp)
    elif 0 < player_hp <= 20:
        print("Player:", "X", player_hp)
    return player_hp

def display_monster_hp_bar(monster_hp):
    if 80 < monster_hp <= 100:
        print("Monster:", "Y" * 5, monster_hp)
    elif 60 < monster_hp <= 80:
        print("Monster:", "Y" * 4, monster_hp)
    elif 40 < monster_hp <= 60:
        print("Monster:", "Y" * 3, monster_hp)
    elif 20 < monster_hp <= 40:
        print("Monster:", "Y" * 2, monster_hp)
    elif 0 < monster_hp <= 20:
        print("Monster:", "Y", monster_hp)
    return monster_hp

def display_message(player):
    moves = ["(L)EFT", "(R)IGHT", "(U)P", "(D)OWN"]
    x, y = player
    if 0 <= x <= 9 and 0 <= y <= 9:
        print("You're currently in room {}.".format(player))
    if x == 0:
        moves.remove("(L)EFT")
    if x == 9:
        moves.remove("(R)IGHT")
    if y == 0:
        moves.remove("(U)P")
    if y == 9:
        moves.remove("(D)OWN")
    print("You can move {}.".format(", ".join(moves)), "or you can (W)AIT.")
    print("Enter Q to quit. Enter C to clear your screen.")

def game_loop():
    monster, door, player, shrine, trap = get_locations()
    num_moves = 0
    player_hp = 100
    monster_hp = 100
    monster_alive = True
    playing = True
    shrine_found = False
    door_found = False
    monster_found = False
    difficulty = input("Choose difficulty: (E)asy (N)ormal, or (H)ard. ")
    while difficulty.upper() != "E" and difficulty.upper() != "N" and difficulty.upper() != "H":
        print("Invalid input.")
        difficulty = input("Choose difficulty: (E)asy, (N)ormal, or (H)ard. ")
    difficulty = difficulty.upper()
    while playing:
        draw_map(monster, door, player, shrine, difficulty, monster_found, door_found, shrine_found)
        valid_moves = get_moves(player)
        display_message(player)
        if difficulty != "E":
            if num_moves > 0 and num_moves % 5 == 0 and monster_alive:
                num = random.randint(0, 3)
                print("\n ** The monster has moved! **\n")
                monster = move_monster(monster, num)
                monster_found = False
            if num_moves > 0 and num_moves % 10 == 0:
                if monster_alive and monster_hp < 100:
                    monster_hp += 5
                    print("\n ** The monster has healed itself 5 hp! **\n")
        if monster == door:
            print("\n ** The monster has escaped! You lose! **\n")
            playing = False
            break
        if display_player_hp_bar(player_hp) <= 0:
            print("\n ** You're dead! **\n")
            playing = False
            break
        if display_monster_hp_bar(monster_hp) <= 0:
            print("\n ** You've killed the monster! Now get to the door! **\n")
            monster_alive = False
        print("{} moves.".format(num_moves))
        move = input("> ")
        move = move.upper()
        if move == "Q":
            print("\n ** See you next time! **\n")
            break
        if move == "C":
            clear_screen()
            continue
        if move in valid_moves:
            player = move_player(player, move)
            num_moves += 1
            if player == monster:
                monster_found = True
                number = random.randint(0, 1)
                if number == 0:
                    player_hp -= 10
                    print("\n ** You've been damaged by the monster! You've lost 10 hp! **\n")
                elif number == 1 and monster_alive:
                    monster_hp -= 10
                    print("\n ** You've damaged the monster! It's lost 10 hp! **\n")
            elif player == shrine:
                shrine_found = True
                if player_hp < 100:
                    player_hp += 5
                    print("\n ** You've been healed 5 hp! **\n")
                else:
                    print("\n ** You're already at full hp! **\n")
            elif player == trap:
                if difficulty == "E":
                    player_hp -= 5
                    print("\n ** You've activated a trap! You've lost 5 hp! **\n")
                elif difficulty == "N":
                    player_hp -= 10
                    print("\n ** You've activated a trap! You've lost 10 hp! **\n")
                elif difficulty == "H":
                    player_hp -= 20
                    print("\n ** You've activated a trap! You've lost 20 hp! **\n")
            elif player == door:
                door_found = True
                if not monster_alive:
                    print("\n ** You've escaped! Congratulations! **\n")
                    playing = False
                    break
                elif monster_alive:
                    print("\n ** You must kill the monster before you can escape! **\n")
        else:
            print("Invalid move.")
    if playing == False:
        game_end()

def game_end():
    play_again = input("Play again? (y/n) ")
    while play_again.lower() != 'y' and play_again.lower() != 'n':
        print("Invalid input.")
        play_again = input("Play again? (y/n) ")
    if play_again.lower() == 'y':
        game_loop()

clear_screen()
print("Welcome to the dungeon!")
input("Press return to start!")
clear_screen()
game_loop()
Steven Parker
Steven Parker
176,543 Points

I doubt TH has any proprietary interest in the original code, they'd probably encourage you to play with it, plus you've changed it substantially. But you could always contact Support to be certain.

And I intended the display_message function just as an example. There's still plenty opportunities to do similar reductions in other functions. Give it a shot, and see how much smaller you can make it!

I get your point about the full move names, I also did that mostly as a technique example.

And this line looked a bit odd: "x, y = x, y". You might consider replacing that with "pass".

But overall, you're really getting the hang of programming. Keep up the good work!