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 trial
David Dzsotjan
6,405 PointsDungeon of the Killer Bunnies - the Dungeon game re-imagined
Here's a juiced up version of the original Dungeon game by Kenneth, re-imagined with the killer bunny from the Knights of the Holy Grail. You can specify the dimensions of the dungeon and the numbers of the bunnies who move around in a random fashion. If you meet one, you have to fight - using the Holy Hand Grenade of Antioch, of course! The hit/miss ratio depends on the number of rabbits in the room with you. If you survived the bloody struggle, you can continue your way to find the Grail.
import random
import os
import math
def clear():
if os.name == 'nt':
os.system('cls')
else:
os.system('clear')
def make_dungeon(Xmax, Ymax):
CELLS = []
for x in list(range(Xmax)):
for y in list(range(Ymax)):
CELLS.append((x, y))
return CELLS
# placing player, door and blood rabbits randomly
def get_positions(CELLS, monster_number, player_health):
# 'been' : rooms where the player has been
player = {'position': random.choice(CELLS), 'been': [], 'health': player_health}
door = random.choice(CELLS)
if player['position'] == door:
return get_positions(CELLS, monster_number, player_health)
# placing the monsters (rabbits)
pos_monster = list(range(monster_number))
for number in list(range(monster_number)):
# accept generated position only if it is different from player and door!
while True:
pos_monster[number] = (random.choice(CELLS))
if pos_monster[number] != player['position'] and pos_monster[number] != door:
break
# list of roomw where the player has been
(x, y) = player['position']
player['been'] = [(x, y)]
return [player, door, pos_monster]
# determine the possible moves at a given position
def get_moves(position, Xmax, Ymax):
moves = ['LEFT', 'RIGHT', 'UP', 'DOWN']
if position[1] == 0:
moves.remove('LEFT')
if position[1] == Ymax - 1:
moves.remove('RIGHT')
if position[0] == 0:
moves.remove('UP')
if position[0] == Xmax - 1:
moves.remove('DOWN')
return moves
def move_player(player, move):
(x, y) = player
if move == 'LEFT':
y -= 1
elif move == 'RIGHT':
y += 1
elif move == 'UP':
x -= 1
elif move == 'DOWN':
x += 1
return (x, y)
def move_monster(monster, Xmax, Ymax):
(x, y) = monster
moves = get_moves(monster, Xmax, Ymax)
move = random.choice(moves)
if move == 'LEFT':
y -= 1
elif move == 'RIGHT':
y += 1
elif move == 'UP':
x -= 1
elif move == 'DOWN':
x += 1
return (x, y)
# Getting visible rooms, to be able to draw fog of war
def fog_of_war(player, CELLS):
visible_rooms = []
(x, y) = player['position']
ind_list = [-1, 0, 1]
for indx in ind_list:
for indy in ind_list:
if (x + indx, y + indy) in CELLS:
visible_rooms.append((x + indx, y + indy))
return visible_rooms
# Drawing map with fog of war: only the nearest rooms are visible to the player
def draw_map(player, door, pos_monster, CELLS, visible_rooms, Ymax, show_door):
# legend explaining symbole
print("\n X: You, R: Killer rabbits, O: The Holy Grail, #: Breadcrumbs, ~: Darkness")
print(' _' * Ymax)
tile = '|{}'
for cell in enumerate(CELLS):
# if it's a room on the right hand edge
if (cell[0] + 1) % Ymax == 0:
# is the cell within sight range?
if cell[1] in visible_rooms:
# if player is in the room
if cell[1] == player['position']:
if cell[1] in pos_monster:
print(tile.format('*|'))
else:
print(tile.format('X|'))
# if bunny in the room, but no player
elif cell[1] in pos_monster:
print(tile.format('R|'))
# if the Grail is in the room
elif cell[1] == door:
show_door = True # we turn it on, s othat we see the Grail later too
print(tile.format('O|'))
# if we've been here before
elif cell[1] in player['been']:
print(tile.format('#|'))
else:
print(tile.format('_|'))
# if it's in darkness
else:
if cell[1] == door and show_door:
print(tile.format('O|'))
elif cell[1] in player['been']:
print(tile.format('#|'))
else:
print(tile.format('~|'))
else:
if cell[1] in visible_rooms:
if cell[1] == player['position']:
if cell[1] in pos_monster:
print(tile.format('*'), end='')
else:
print(tile.format('X'), end='')
elif cell[1] in pos_monster:
print(tile.format('R'), end='')
elif cell[1] == door:
show_door = True
print(tile.format('O'), end='')
elif cell[1] in player['been']:
print(tile.format('#'), end='')
else:
print(tile.format('_'), end='')
# if it's in darkness
else:
if cell[1] == door and show_door:
print(tile.format('O'), end='')
elif cell[1] in player['been']:
print(tile.format('#'), end='')
else:
print(tile.format('~'), end='')
return show_door
# Fighting the beast(s)
def fight(player, pos_monster):
monster_health = 2
# the number of beasts in the room influences the hit/miss ratio
monster_number = 0
for beast in pos_monster:
if beast == player['position']:
monster_number += 1
# chance to hit * 100
hit_ratio = 100 / (monster_number + 1)
# commentary
remark2 = ''
# the fight goes on until any of them die
while monster_health > 0 and player['health'] > 0:
clear()
print("\nThey've found you! Your grip tightens on the Holy Hand Grenade of Antioch.\n")
if monster_number == 1:
print("1 killer bunny in the room.\n")
print("Your chance of landing a hit: {}%".format(hit_ratio))
print("The probability that you get hurt: {}%".format(100 - hit_ratio))
else:
print("{} bunnies in the room, you attack the closest one.\n".format(monster_number))
print("Your chance to land a hit: {}%".format(round(hit_ratio, 2)))
print("The probability that you get hurt: {}%".format(round(100 - hit_ratio), 2))
print('\n')
print("Player Health: " + '=' * player['health'])
print("Rabbit health: " + '=' * monster_health)
print('\n')
print(remark2 + '\n')
input("Hit Enter to launch the Holy Hand Grenade! 1, 2, 3... ")
# BATTLE
if hit_ratio > random.randint(0, 100):
monster_health -= 1
remark2 = "A fine hit!"
else:
player['health'] -= 1
remark2 = "Ouch! You missed and it managed to sneak a nasty bite!"
if not monster_health:
clear()
if monster_number == 1:
input("\nThe killer bunny is dead and you're safe once again. \nPress Enter")
else:
plural = ''
if monster_number == 3:
plural = 's'
input(
"The lifeless carcass makes the other{} recoil and you use this time to decide which way to run. \nPress Enter".format(
plural))
return True
else:
return False
# You have to escape the rabbits and get to the door. If you meet a rabbit, you fight to the death.
# You can specify the dimensions of the dungeon, and the number of rabbits. Good luck!
# Your sight is limited to the room you are in and the adjacent rooms.
# If the Grail is close (your distance from it is less than 1/4 of the longest side of the dungeon), it is shown on the map
def dungeon_game():
clear()
print("\nWelcome to the Killer Bunny Dungeon!\n")
print("Evade the bloodthirsty rabbits and get to the Holy Grail beyond them!")
input("However, if you meet one (or more), you'll have to stand your ground and fight!\n")
print("The map aids you by showing your position and the rooms where you've")
print("already been, thanks to the breadcrumbs you scatter around.\n")
print("Also, you can see what's going on in the rooms next to yours, but not further!")
print("It's dark and foggy down here... and full of savage rabbits!\n")
# getting the parameters of the dungeon
while True:
try:
Ymax = int(input("How wide should the dungeon be? "))
Xmax = int(input("How deep? "))
monster_number = int(input("And how many bunnies? "))
except ValueError:
print("That is not an integer!")
continue
else:
break
player_health = 5
CELLS = make_dungeon(Xmax, Ymax)
(player, door, pos_monster) = get_positions(CELLS, monster_number, player_health)
# rooms adjacent to player position
visible_rooms = fog_of_war(player, CELLS)
# if it's True, the Grail is shown on the map
show_door = False
remark = ''
while True:
# draw dungeon, with player and monster positions
clear()
show_door = draw_map(player, door, pos_monster, CELLS, visible_rooms, Ymax, show_door)
print('\n' + remark + '\n')
# move player
moves = get_moves(player['position'], Xmax, Ymax)
print("You can move {}, or 'WAIT'.".format(moves))
print("Enter QUIT to quit.")
move = input("> ")
move = move.upper()
if move == 'QUIT':
break
if move in moves:
player['position'] = move_player(player['position'], move)
# if player hasn't been here yet, append to 'been'
if not player['position'] in player['been']:
player['been'].append(player['position'])
remark = ''
# updating list of adjacent rooma
visible_rooms = fog_of_war(player, CELLS)
# if the Grail is close, show it on the map
if math.sqrt(math.pow(player['position'][0] - door[0], 2) + math.pow(player['position'][1] - door[1], 2)) < max(Xmax, Ymax) / 4:
show_door = True
remark = 'You feel a reassuring presence... The Grail is near!'
# player position doesn't change, only monsters move
elif move == 'WAIT':
remark = "You stay still and listen as they hop around in the dark... creepy..."
else:
remark = "** Walls are hard, stop walking into them! **"
continue
# move monster(s)
for number in enumerate(pos_monster):
pos_monster[number[0]] = move_monster(pos_monster[number[0]], Xmax, Ymax)
if player['position'] == door:
clear()
print("\nYou've found the Holy Grail! You are saved!!")
break
elif player['position'] in pos_monster:
# fight them and see if you stay alive
alive = fight(player, pos_monster)
if alive:
# kill 1 monster that was in the same room with player
pos_monster.remove(player['position'])
remark = "Quickly, before there's more of them!!"
continue
else:
clear()
print("Oh no... You became rabbit food...")
break
# play again?
again = input("\nWould you like to play again?(Y/n) ")
if again.lower() == 'n':
print("Bye-bye!")
else:
dungeon_game()
dungeon_game()
2 Answers
Iain Simmons
Treehouse Moderator 32,305 PointsAm I meant to be able to see where everything is? It makes it fairly easy... Though you should probably start with a legend/key to tell the player what each of the symbols mean.
Great idea with the health bars! And awesome theme. :)
Oh and I just got an error:
How wide should the dungeon be? 7
How deep? 7
And how many bunnies? 5
Traceback (most recent call last):
File "bunnies.py", line 255, in <module>
dungeon_game()
File "bunnies.py", line 252, in dungeon_game
dungeon_game()
File "bunnies.py", line 252, in dungeon_game
dungeon_game()
File "bunnies.py", line 252, in dungeon_game
dungeon_game()
File "bunnies.py", line 252, in dungeon_game
dungeon_game()
File "bunnies.py", line 252, in dungeon_game
dungeon_game()
File "bunnies.py", line 197, in dungeon_game
(player, door, pos_monster) = get_positions(CELLS, monster_number, player_health)
File "bunnies.py", line 27, in get_positions
return get_positions(CELLS, monster_number)
TypeError: get_positions() missing 1 required positional argument: 'player_health'
Looks like you missed an argument on line 27:
return get_positions(CELLS, monster_number)
Should probably be:
return get_positions(CELLS, monster_number, player_health)
David Dzsotjan
6,405 PointsIain Simmons Hi Iain, thanks so much for taking a look at the game and noticing the cracks in the code! :) And thanks for the suggestions! I have taken care of the error message, and rounded the hit/miss chances to 2 digits as suggested. Also, instead of showing the whole dungeon, I introduced a fog-of-war, so that only the adjacent rooms are visible (I think it makes it a bit more exciting if you see that there are monsters around, but you can't see all of them). Additionally, I added the feature of giving a hint about the position of the Grail, once you get close enough to it - so that it won't get so very boring to randomly walk around. Ah, and there are also breadcrumbs, showing the rooms where you've been :) I updated the code in the original message! Wishing you a nice day! :))
Iain Simmons
Treehouse Moderator 32,305 PointsIain Simmons
Treehouse Moderator 32,305 PointsOh and you might want to round off your probabilities to a couple of decimal places or something :)