My TrapRoomTile is now throwing an error TrapRoomTile has no attribute trap. I declare self.trap = items.Pitfall() its written exactly how my Enemy tiles are written but I keep getting the error. I imagine I'm overlooking something small like usual so any help would be appreciated
import enemies
import npc
import random
import time
import items
class MapTile:
def __init__(self, x, y):
self.x = x
self.y = y
def intro_text(self):
raise NotImplementedError("Create a subclass instead!")
def modify_player(self, player):
pass
class StartTile(MapTile):
def intro_text(self):
return """
\n You find yourself in a cave with a flickering torch on the wall.
You can make out four paths, each equally as dark and foreboding.
"""
time.sleep(0.5)
class EnemyTile(MapTile):
'''
def __init__(self, x, y):
r = random.random()
if r < 0.50:
self.enemy = enemies.GiantSpider()
self.alive_text = "\nA giant spider jumps down from " \
"its web in front of you!"
self.dead_text = "\nThe corpse of a dead spider " \
"rots on the ground."
elif r < 0.80:
self.enemy = enemies.Ogre()
self.alive_text = "\n An ogre is blocking your path!"
self.dead_text = "\nA dead ogre reminds you of your triumph."
elif r < 0.95:
self.enemy = enemies.BatColony()
self.alive_text = "\nYou hear a squeaking noise growing louder" \
"...suddenly you are lost in swarm of bats!"
self.dead_text = "\nDozens of dead bats are scattered on the ground."
else:
self.enemy = enemies.RockMonster()
self.alive_text = "\nYou've disturbed a rock monster " \
"from his slumber!"
self.dead_text = "\nDefeated, the monster has reverted " \
"into an ordinary rock."
super().__init__(x, y)
'''
def intro_text(self):
text = self.alive_text if self.enemy.is_alive() else self.dead_text
time.sleep(0.1)
return text
def modify_player(self, player):
if self.enemy.is_alive():
player.hp = player.hp - self.enemy.damage
print("Enemy does {} damage. You have {} HP remaining.".
format(self.enemy.damage, player.hp))
if not self.enemy.is_alive():
player.gold = player.gold + self.enemy.gold
if self.enemy.gold == 0:
pass
player.exp = player.exp + self.enemy.exp
total_exp = player.exp
levels = [0,200,450,1012]
if True:
current_level = sum(1 for x in levels if x <= total_exp)
player.player_lvl = current_level
if not self.enemy.is_alive():
self.enemy.exp = 0
class GoblinScoutTile(EnemyTile):
def __init__(self,x,y):
self.enemy = enemies.GoblinScout()
r = random.random()
if r < .20:
self.alive_text = "\nA small goblin jumps out at you its not much to look at..."\
"Lookout its got a knife!"
elif r < 50:
self.alive_text = "\nOut of a dark recess in the wall jumps a goblin"\
" ready to scewer you with his dagger"
else:
self.alive_text = "\nYou hear a gutteral voice out of the dark "\
"walks a goblin ready for battle!"
self.dead_text = "\nA dead goblin body."
super().__init__(x, y)
class GoblinBasherTile(EnemyTile):
def __init__(self,x,y):
self.enemy = enemies.GoblinBasher()
self.alive_text = "\nLookout a Goblin basher and he is looking for a fight."
self.dead_text = "\n The body of a dead Goblin basher"
super().__init__(x,y)
class VictoryTile(MapTile):
def modify_player(self,player):
player.victory = True
def intro_text(self):
return """
You see a bright light in the distance...
... it grows as you get closer! It's sunlight!
Victory is yours!
"""
class FindGoldTile(MapTile):
def __init__(self, x, y):
self.gold = random.randint(1, 50)
self.gold_claimed = False
super().__init__(x, y)
def modify_player(self, player):
if not self.gold_claimed:
self.gold_claimed = True
player.gold = player.gold + self.gold
print("+{} gold added.".format(self.gold))
def intro_text(self):
if self.gold_claimed:
return """
Another unremarkable part of the cave. You must forge onwards.
"""
else:
return """
Someone dropped some gold. You pick it up.
"""
class TrapRoomTile(MapTile):
def __init__(self, x, y):
r = random.randint(1,2)
if r == 1:
self.trap = items.PitFall()
self.tripped_text = "The open hole of a Pit Fall trap obstructs the tunnel."
self.set_text = "The floor in this hallway is unusually clean."
else:
self.set_text = "Looks like more bare stone... "
super().__init__(x, y)
def modify_player(self,player):
if not self.trap.is_tripped():
player.hp = player.hp - self.items.damage
print("You stumbled into a trap!")
time.sleep(1)
print("\nTrap does {} damage. You have {} HP remaining.".
format(self.items.damage, player.hp))
def intro_text(self):
text = self.tripped_text if self.trap.is_tripped() else self.set_text
time.sleep(0.1)
return text
class EmptyRoomTile(MapTile):
def intro_text(self):
r = random.random()
if r < .10:
return"""
Rough hewn stone walls are all you can make out in the flickering tourch light.
"""
elif r < .30:
return"""
There is nothing remarkable in this part of the tunnel keep moving onward!
"""
elif r < .50:
return"""
The dirt in this part of th ecave is scuffed but otherise there is nothing
remarkable here. best push on.
"""
elif r < 70:
return"""
You've been walking for a while without finding anyone or anything.
no sense in going back now better keep moving.
"""
else:
return"""
Great more stone... Is that a breeze I feel better keep going.
"""
class TraderTile(MapTile):
def __init__(self, x, y):
self.trader = npc.Trader()
super().__init__(x, y)
def check_if_trade(self, player):
while True:
print("Would you like to (B)uy, (S)ell, or (Q)uit?")
user_input = input()
if user_input in ['Q', 'q']:
return
elif user_input in ['B', 'b']:
print("Here's whats available to buy: ")
self.trade(buyer=player, seller=self.trader)
elif user_input in ['S', 's']:
print("Here's whats available to sell: ")
self.trade(buyer=self.trader, seller=player)
else:
print("Invalid choice!")
def trade(self, buyer, seller):
for i, item in enumerate(seller.inventory, 1):
print("{}. {} - {} Gold".format(i, item.name, item.value))
while True:
user_input = input("Choose an item or press Q to exit: ")
if user_input in ['Q', 'q']:
return
else:
try:
choice = int(user_input)
to_swap = seller.inventory[choice - 1]
self.swap(seller, buyer, to_swap)
except ValueError:
print("Invalid choice!")
def swap(self, seller, buyer, item):
if item.value > buyer.gold:
print("That's too expensive")
return
seller.inventory.remove(item)
buyer.inventory.append(item)
seller.gold = int(seller.gold) + int(item.value)
buyer.gold = int(buyer.gold) - int(item.value)
print("Trade complete!")
def intro_text(self):
return """
A frail not-quite-human, not-quite-creature squats in the corner
clinking his gold coins together. He looks willing to trade.
"""
world_dsl = """
|EN|EN|VT|EN|EN|
|EN| | | |EN|
|EN|FG|GS| |TT|
|TT| |ST|FG|EN|
|FG| |TR| |FG|
"""
def is_dsl_valid(dsl):
if dsl.count("|ST|") != 1:
return False
if dsl.count("|VT|") == 0:
return False
lines = dsl.splitlines()
lines = [l for l in lines if l]
pipe_counts = [line.count("|") for line in lines]
for count in pipe_counts:
if count != pipe_counts[0]:
return False
return True
tile_type_dict = {"VT": VictoryTile,
"EN": EnemyTile,
"ST": StartTile,
"FG": FindGoldTile,
"TT": TraderTile,
"GS": GoblinScoutTile,
"GB": GoblinBasherTile,
"ER": EmptyRoomTile,
"TR":TrapRoomTile,
" ": None}
world_map = []
start_tile_location = None
def parse_world_dsl():
if not is_dsl_valid(world_dsl):
raise SyntaxError("DSL is invalid!")
dsl_lines = world_dsl.splitlines()
dsl_lines = [x for x in dsl_lines if x]
for y, dsl_row in enumerate(dsl_lines):
row = []
dsl_cells = dsl_row.split("|")
dsl_cells = [c for c in dsl_cells if c]
for x, dsl_cell in enumerate(dsl_cells):
tile_type = tile_type_dict[dsl_cell]
if tile_type == StartTile:
global start_tile_location
start_tile_location = x, y
row.append(tile_type(x, y) if tile_type else None)
world_map.append(row)
def tile_at(x, y):
if x < 0 or y < 0:
return None
try:
return world_map[y][x]
except IndexError:
return None
You create the trap only sometimes. 50% of the time you don't put that attribute to your TrapRoomTile at all:
r = random.randint(1,2)
if r == 1:
self.trap = items.PitFall()
You could put self.trap = None to the else there, if you really want to have trap tiles without traps. Then you need to check if the trap tile has a trap when you try to use it.
It might also be that a component based approach, instead of subclassing, would serve you better, but that's another story.
Related
I've recently been trying to make a Textadventure but I'm having a big problem with the idea of "moving" around a List (x_achses) with a List inside(y-achses) it (my father called it a matrix) and in the second list (y-achses) there are a bunch of random types of things like an enemy. In that "Matrix" I wanna move around and on certain fields it should activate the assigned class(here it should simply print the classes name).
I've tried using the index of the list and changing dependently but it didn't work, I've tried using the len() function and changing it dependently but nothing ever seems to be happening. No classes or anything seem to be called. I have no idea what to do.
import random
import secrets
class Character:
def __init__(self, hp, ad, name):
self.hp = hp
self.ad = ad
self.name = name
def get_hit(self, ad):
self.hp = self.hp - ad
if self.hp <= 0:
self.die()
def is_dead(self):
return self.hp <= 0
def die(self):
print(self.name + " died")
class Human_enemies(Character):
def __init__(self):
super().__init__(self, 10, 3, "Mermaids")
Human_enemies.enemie_amount = random.randint(1, 9)
print("Mermaids")
def enemie_amount(self):
enemie_amount = random.randint(1, 9)
enemies = []
for i in range(enemie_amount):
enemies.append(Human_enemies())
class Vessel:
def __init__(self, hp, ad, storage, name):
self.hp = hp
self.ad = ad
self.storage = storage
self.name = name
def get_hit(self, ad):
self.hp = self.hp - ad
if self.hp <= 0:
self.die()
def is_dead(self):
return self.hp <= 0
def die(self):
print(self.name + " died")
class Sloop_ship(Vessel):
def __init__(self):
super().__init__(self, 1000, 50-100, 0, "Sloop")
def type(self):
print("Sloop")
class Brigattine_ship(Vessel):
def __init__(self):
super().__init__(self, 2500, 70-110, 15, "Brig")
def type(self):
print("Brig")
class Galleon_ship(Vessel):
def __init__(self):
super().__init__(self, 5000, 150-200, 40, "Galleon")
def type(self):
print("Galleon")
class Shipwreck:
def __init__(self):
print("Shipwreck")
wanna_loot = input("Do you want to check the wreck for any loot or survivors?\n")
#TODO: Make this better, sth. like chance of death or sth.
class Fields:
def Rougethingi(self):
random1 = random.randint(0, 5)
if random1 == 0:
return Shipwreck
elif random1 == 1:
return Human_enemies
elif random1 == 2:
return Sloop_ship
elif random1 == 3:
return Brigattine_ship
elif random1 == 4:
return Galleon_ship
else:
return None
class Map():
def __init__(self, x, y):
self.rowsX = []
self.x = x
self.y = y
for i in range(x):
self.rowsY = []
for r in range(y):
self.rowsY.append(Fields.Rougethingi(self))
self.rowsX.append(self.rowsY)
def print_map(self):
for j in self.rowsX:
print("")
for f in self.rowsY:
if f == None:
print("0 ", end = "")
elif f != None:
print("1 ", end = "")
print("\n")
class Player(Character):
def __init__(self):
super().__init__(self, 100, 1, player_name)
def start(self):
self.player_x = 1
self.player_y = 1
n.rowsX[self.player_x].__init__()
n.rowsY[self.player_y].__init__()
def move_right(self):
self.player_x = self.player_x + 1
if self.player_x >= n.x or self.player_x >= n.x - n.x:
print("You can't go further right")
def move_left(self):
self.player_x = self.player_x - 1
if self.player_x >= n.x or self.player_x >= n.x - n.x:
print("You can't go further right")
def move_up(self):
self.player_y = self.player_y + 1
if self.player_y >= n.y or self.player_y >= n.y - n.y:
print("You can't go further right")
def move_down(self):
self.player_y = self.player_y - 1
if self.player_y >= n.y or self.player_y >= n.y - n.y:
print("You can't go further right")
def quit_game():
exit("And the mighty adventurer decided to rest for the day...")
def print_help():
utility = print("(help, save, load, map,)")
movement = print("(forward, backwards, starboard, port)")
utility
movement
def save():
#TODO: Make a save system
pass
def load():
#TODO: Make a load system
pass
if __name__ == "__main__":
player_name = input("What is thy title scalywag?\n")
n = Map(30, 30)
m = Player.start
print("You shall call us for 'help' to help you with your role of Captain\n")
#? This is the main input loop --------------------------------------v
while True:
cmd = input(">>>")
if cmd == "help":
print_help()
continue
elif cmd == "map":
n.print_map()
continue
elif cmd == "quit":
quit_game()
elif cmd == "save":
save()
continue
elif cmd == "load":
load()
continue
elif cmd == "forward":
Player.move_up
continue
elif cmd == "backwards":
Player.move_down
continue
elif cmd == "starboard":
Player.move_right
continue
elif cmd == "port":
Player.move_left
continue
else:
print("I dont understand that... \n")
continue
#? This is the main input loop --------------------------------------v
So I am creating a card game in python using classes. I got it all set up to the point where it should work. But when I ran it it just simply never stops running. I am not really sure how to make this code minimal and reproduceable, because I do not know where the issue is at.
Here is the code i have written.
It has to be in the play_uno() class object.
""" UNO Simulator """
import random
class card():
def __init__(self, value, color, wild):
'''
An UNO deck consists of 108 cards, of which there are 76 Number cards,
24 Action cards and 8 Wild cards. UNO cards have four color "suits",
which are red, yellow, blue and green.
'''
self.card_nums = ['0','1','2','3','4','5','6','7','8','9']
self.card_colors = ['red','blue','green','yellow']
self.spec_cards = ['draw2','reverse','skip']
self.wild_cards = ['wild','wild_draw_4']
if wild == False:
self.value = self.card_nums[value]
self.color = self.card_colors[color]
elif wild == 'special':
self.value = self.spec_cards[value]
self.color = self.card_colors[color]
elif wild == True:
self.value = self.wild_cards[value]
self.color = None
class generate_deck():
def __init__(self):
self.number_cards = self.get_num_cards()
self.special_cards = self.get_special_cards()
self.wild_cards = self.get_wild_cards()
self.cards = self.number_cards + self.special_cards + self.wild_cards
random.shuffle(self.cards)
def get_num_cards(self):
# only one zero per color
with_zeroes = [card(i,j,False) for i in range(10) for j in range(4)]
no_zeroes = [card(i,j,False) for i in range(1,10) for j in range(4)]
return no_zeroes + with_zeroes
def get_wild_cards(self):
wild_draw4s = [card(i,None,True) for i in range(2) for x in range(2)]
wilds = [card(i,None,True) for i in range(2) for x in range(2)]
return wilds + wild_draw4s
def get_special_cards(self):
return [card(i,j,'special') for i in range(3) for j in range(4) for x in range(2)]
class player():
def __init__(self, name):
self.wins = 0
self.name = name
self.cheater = False
self.cards = ''
self.turn = 0
self.uno = 0
class play_uno():
def __init__(self, num_players = 3, num_cheaters = 0, cards_per_player = 5):
# get started
self.rules = 'default'
self.status = 'ongoing'
self.deck = generate_deck().cards
self.played_cards = []
self.dro = 0
self.direction = 0
self.top_card = self.deck.pop() # random card as first card to play on
self.tot_turns = 0
# generate players, make cheaters later
self.players = [player('player' + str(i)) for i in range(num_players + num_cheaters)]
# give each player 7 cards to start
for _player_ in self.players:
_player_.cards = [self.draw_card() for i in range(cards_per_player)]
# start playing turns in order
# do not know how to reverse yet
"""
Right now it is endless for some reason.
"""
while self.status == 'ongoing':
for _player in self.players:
self.turn(_player)
def draw_card(self):
# draws random card from deck instead of card on top
if len(self.deck) == 0:
self.re_shuffle()
self.dro += 1
return self.deck.pop()
def re_shuffle(self):
self.deck = self.played_cards
random.shuffle(self.deck)
self.played_cards = []
return self.deck, self.played_cards
def play_card(self, player_cards, _card):
self.top_card = _card
return player_cards.remove(_card), self.played_cards.append(_card), self.top_card
def game_over(self, winner):
winner.wins += 1
self.game_status = 'over'
def turn(self, _player):
played = False
# check if someone played wild card last turn
if self.top_card.value in card(1,2,None).wild_cards:
self.top_card.color = random.choice(card.card_colors)
if self.top_card.value == 'wild_draw_4':
_player.cards += [self.draw_card() for i in range(4)]
self.tot_turns += 1
return _player
# check for special cards
elif self.top_card.value in card(1,2,None).spec_cards:
if self.top_card.value == 'draw2':
_player.cards += [self.draw_card() for i in range(4)]
self.tot_turns += 1
return _player
# for now we are treating reverse cards like skips
elif self.top_card.value == 'reverse' or self.top_card.value == 'skip':
played = True
self.tot_turns += 1
return _player
# If its a normal card, or regular wild
if played == False:
for _card in _player.cards:
if _card.color == self.top_card.color:
self.play_card(_player.cards, _card)
played = True
break
elif _card.value == self.top_card.value:
self.play_card(_player.cards, _card)
played = True
break
# if the player cannot play at all
# rn they just move on if they have to draw,
# cant play the card they just drew.
if played == False:
_player.cards += [self.draw_card()]
played = True
self.tot_turns += 1
# check if the player won or not
if len(_player.cards) == 0:
self.game_over(_player)
elif len(_player.cards) == 1:
_player.uno += 1
return _player.cards
In the function turn in the play_uno class you are checking for certain wild/special cards. If the value is reverse, for example, the function hits a return _player line which ends the execution of the function and the player is unable to play another card.
Move the return statements to the end of the function if you want to ensure the rest of the code is run.
I did not run the code, but I think you should test a player's number of cards before drawing again. Like this:
if len(_player.cards) == 0:
self.game_over(_player)
elif len(_player.cards) == 1:
_player.uno += 1
if played == False:
_player.cards += [self.draw_card()]
played = True
self.tot_turns += 1
Or are the rules of this game different? I sincerely don't remember them anymore.
The while loop at the end of play_uno's __init__ checks for status, which never changes.
The loop calls turn on each of players every iteration. You must change status somewhere or you must put an if ...: break in the while loop (e.g. if not _player.cards).
EDIT: It appears that you meant self.status instead of self.game_status, in game_over. Try changing game_status to status there.
I have a simple problem, and I need a pari of fresh eyes for this. I have a dictionary for my world:
world = {}
and a function that fills rooms in that dictionary based on a 2d array:
def makeRoomsForLevel():
counter = 1
for r in range(len(rooms)):
for c in range(len(rooms)):
if rooms[c][r] == 1:
world[f"room{counter}"] = Room(
f"room{counter}",
"",
{},
False,
None,
False,
None,
False,
None,
True,
(r, c)
)
counter += 1
And yet another function that I am working on to find the exits for the room:
def findExits():
counter = 1;
for x in range(len(world)):
if rooms[world[f"room{counter}"]].coords[0] + 1 == 1:
pass
I tried running this before I filled in the if statement, and it gave me this error:
IndexError: only integers, slices (':'), ellipsis ('...'), numpy.newaxis ('None') and integer or boolean arrays are valid indicies
I kinda know what it means, but it hasn't given me any problems with this before hand.
Here is the room class:
class Room():
def __init__(self, name, description, exits, hasWeapon, weapon, hasItem, item, hasEnemy, enemy, isFirstVisit, coords):
self.name = name
self.description = description
self.exits = exits
self.hasWeapon = hasWeapon
self.weapon = weapon
self.hasItem = hasItem
self.item = item
self.hasEnemy = hasEnemy
self.enemy = enemy
self.isFirstVisit = isFirstVisit
self.coords = coords
And here is what "rooms" is:
rooms = np.zeros((11, 11))
Here is the full generation code:
rooms = np.zeros((11, 11))
maxRooms = 7
possibleNextRoom = []
world = {}
def startLevel():
for r in range(len(rooms[0])):
for c in range(len(rooms[1])):
rooms[r][c] = 0
possibleNextRoom.clear()
halfHeight = int(len(rooms[1]) / 2)
halfWidth = int(len(rooms[0]) / 2)
rooms[halfWidth][halfHeight] = 1
def resetLevel():
for r in range(len(rooms[0])):
for c in range(len(rooms[1])):
rooms[r][c] = 0
possibleNextRoom.clear()
def countRooms():
roomCount = 0
for r in range(len(rooms)):
for c in range(len(rooms)):
if rooms[r][c] == 1:
roomCount += 1
return roomCount
def findPossibleRooms():
for r in range(len(rooms) - 1):
for c in range(len(rooms) - 1):
if rooms[r][c] == 1:
if rooms[r][c+1] != 1:
possibleNextRoom.append((r, c+1))
if rooms[r][c-1] != 1:
possibleNextRoom.append((r, c-1))
if rooms[r-1][c] != 1:
possibleNextRoom.append((r-1, c))
if rooms[r+1][c] != 1:
possibleNextRoom.append((r+1, c))
def addRoom():
x = random.randrange(0, len(possibleNextRoom))
rooms[possibleNextRoom[x][0]][possibleNextRoom[x][1]] = 1
possibleNextRoom.pop(x)
def generateLevel():
global x, possibleNextRoom
startLevel()
while countRooms() < maxRooms:
countRooms()
findPossibleRooms()
addRoom()
def makeRoomsForLevel():
counter = 1
for r in range(len(rooms)):
for c in range(len(rooms)):
if rooms[c][r] == 1:
world[f"room{counter}"] = Room(
f"room{counter}",
"",
{},
False,
None,
False,
None,
False,
None,
True,
(r, c)
)
counter += 1
def findExits():
counter = 1;
for x in range(len(world)):
if rooms[world[f"room{counter}"].coords[0]] + 1 == 1:
pass
def fillRooms():
counter = 1
for x in range(len(world)):
x = random.randrange(1, 2)
if x == 1:
world[f"room{counter}"].hasItem = True
x = random.randrange(1, 10)
if x == 1:
world[f"room{counter}"].hasWeapon = True
x = random.randrange(1, 4)
if x == 1:
world[f"room{counter}"].hasEnemy = True
counter += 1
And here is the entire code:
import random
import numpy as np
world = {}
class Player():
def __init__(self, health, maxHealth, baseDmg, dmg, name, weapons, items, isAlive, previousRoom, roomName):
self.health = health
self.maxHealth = maxHealth
self.baseDmg = baseDmg
self.dmg = dmg
self.name = name
self.weapons = weapons
self.items = items
self.isAlive = isAlive
self.previousRoom = previousRoom
self.room = world[roomName]
def Move(self, direction):
if direction not in self.room.exits:
print("Cannot Move In That Direction!")
return
newRoomName = self.room.exits[direction]
self.previousRoom = world[self.room.name]
print("Moving to", newRoomName)
self.room = world[newRoomName]
def MoveBack(self):
self.room = world[self.previousRoom.name]
print("Moving to", self.room.name)
class Enemy():
def __init__(self, health, dmg, hasLoot, lootItem, isAlive):
self.health = health
self.dmg = dmg
self.hasLoot = hasLoot
self.lootItem = lootItem
self.isAlive = isAlive
class Weapon():
def __init__(self, name, dmg, description):
self.name = name
self.dmg = dmg
self.description = description
class Item():
def __init__(self, name, amt, description):
self.name = name
self.amt = amt
self.description = description
class Room():
def __init__(self, name, description, exits, hasWeapon, weapon, hasItem, item, hasEnemy, enemy, isFirstVisit, coords):
self.name = name
self.description = description
self.exits = exits
self.hasWeapon = hasWeapon
self.weapon = weapon
self.hasItem = hasItem
self.item = item
self.hasEnemy = hasEnemy
self.enemy = enemy
self.isFirstVisit = isFirstVisit
self.coords = coords
#######################Dungeon Generation###################
rooms = np.zeros((11, 11))
maxRooms = 7
possibleNextRoom = []
def startLevel():
for r in range(len(rooms[0])):
for c in range(len(rooms[1])):
rooms[r][c] = 0
possibleNextRoom.clear()
halfHeight = int(len(rooms[1]) / 2)
halfWidth = int(len(rooms[0]) / 2)
rooms[halfWidth][halfHeight] = 1
def resetLevel():
for r in range(len(rooms[0])):
for c in range(len(rooms[1])):
rooms[r][c] = 0
possibleNextRoom.clear()
def countRooms():
roomCount = 0
for r in range(len(rooms)):
for c in range(len(rooms)):
if rooms[r][c] == 1:
roomCount += 1
return roomCount
def findPossibleRooms():
for r in range(len(rooms) - 1):
for c in range(len(rooms) - 1):
if rooms[r][c] == 1:
if rooms[r][c+1] != 1:
possibleNextRoom.append((r, c+1))
if rooms[r][c-1] != 1:
possibleNextRoom.append((r, c-1))
if rooms[r-1][c] != 1:
possibleNextRoom.append((r-1, c))
if rooms[r+1][c] != 1:
possibleNextRoom.append((r+1, c))
def addRoom():
x = random.randrange(0, len(possibleNextRoom))
rooms[possibleNextRoom[x][0]][possibleNextRoom[x][1]] = 1
possibleNextRoom.pop(x)
def generateLevel():
global x, possibleNextRoom
startLevel()
while countRooms() < maxRooms:
countRooms()
findPossibleRooms()
addRoom()
def makeRoomsForLevel():
counter = 1
for r in range(len(rooms)):
for c in range(len(rooms)):
if rooms[c][r] == 1:
world[f"room{counter}"] = Room(
f"room{counter}",
"",
{},
False,
None,
False,
None,
False,
None,
True,
(r, c)
)
counter += 1
def findExits():
counter = 1;
for x in range(len(world)):
if rooms[world[f"room{counter}"].coords[0]] + 1 == 1:
pass
def fillRooms():
counter = 1
for x in range(len(world)):
x = random.randrange(1, 2)
if x == 1:
world[f"room{counter}"].hasItem = True
x = random.randrange(1, 10)
if x == 1:
world[f"room{counter}"].hasWeapon = True
x = random.randrange(1, 4)
if x == 1:
world[f"room{counter}"].hasEnemy = True
counter += 1
############################################################
generateLevel()
makeRoomsForLevel()
findExits()
WoodenSword = Weapon("Wooden Sword", 5, "A wooden sword. Looks like a kid's toy.")
IronDagger = Weapon("Iron Dagger", 8, "Small, sharp, and pointy. Good for fighting monsters!")
HealthPot = Item("Health Potion", 1, "A Potion of Instant Health. Restores 10 Health.")
goblin1 = Enemy(25, 2, True, [HealthPot, IronDagger], True)
player = Player(10, 10, 5, 5, "", [], [], True, "room1", "room1")
def ShowInv():
print("*******************************")
print("Name:", player.name)
print("Health:", player.health)
print("Weapons:")
for i in player.weapons:
print(" ===============================")
print(" Weapon:", i.name)
print(" Description:", i.description)
print(" Damage:", i.dmg)
print(" ===============================")
print("Items:")
for i in player.items:
print(" ===============================")
print(" Item:", i.name)
print(" Amount:", i.amt)
print(" Description:", i.description)
print(" ===============================")
print("*******************************")
def testItems(item):
exists = item in player.items
return exists
def fight(enemy):
print("Your Health:", player.health)
print("Enemy Health:", enemy.health)
ans = input("What would you like to do?\n>>")
if ans == "attack":
chance = random.randrange(1, 20)
if chance >= 10:
enemy.health -= player.dmg
else:
print("You did not roll high enough...\nYour turn has been passed...")
if ans == "heal":
chance = random.randrange(1, 20)
if testItems(HealthPot):
if chance >= 10:
x = 0
for item in player.items:
if item == HealthPot:
player.health += 10
if player.health > player.maxHealth:
player.health = player.maxHealth
item.amt -= 1
if item.amt <= 0:
player.items.pop(x)
break
x += 1
else:
print("You did not roll high enough...\nYour turn has been passed...")
if ans == "run":
chance = random.randrange(1, 20)
if chance >= 10:
player.MoveBack()
else:
print("You did not roll high enough...\nYour turn has been passed...")
if enemy.health > 0 and player.health > 0:
chance = random.randrange(1, 20)
if chance >= 10:
player.health -= enemy.dmg
else:
if enemy.health <= 0:
enemy.isAvile = False;
def testRoom():
if player.room.hasWeapon:
if player.room.isFirstVisit:
player.weapons.append(player.room.weapon)
if player.room.hasItem:
if player.room.isFirstVisit:
player.items.append(player.room.item)
if player.room.hasEnemy:
if player.room.isFirstVisit:
while player.room.enemy.health > 0:
fight(player.room.enemy)
player.room.isFirstVisit = False
while True:
command = input(">>")
if command in {"N", "S", "E", "W"}:
player.Move(command)
testRoom()
elif command == "look":
print(player.room.description)
print("Exits:", *','.join(list(player.room.exits.keys())))
elif command == "inv":
ShowInv()
elif command == "heal":
if testItems(HealthPot):
player.health += 10
if player.health > player.maxHealth:
player.health = player.maxHealth
else:
print("You don't have any", HealthPot.name, "\bs")
else:
print("Invalid Command")
IIUC, you need something like:
#find the name of the room given the coordinates
def findRoom(x, y):
for i in range(len(world)):
if world[f"room{i+1}"].coords == (x, y):
return f"room{i+1}"
return None
def findExits():
for i in range(len(world)):
x, y = world[f"room{i+1}"].coords
exits = dict()
#east
if rooms[x, y+1] == 1:
exits["E"] = findRoom(x, y+1)
#south
if rooms[x+1, y] == 1:
exits["S"] = findRoom(x+1, y)
#west
if rooms[x, y-1] == 1:
exits["W"] = findRoom(x, y-1)
#north
if rooms[x-1, y] == 1:
exits["N"] = findRoom(x-1, y)
world[f"room{i+1}"].exits = exits
After running the above, the exits for room1 look like:
>>> world["room1"].exits
{'E': 'room2', 'S': 'room4'}
You can work with the above structure, changing the keys and values as needed.
This question already has an answer here:
NoneType object is not subscriptable
(1 answer)
Closed 2 years ago.
I'm trying to write code for a text based RPG by following along in a book called Make Your Own Python Text Adventure by Phillip Johnson. I am having trouble identifying where the error lies in my code. I've gone through it several times and can't seem to figure it out. I've tried fiddling with different things but so far it just gives different errors. I'll post my code and the Traceback.
Traceback (most recent call last):
File "C:\Users\Timothy Hall\Desktop\Yradel_Game\Yradel_Game5.py", line 99, in <module>
main()
File "C:\Users\Timothy Hall\Desktop\Yradel_Game\Yradel_Game5.py", line 31, in main
player = Player()
File "C:\Users\Timothy Hall\Desktop\Yradel_Game\player.py", line 11, in __init__
self.x = world.start_tile_location[0]
TypeError: 'NoneType' object is not subscriptable
Yradel_Game5.py
# this program is a text based rpg
#import the OrderedDict
from collections import OrderedDict
# import the player module
from player import Player
# import the world module
import world
def main():
# display Yradellian name examples
print("Some examples of female Yradellian names include Nelsi, Drew, Casey, Ilya, etc.")
print("Some examples of male Yradellian names include Toreth, Daren, Attel, Mayes, etc.")
# get the character name from the user
yesName = False
while yesName == False:
charName = input("What can I call you? ")
nameInput = input("Your name is, " + charName + "? Type Yes or No: ")
if nameInput == "Yes":
yesName = True
else:
print("Sorry...")
# call the parse world function
world.parse_world_dsl()
# create a player object
player = Player()
# display the welcome message
print()
player.say_hello(charName)
# display current inventory
print()
print("Here's your starting game Inventory:")
player.print_inventory()
while True:
# display the intro text for each tile
print()
room = world.tile_at(player.x, player.y)
print(room.intro_text())
# modify the player depending on the tile type
room.modify_player(player)
# get the action input from the user
print()
choose_action(room, player)
# create a funtion for available actions
def get_available_actions(room, player):
actions = OrderedDict()
print("Choose what to do...")
if player.inventory:
action_adder(actions, "i", player.print_inventory, "Print inventory")
if isinstance(room, world.TraderTile):
action_adder(actions, "t", player.trade, "Trade")
if isinstance(room, world.EnemyTile) and room.enemy.is_alive():
action_adder(actions, "a", player.attack, "Attack")
else:
if world.tile_at(room.x, room.y - 1):
action_adder(actions, "n", player.move_north, "Go North!")
if world.tile_at(room.x + 1, room.y):
action_adder(actions, "e", player.move_east, "Go East!")
if world.tile_at(room.x, room.y + 1):
action_adder(actions, "s", player.move_south, "Go South!")
if world.tile_at(room.x - 1, room.y):
action_adder(actions, "w", player.move_west, "Go West!")
if player.hp < 100:
action_adder(actions, "h", player.heal, "Heal")
return actions
# create the action adder function
def action_adder(action_dict, hotkey, action, name):
action_dict[hotkey.lower()] = action
action_dict[hotkey.upper()] = action
print("{}: {}".format(hotkey, name))
# create a function to utilize the action dictionary
def choose_action(room, player):
action = None
while not action:
available_actions = get_available_actions(room, player)
action_input = input("Action: (Type a letter) ")
action = available_actions.get(action_input)
if action:
action()
else:
print("That is invalid input.")
# call the main function
main()
world.py
# import the enemies module
import enemies
# import the npc module
import npc
# import the random module
import random
# create a parent class for the map tiles
class MapTile:
def __init__(self, x, y):
self.x = x
self.y = y
def intro_text(self):
raise NotImplementedError("Create a subclass instead!")
def modify_player(self, player):
pass
# create the tile subclasses
class StartTile(MapTile):
def intro_text(self):
return """You find yourself in a forest, sunlight trickling through the leaves overhead. Your feet crunch over the underbrush. You can see four paths through the trees.\n"""
class BoringTile(MapTile):
def intro_text(self):
return """The trees all look the same here...\n"""
class CityTile(MapTile):
def intro_text(self):
return """You made it out of the forest into a small town known as Burenburg. The people greet you warmly and you are filled with a sense of accomplishment.\n"""
class EnemyTile(MapTile):
# have enemies randomly appear
def __init__(self, x, y):
r = random.random()
if r < 0.50:
self.enemy = enemies.Wolf()
self.alive_text = "A lone Wolf approaches you baring its fangs."
self.dead_text = "The Wolf keels over, dead before you."
else:
self.enemy = enemies.Goblin()
self.alive_text = "A Goblin tries to steal your gold, you must defend yourself against his blade."
self.dead_text = "The Goblin sticks its tongue out at you as it falls over dead."
super().__init__(x, y)
# display their alive/dead message
def intro_text(self):
text = self.alive_text if self.enemy.is_alive() else self.dead_text
return text
# have enemies attack the player
def modify_player(self, player):
if self.enemy.is_alive():
player.hp = player.hp - self.enemy.damage
print("Enemy does {} damage. You have {} HP remaining.".format(self.enemy.damage, player.hp))
class TraderTile(MapTile):
def __init__(self, x, y):
self.trader = npc.Trader()
super().__init__(x, y)
# create a method to trade between a buyer and seller
def trade(self, buyer, seller):
for i, item in enumerate(seller.inventory, 1):
print("{}. {} - {} Gold".format(i, item.name, item.value))
while True:
user_input = input("Choose an item or press Q to exit: ")
if user_input in ["Q", "q"]:
return
else:
try:
choice = int(user_input)
to_swap = seller.inventory[choice - 1]
self.swap(seller, buyer, to_swap)
except ValueError:
print("Invalid choice!")
# create a method to swap items and gold between the buyer and seller
def swap(self, seller, buyer, item):
if item.value > buyer.gold:
print("That's too expensive")
return
seller.inventory.remove(item)
buyer.inventory.append(item)
seller.gold = seller.gold + item.value
buyer.gold = buyer.gold - item.value
print("Trade complete!")
# create a method to accept user input about who is the buyer and seller
def check_if_trade(self, player):
while True:
print("Would you like to (B)uy, (S)ell or (Q)uit?")
user_input = input()
if user_input in ["Q", "q"]:
return
elif user_input in ["B", "b"]:
print("Here's what's available to buy: ")
self.trade(buyer = player, seller = self.trader)
elif user_input in ["S", "s"]:
print("Here's what's available to sell: ")
self.trade(buyer = self.trader, seller = player)
else:
print("Invalid choice!")
# create intro text for this room
def intro_text(self):
return """A man in a brown robe awaits you, willing to trade."""
class GoldTile(MapTile):
def __init__(self, x, y):
self.gold = random.randint(1, 50)
self.gold_claimed = False
super().__init__(x, y)
def modify_player(self, player):
if not self.gold_claimed:
self.gold_claimed = True
player.gold = player.gold + self.gold
print("+{} Gold added.".format(self.gold))
def intro_text(self):
if self.gold_claimed:
return """Another clearing in the forest with nothing in it but cute animals and underbrush."""
else:
return """Someone must've dropped some gold! You pick it up."""
# create the basic world map
world_dsl = """
| |E|C|E| |
|B|B| |B| |
|T| |G|E|B|
|G| |E| |G|
|E|B|S|T|B|
| | |E| |G|
| | |G|B|E|
"""
# create a function to validate the dsl
def is_dsl_valid(dsl):
if dsl.count("|S|") != 1:
return False
if dsl.count("|C|") == 0:
return False
lines = dsl.splitlines()
lines = [l for l in lines if l]
pipe_counts = [line.count("|") for line in lines]
for count in pipe_counts:
if count != pipe_counts[0]:
return False
return True
# define a dictionary that maps dsl abreviations to tile types
tile_type_dict = {"C": CityTile,
"E": EnemyTile,
"S": StartTile,
"B": BoringTile,
"G": GoldTile,
"T": TraderTile,
" ": None}
world_map = []
start_tile_location = None
# create the function to parse the dsl
def parse_world_dsl():
if not is_dsl_valid(world_dsl):
raise SyntaxError("DSL is invalid!")
dsl_lines = world_dsl.splitlines()
dsl_lines = [x for x in dsl_lines if x]
# iterate over each line in the dsl
# instead of i, the variable y is used because we're working with an X-Y grid.
for y, dsl_row in enumerate(dsl_lines):
# create an object to store the tiles
row = []
# split the line into abbreviations using the "split" method
dsl_cells = dsl_row.split("|")
# the split method includes the beginning and end of the line
# so we need to remove those nonexistent cells
dsl_cells = [ c for c in dsl_cells if c]
# iterate over each cell in the dsl line
# instead of j, the variable x is used becuase we're working with an X-Y grid
for x, dsl_cell in enumerate(dsl_cells):
# look up the abbreviation in the dictionary
tile_type = tile_type_dict[dsl_cell]
# if the dictionary returned a valid type, create a new
# tile object, pass it the X-Y coordinates as required
# by the tile__init__(), and add it to the row object. If
# None was found in the dictionary, we just add None.
row.append(tile_type(x, y) if tile_type else None)
# add the whole row to the world_map
world_map.append(row)
# create a function that locates the tile at a specific coordinate
def tile_at(x, y):
if x < 0 or y < 0:
return None
try:
return world_map[y][x]
except IndexError:
return None
player.py
# import the items module
import items
# import the world module
import world
# create a class for Player
class Player:
# define the inventory
def __init__(self):
self.inventory = [items.Dagger(), items.BreadRoll(), items.Potion()]
self.x = world.start_tile_location[0]
self.y = world.start_tile_location[1]
self.hp = 100
self.gold = 100
# print the inventory and the best weapon
def print_inventory(self):
print("Inventory:")
for item in self.inventory:
print("* " + str(item))
print("Gold: {}".format(self.gold))
best_weapon = self.most_powerful_weapon()
print("Your best weapon is your {}".format(best_weapon))
# create a function to heal the player
def heal(self):
consumables = [item for item in self.inventory if isinstance(item, items.Consumable)]
if not consumables:
print("You don't have any items to heal you!")
return
for i, item in enumerate(consumables, 1):
print("Choose an item to use to heal: ")
print("{}. {}".format(i, item))
valid = False
while not valid:
choice = input("")
try:
to_eat = consumables[int(choice) - 1]
self.hp = min(100, self.hp + to_eat.healing_value)
self.inventory.remove(to_eat)
print("Current HP: {}".format(self.hp))
valid = True
except (ValueError, IndexError):
print("Invalid choice, try again.")
# welcome the user to the game
def say_hello(self, name):
print("Hello, " + name, ", welcome to the world of Yradel.")
# create a function to determine the best weapon
def most_powerful_weapon(self):
max_damage = 0
best_weapon = None
for item in self.inventory:
try:
if item.damage > max_damage:
best_weapon = item
max_damage = item.damage
except AttributeError:
pass
return best_weapon
# create functions to let the player move
def move(self, dx, dy):
self.x += dx
self.y += dy
def move_north(self):
self.move(dx = 0, dy = -1)
def move_east(self):
self.move(dx = 1, dy = 0)
def move_south(self):
self.move(dx = 0, dy = 1)
def move_west(self):
self.move(dx = -1, dy = 0)
# create a function to attack the enemy
def attack(self):
best_weapon = self.most_powerful_weapon()
room = world.tile_at(self.x, self.y)
enemy = room.enemy
print("You use {} against the {}!".format(best_weapon.name, enemy.name))
enemy.hp -= best_weapon.damage
if not enemy.is_alive():
print("You killed {}!".format(enemy.name))
else:
print("{} HP is {}.".format(enemy.name, enemy.hp))
# create a method to allow trade
def trade(self):
room = world.tile_at(self.x, self.y)
room.check_if_trade(self)
It looks like you forgot to set start_tile_location in this function:
start_tile_location = None
# create the function to parse the dsl
def parse_world_dsl():
if not is_dsl_valid(world_dsl):
raise SyntaxError("DSL is invalid!")
dsl_lines = world_dsl.splitlines()
dsl_lines = [x for x in dsl_lines if x]
# iterate over each line in the dsl
# instead of i, the variable y is used because we're working with an X-Y grid.
for y, dsl_row in enumerate(dsl_lines):
# create an object to store the tiles
row = []
# split the line into abbreviations using the "split" method
dsl_cells = dsl_row.split("|")
# the split method includes the beginning and end of the line
# so we need to remove those nonexistent cells
dsl_cells = [ c for c in dsl_cells if c]
# iterate over each cell in the dsl line
# instead of j, the variable x is used becuase we're working with an X-Y grid
for x, dsl_cell in enumerate(dsl_cells):
# look up the abbreviation in the dictionary
tile_type = tile_type_dict[dsl_cell]
# if the dictionary returned a valid type, create a new
# tile object, pass it the X-Y coordinates as required
# by the tile__init__(), and add it to the row object. If
# None was found in the dictionary, we just add None.
row.append(tile_type(x, y) if tile_type else None)
if "S" == dsl_cell:
start_tile_location = (x, y)
# add the whole row to the world_map
world_map.append(row)
I have made a sonar finding game and some functions for it. I have put these functions into a class and now I want to call them so I can play the game, however when I define the class call it now returns things to me like game.getNewBoard() takes 0 positional arguments and tells me I have given it one when I have not and so on. Any help would be appreciated.
# Sonar
import random
import sys
class OceanTreasure:
def drawBoard(board):
# Draw the board data structure.
hline = ' ' # initial space for the numbers down the left side of the board
for i in range(1, 6):
hline += (' ' * 9) + str(i)
# print the numbers across the top
print(hline)
print(' ' + ('0123456789' * 6))
print()
# print each of the 15 rows
for i in range(15):
# single-digit numbers need to be padded with an extra space
if i < 10:
extraSpace = ' '
else:
extraSpace = ''
print('%s%s %s %s' % (extraSpace, i, getRow(board, i), i))
# print the numbers across the bottom
print()
print(' ' + ('0123456789' * 6))
print(hline)
def getRow(board, row):
# Return a string from the board data structure at a certain row.
boardRow = ''
for i in range(60):
boardRow += board[i][row]
return boardRow
def getNewBoard():
# Create a new 60x15 board data structure.
board = []
for x in range(60): # the main list is a list of 60 lists
board.append([])
for y in range(15): # each list in the main list has 15 single-character strings
# I thought about using different char to make it more readble?? Admit it, it looks dull with just these ~
if random.randint(0, 1) == 0:
board[x].append('~')
else:
board[x].append('~')
return board
def getRandomChests(numChests):
# Create a list of chest data structures (two-item lists of x, y int coordinates)
chests = []
for i in range(numChests):
chests.append([random.randint(0, 59), random.randint(0, 14)])
return chests
def isValidMove(x, y):
# Return True if the coordinates are on the board, otherwise False.
return x >= 0 and x <= 59 and y >= 0 and y <= 14
def makeMove(board, chests, x, y):
# Change the board data structure with a sonar device character. Remove treasure chests
# from the chests list as they are found. Return False if this is an invalid move.
# Otherwise, return the string of the result of this move.
if not isValidMove(x, y):
return False
smallestDistance = 100 # any chest will be closer than 100.
for cx, cy in chests:
if abs(cx - x) > abs(cy - y):
distance = abs(cx - x)
else:
distance = abs(cy - y)
if distance < smallestDistance: # we want the closest treasure chest.
smallestDistance = distance
if smallestDistance == 0:
# xy is directly on a treasure chest!
chests.remove([x, y])
return 'You have found a sunken treasure chest!'
else:
if smallestDistance < 10:
board[x][y] = str(smallestDistance)
return 'Treasure detected at a distance of %s from the sonar device.' % (smallestDistance)
else:
board[x][y] = 'O'
return 'Sonar did not detect anything. All treasure chests out of range.'
def enterPlayerMove():
# Let the player type in her move. Return a two-item list of int xy coordinates.
print('Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)')
while True:
move = input()
if move.lower() == 'quit':
print('Thanks for playing!')
sys.exit()
move = move.split()
if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isValidMove(int(move[0]), int(move[1])):
return [int(move[0]), int(move[1])]
print('Enter a number from 0 to 59, a space, then a number from 0 to 14.')
def playAgain():
# This function returns True if the player wants to play again, otherwise it returns False.
print('Do you want to play again? (yes or no)')
return input().lower().startswith('y')
print('S O N A R !')
print()
while True:
# game setup
game=OceanTreasure()
sonarDevices = 20
theBoard = game.getNewBoard()
theChests = getRandomChests(3)
drawBoard(theBoard)
previousMoves = []
while sonarDevices > 0:
# Start of a turn:
# sonar device/chest status
if sonarDevices > 1: extraSsonar = 's'
else: extraSsonar = ''
if len(theChests) > 1: extraSchest = 's'
else: extraSchest = ''
print('You have %s sonar device%s left. %s treasure chest%s remaining.' % (sonarDevices, extraSsonar, len(theChests), extraSchest))
x, y = enterPlayerMove()
previousMoves.append([x, y]) # we must track all moves so that sonar devices can be updated.
moveResult = makeMove(theBoard, theChests, x, y)
if moveResult == False:
continue
else:
if moveResult == 'You have found a sunken treasure chest!':
# update all the sonar devices currently on the map.
for x, y in previousMoves:
makeMove(theBoard, theChests, x, y)
drawBoard(theBoard)
print(moveResult)
if len(theChests) == 0:
print('You have found all the sunken treasure chests! Congratulations and good game!')
break
sonarDevices -= 1
if sonarDevices == 0:
print('We\'ve run out of sonar devices! Now we have to turn the ship around and head')
print('for home with treasure chests still out there! Game over.')
print(' The remaining chests were here:')
for x, y in theChests:
print(' %s, %s' % (x, y))
if not playAgain():
sys.exit() #I thought this is a better way than just break or make false, correct me if I am wrong
Every class method in Python receives the instance it is called from as the first parameter automatically. That means, that the first parameter is always self:
def drawBoard(self, board):