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
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:
playing = False
if display_monster_hp_bar(monster_hp) == 0:
print("\n ** You've killed the monster! Now get to the door! **\n")
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!")
clear_screen()
game_loop()
```

First off, great job! 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)]
```

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.

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.

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` :-)

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:
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!")
clear_screen()
game_loop()
```

Am I allowed to post the game to GitHub?

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

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:
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!")
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!
And this line looked a bit odd: "`x, y = x, y`". You might consider replacing that with "`pass`".