Having trouble understanding classes - python

i feel like this is a very simple concept but i just cant seem to wrap my head around classes and returning values from them... this what i've come up with to try and practice, very much WIP but i'm stuck here.
thanks in advance
import random
global d6
print("welcome to fight night")
#dice roll
class die():
def __init__(self, sides):
self.sides = sides
def roll(self):
chance = random.randint(1,self.sides)
if chance == 3:
return True
if chance != 3:
return False
d6 = die(6)
class Player():
def __init__(self, name, weight, spec):
self.name = name
self.weight = weight
self.spec = spec
fighter1 = Player("BRUCE LEE", 150, "SUPER SPEED")
fighter2 = Player(GEORGE FORMAN", 225, "REACH")
## how do i return true or false here to check hit?
print(fighter1(name) + "attacks"+ fighter2(name))
d6.roll()
if d6 == True:
print(fighter(name) + "hit" + fighter2(name))
if d6 == False:
print("attack missed")

A few things about class conventions:
The name of a class should typically start with an uppercase letter.
The () after the name of the class is not necessary unless you're
using inheritance.
Code:
class Die:
def __init__(self, number_of_sides):
self.number_of_sides = number_of_sides
def roll(self):
"""
Returns True if the roll succeeds, returns False otherwise.
There is a (1 / self.number_of_sides) chance the roll will succeed.
"""
from random import choice
return choice([True] + [False] * (self.number_of_sides-1))
die = Die(6)
if die.roll():
print("Nice!")
else:
print("Sorry, try again.")
EDIT I think you need a more fundamental example of how the return keyword works:
def give_me_a_number():
return 5 + 4
x = give_me_a_number()
print(x)
Output:
9

d6.roll() returns a value (which is True or False) -- d6 does not become that value, it returns it to you. When you roll a 3 on a real d6, the die does not itself entirely transform into the number 3 in a puff of smoke and mathemagicks, it just shows it to you on one of its faces. :)
So you could do:
if d6.roll():
print(fighter1.name + " hit " fighter2.name)
else:
print("attack missed")
Or if you wanted to be more verbose:
die_result = d6.roll()
if die_result is True:
print(fighter1.name + " hit " fighter2.name)
if die_result is False:
print("attack missed")

I would restructure your die class such that it only returns a random value as the result of roll. You generally want objects (instances of classes) to be independent - the user of the die object should determine what is done with the random value the object generates, not the die itself.
For example, let's say we have many players, of varying strengths. A strong player attacking a weak player should have odds different from a weak player attacking a strong player. This data (strong vs weak) should be inside the player object, not the die object. So I'd make two more methods for the Player, and an attribute like 'strength' which gets an int value...
import random
class Die:
def __init__(self, sides):
self.sides = sides
def roll(self):
chance = random.randint(1,self.sides)
return chance
class Player:
def __init__(self, name, weight, spec):
self.name = name
self.weight = weight
self.spec = spec
def get_stats(self):
return self.spec
def attack(self, target):
odds = self.spec / (self.spec + target.get_stats())
#print(odds)
d6 = Die(6);
temp = d6.roll()
#print(temp)
if (d6.roll() / 6 < odds):
return ("hit")
else:
return ("miss")
if __name__ == "__main__":
print("welcome to fight night")
fighter1 = Player("BRUCE LEE", 150, 100)
fighter2 = Player("GEORGE FORMAN", 225, 100)
for x in range(20):
result = fighter1.attack(fighter2)
print(result)
So, now the Player objects control the odds themselves, not the die objects, making everything more flexible. (Making the Die a local object inside the function is a bit high on overhead, incidentally, but global objects/variables have many issues and are probably best avoided)

Related

How do I make my (derived?) python instance attributes "self-update" based on alterations to other same-instance attributes?

I know that I have a misunderstanding of how Python attributes work because I'm here writing this problem, but I don't know exactly what I'm misunderstanding. I'm trying to get
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
to attain their values based on self.hand, which I pass to __init__ upon instantiation. Throughout the game I am altering .hand, and I want .card & .card_val to change every time I change .hand, instead of having to tell it to do that elsewhere (outside of the attribute definitions). I know there is a way to do this, or at least I think there is, and by that I mean simply by defining their values as inherited based on whatever .hand is at any given time, without calling an internal or external function.
In the posted code, I have altered it to work as the game instructions require, using...
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
...but that's what I want to change. I want what that function is doing to be executed more concisely inside of the attribute definitions upon handling of the instance. Basically my question is why can't these two attributes get their values directly from the first attribute that I define via "hand" passed to the init?
Any help would be appreciated, and more importantly, I think more than just solutions, it would help me even more to understand what I am misunderstanding about how attributes, instances, and instantiation and all that works so that I know where my thinking is wrong. Thanks!
import random
from random import shuffle
from collections import deque
class Deck():
def __init__(self):
self.ranks = {"Ace":14, "King":13, "Queen":12, "Jack":11, "10":10, "9":9, "8":8, "7":7, "6":6, "5":5, "4":4, "3":3, "2":2}
self.suites = ["Heart", "Diamond", "Spade", "Club"]
self.cards = []
def create_cards(self):
for suite in self.suites:
for key in self.ranks.keys():
self.cards.append(key + " " + suite)
def shuffle(self):
random.shuffle(deck.cards)
deck = Deck()
deck.create_cards()
deck.shuffle()
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
def war(bool, p1, p2):
if bool == True:
for player in [p1, p2]:
player.card_number = 4
else:
for player in [p1, p2]:
player.card_number = 0
p2 = Player(deque(deck.cards[::2]))
p1 = Player(deque(deck.cards[1::2]))
p2.name = "The Computer"
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
def cant_war_lose(winner, loser):
print("{a} doesn't have enough cards to go to war, so {b} wins the Battle!".format(a = loser, b = winner))
def battle_win(winner, loser):
print("{a} has run out of cards, therefore {b} has won via Battle!".format(a = loser, b = winner))
def play_cards(p1, p2):
war(False, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-1)
elif p1.card_val == p2.card_val:
if len(p1.hand) < 5 or len(p2.hand) < 5:
if len(p1.hand) > len(p2.hand):
cant_war_lose(p1.name, p2.name)
else:
cant_war_lose(p2.name, p1.name)
return 0
else:
input("War is inititated! Press Enter to continue!")
print("---------------------------")
war(True, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
for i in range(0,5):
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-5)
elif p1.card_val < p2.card_val:
for i in range(0,5):
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-5)
else:
p1.hand.rotate(-1)
p2.hand.rotate(-1)
elif p1.card_val < p2.card_val:
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-1)
if len(p1.hand) != 0 and len(p2.hand) != 0:
input("After the last round of Battle, {a} now has {b} cards, and {c} now has {d} cards! Press Enter to continue!".format(a = p1.name, b = len(p1.hand), c = p2.name, d = len(p2.hand)))
print("---------------------------")
else:
if len(p1.hand) > len(p2.hand):
battle_win(p1.name, p2.name)
else:
battle_win(p2.name, p1.name)
return 0
def game_run():
run = 1
p1.name = input("Player 1's name? ")
print("---------------------------")
while run == 1:
if play_cards(p1, p2) == 0:
run = 0
game_run()
You can use the property decorator to create a calculated property
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
#property
def hand(self):
return self._hand
#hand.setter
def hand(self, value):
self._hand = value
self.card = self._hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
What you misunderstand is variables, not instances. For instance, the attribute card is a scalar variable attached to the instance. Assigning to it with
self.card = <blah>
does not bind it to blah for constant recomputation. This is a value assignment, not a memory mapping. If you want that long-term binding, you must either write the maintenance routine yourself -- which you've already done, in a way, with the consistent recomputation -- or you must assign a mutable reference to self.card, so that card refers to teh same object as the expression you created.
Given that you are consistently rotating and altering the hand, this is not feasible in your design. Instead, simply write an access routine, perhaps get_next_card(hand), which will rotate the hand, extract the card, and return the desired rank and suit.
If you plan to program more card games, you will also find it handy to define a class card and class hand, with appropriate support routines. Maintain the card as a pair of integers; convert to strings only for printing.
Does that get you moving?
For anyone who wanted to compare a before and after of the problem & final solution, below is the working code for my specific issue. All I had to do was convert self.card and self.card_val to a calculated property. By passing in hand, and subsequently handling only hand, self.card & self.card_val are calculated, since every time I handle the instance of the class (by handling hand), these "method attributes" are being called and altered. Thanks for the input, guys!
class Player():
def __init__(self, hand):
self.name = "name"
self.card_number = 1
self.hand = hand
#property
def card(self):
return self.hand[self.card_number].split()
#property
def card_val(self):
return deck.ranks.get(self.card[0])

Problems with switching players and keeping track of each of their score when making a Cricket game

I want to create a very simple text-based Cricket game. But I am quite stuck.
from random import randint
class Player():
def __init__(self):
pass
def run(self, player, score):
# some code that will take a player and a score and associate that
# score with the player and store it (in a dictionary)
def switch(self, player):
# some code that will take a player name and change the current
# batsmen to the next one, for e.g this should change "a" to "b" or
# vice versa but not "c"
team_players = ["a", "b", "c"]
player = Player()
position = 0
run = randint(0,6)
current_batsman = team_players[position]
if run%2 == 0: # which means run is even
player.run(current_batsman, run) # sending the current player and their run
else: # if the run is odd
player.run(current_batsman, run) # still doing the same stuff as before but...
player.switch(current_batsman) # the current batsman should now be switched
Maybe tweaking position in the Player class somehow might help.
I hope my code explains my problem thoroughly. And by the way, in Cricket scores are called run and if a player (batsman) makes an odd run (1, 3, 5) the next batsman comes to play, and there are only two batsmen in the field until one gets out but I want my game to be really simple, for now. Any help is greatly appreciated. Thanks.
I don't know Cricket's rules but in class Player I would keep player's name and his score. It could have also function run() which adds random value to his score (or create method with parameter - value which you want to add to score)
class Player():
def __init__(self, name):
self.name = name
self.score = 0
def run(self):
self.score += random.randint(0, 6)
def __str__(self):
return "{} (score: {})".format(self.name, self.score)
I also added __str__ to easily display player.
Next I would create class Team which keeps all players, keep information which player is current batsman, switch batsman, and use run() for current batsman
class Team():
def __init__(self, players):
self.players = players
self.current_batsman = 0
self.current_run = 0
def set_next_batsman(self):
self.current_batsman += 1
if self.current_batsman >= len(self.players):
self.current_batsman = 0
def get_current_batsman(self):
return self.players[self.current_batsman]
def run(self):
self.players[self.current_batsman].run()
if self.current_run % 2 != 0:
self.set_next_batsman()
self.current_run += 1
def __str__(self):
return "Player: " + ", ".join(str(p) for p in self.players)
def total_score(self):
return sum(p.score for p in self.players)
And then two teams can play:
team1 = Team( [Player("a"), Player("b"), Player("c")] )
team2 = Team( [Player("x"), Player("y"), Player("z")] )
print('Team1:', team1)
print('Team2:', team2)
for number in range(1, 5):
print('Round:', number)
print('Team1 current batsman:', team1.get_current_batsman())
team1.run()
print('Team2 current batsman:', team2.get_current_batsman())
team2.run()
print('Team1:', team1)
print('Team2:', team2)
print('Team1 total score:', team1.total_score())
print('Team2 total score:', team2.total_score())

Python - Unexpected behaviour of class attributes

Edited in simple words
code:
class temp:
attr1 = 0
attr2 = []
t1 = temp()
t2 = temp()
t1.attr1 = 50
t1.attr2.append(50)
print(t1.attr1)
print(t1.attr2)
print(t2.attr1)
print(t2.attr2)
output:
50
[50]
0
[50]
I have called append only on attr2 object t1 but the append changes attr2 of both objects. if attr2 is shared (class attributes) then why does attr1 values are different for t1 and t2. What might have caused this unexpected behaviour ?
old question
I am writing a python code for blackjack. The code I have written is as follows.
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.cards)
print(p1.cards)
dealer.hit()
print(dealer.cards)
print(p1.cards)
print(dealer.total)
print(p1.total)
Game = False
output of this code is as follows
Welcome to BlackJack ....
Enter the amount you currrently have for the game55
[]
[]
[45]
[45]
6
0
as you can see I had called hit() only once on dealer object but it is appending it to cards attribute of both dealer as well as p1 object. However total attribute is different. Can anyone explain what might have caused this unexpected behaviour ?
When you do t1.attr1 = 50, you're rebinding attr1 to a new value in the t1 object's attribute namespace. It previously let you access the value bound in the class namespace, but when you bind a new value, you hide the one from the class (for that instance only).
In contrast, when you do t1.attr2.append(50), you're mutating the existing list (which is bound in the class namespace, but is visible though all instances) in place, with no rebinding of variables happening at all. This is why you see the change in t2. The variables t1.attr2 and t2.attr2 are both references to the same object (which you can verify using the is operator: t1.attr2 is t2.attr2).
In general, it's usually not a good idea to use lists or other mutable values for class variables if you don't want them to be shared by all instances. It's not forbidden though, because sometimes you do specifically do want the shared behavior.
I got what you are asking. You need to differentiate all cards with player cards. So, instead of naming everything as cards, I would suggest doing this:
class Player:
all_cards = []
total = 0
amount = 0
and update __init__ as :
def __init__(self, money=0):
self.amount = money
self.player_cards = []
while doing append operation, append it to all_cards and to the player_cards. Anyway, you are printing only player cards, you can see different list of cards.
Here is full code :
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
all_cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.player_cards = []
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.player_cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.player_cards.append(no)
self.all_cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.player_cards)
print(p1.player_cards)
dealer.hit()
print(dealer.player_cards)
print(p1.player_cards)
print(dealer.total)
print(p1.total)
Game = False
This happened because list is a mutable object, and it is created once only when defining the class, that is why it becomes shared when you create two instances. Therefore, to solve this problem, we can use constructor like what I have mentioned above. When we put the list in constructor, whenever the object is instantiated, the new list will also be created.

Skill creation in a python text based RPG

I am in the process of creating a text-based RPG for the purpose of learning more about OOP in Python. Right now things are going well, but I'm facing a problem that I don't exactly can find a good answer: Skill implementation. I also think that I'm going to face similar problem with the Items.
At this moment I have 3 classes(professions): Fighter, Rogue, and Mage. Each of these classes should have their own skills, but, skills have similar things(name, description, MP usage), so I created a BasicSkill class, and 3 subclasses: PassiveSkill, ActiveSkill and ChanelledSkill.
Explaining: PassiveSkill are skills that doesn't require MP to use, are always active, and in most of cases, give the player buffs, like dual-wielding, more resistance, etc. ActiveSkill are skills that require MP to be used, and, in majority, cause damage or a instant buff. Finally, ChanelledSkill are skills that require more than one turn to be used, cost more MP than the others, and can be interrupted
But, when comes to the skill creation, I can only think in something like this:
def hack_and_slash(self):
"""This is a Fighter skill, it's melee and has no cool down."""
from skills import ActiveSkill
hack_and_slash = ActiveSkill("Hack n' Slash", "A powerful double attack that has a chance of causing bleeding.",
8, 3)
self.mp -= hack_and_slash.
# more about the skill goes here, like what it does in terms of damaging, buffing, etc.
Which I think is not exactly what I'm aiming for, since I have to instantiate it every time I use, or something in the same lines.
I want to create the skills in a way that would require less instantiation, and also make more possible to cast. Also, all characters(player, npc, and enemies) can cast skills.
There's a bunch of files in this project, so I will post the ones I think are more relevant here, and, if you want, you can check out the project here.
This is my Player class:
from character import Character
# TODO implementation of player basic attacks and abilities
# TODO implementation of a D&D style skill system(diplomacy, stealth, streetwise, etc)
class Player(Character):
LEVEL_UP = 100 # initial XP to lvl up
COMMANDS = {'attack': 'a', 'character': 'c', 'inventory': 'i'} # commands available for the player
# in future this skills will be used inside and outside combat situations, for like take out an enemy in stealth,
# or convince a guard to let you in a closed door
SKILLS = {'persuasion': 0, 'intimidation': 0, 'stealth': 0, 'perception': 0}
DAILY_SELF_HEALS = 4 # each ingame day player can use 4 self heals, it resets after getting the rested status
ATTRIBUTES = {'strenght': 0, 'constitution': 0, 'dexterity': 0, 'intelligence': 0, 'wisdom': 0, 'charisma': 0}
def __init__(self, armor_class):
super().__init__(input("Tell us your name, hero:\n>"), 20, 10, {'gold': 10, 'torch': 1}, armor_class, 1)
self.exp = 0
self.max_hp = self.hp # max HP of the player
self.max_mp = self.mp # max MP of the player
self._strength = 0 # should I change it to dictionary with the attributes instead?
self._constitution = 0
self._dexterity = 0
self._intelligence = 0
self._wisdom = 0
self._charisma = 0
def level_up(self):
if self.exp >= self.LEVEL_UP:
self.lvl += 1
self.LEVEL_UP *= 1.25
self.LEVEL_UP = int(self.LEVEL_UP)
self.exp = 0
stat_choice = input(
"You have 2 points to spend in your attributes!\nType which attributes you want to raise up: ")
stat_choice = stat_choice.split(',')
self.ATTRIBUTES[stat_choice[0]] += 1
self.ATTRIBUTES[stat_choice[1]] += 1
self.max_hp = int(self.max_hp * 1.1)
self.max_mp = int(self.max_mp * 1.1)
return True
else:
return False
def gain_exp(self, exp):
self.exp += exp
print("You gained %d XP" % exp)
if self.level_up():
print("Congratulations, you gained a level!\nYour current level is %d\n" % self.lvl)
else:
print("Your current XP is %d/%d\n" % (self.exp, self.LEVEL_UP))
def get_loot(self, enemy):
# buggy right now, problem with remove inventory from dead enemy
for item in enemy.inventory:
while True:
get_item = input("You found %s! do you want to get it?(Y=yes/N=no/A=all items)" % item)
if get_item.lower() == "a":
self.inventory.update(enemy.inventory)
enemy.inventory = {}
return
elif get_item.lower() == "y":
self.inventory[item] = enemy.inventory[item]
enemy.inventory.pop(item, None)
break
elif get_item.lower() == "n":
break
else:
print("Unfortunately, you don't have this choice hero, take a look again...")
def combat(self, enemy):
"""
the combat between two entities(player and enemy)
:param enemy: the enemy of current character
:return:
"""
if super().combat(enemy):
self.gain_exp(10)
self.get_loot(enemy)
else:
print("\t\t[GAME OVER...]\t\t")
def self_heal(self):
if self.DAILY_SELF_HEALS > 0:
self.DAILY_SELF_HEALS -= 1
self.hp += int(self.max_hp / 4)
def rest(self):
rest_time = input("\tHow much time would you like to rest? ")
if rest_time >= 6:
self.STATUS['rested'] = True
def __str__(self):
str_info = "\tName: [%s]\tLEVEL: %d\n\tHP: %2d\t\tMP: %2d" % (self.name, self.lvl, self.hp, self.mp)
str_stats = "\t\tSTR: %2d\n\t\tCON: %2d\n\t\tDEX: %2d\n\t\tINT: %2d\n\t\tWIS: %2d\n\t\tCHA: %2d\n" % (
self._strength, self._constitution, self._dexterity, self._intelligence, self._wisdom,
self._charisma)
return "|%s|\n%s" % (str_info, str_stats)
This, my Character class:
# all character, being Player, NPC, or Enemy content should be implemented here
from utils import Dice
class Character:
"""
Main character class, all living entities derivate from it
"""
# a dictionary of possible status of player, the idea is that this status change gameplay/combat in the future.
# By default all characters are rested.
STATUS = {'rested': True, 'hunger': False, 'poisoned': False, 'bleeding': False, 'blind': False, 'frozen': False,
'paralyzed': False, 'dead': False}
# Right now there are only four equipment slot, which one should receive an armor object, not yet implemented
EQUIPMENT_SLOTS = {'head': None, 'chest': None, 'legs': None, 'boots': None}
def __init__(self, name, hp, mp, inventory, armor_class, lvl):
"""
Constructor for a character
:param name: name for the character
:type name: str
:param hp: hit points(health) for the character
:type hp: int
:param mp: magic points(mana) for the character
:type mp: int
:param inventory: inventory of character(gold, items, equips)
:type inventory: dict
:param armor_class: main defense
:type armor_class: int
:param lvl: level of character
:type lvl: int
:return: Character object
"""
self.name = name
self.hp = hp
self.mp = mp
self.inventory = inventory
self.armor_class = armor_class
self.lvl = lvl
self.atk_bonus = 1
self.atk_dmg = 4
self.movement_speed = 6
def take_dmg(self, dmg):
self.hp -= dmg
if self.hp <= 0:
print("\n[%s has died!]" % self.name)
self.STATUS['dead'] = True
else:
print("\n[%s has %d health left" % (self.name, self.hp))
def attack(self, enemy):
"""
All characters can attack, so this is the main attack(physical) method
:param enemy: Enemy that is in combat with character
:return:
"""
d20 = Dice(20)
dmg = self.atk_dmg
if d20.roll() + self.atk_bonus >= enemy.armor_class:
if d20 == 20:
print("CRITICAL HIT!")
dmg *= 2
else:
dice_dmg = Dice(self.atk_dmg)
dmg = dice_dmg.roll()
print("\n[%s hit %s for %d damage]" % (self.name, enemy.name, dmg))
enemy.take_dmg(dmg)
if enemy.STATUS['dead']:
return
else:
print("\n[%s missed]" % self.name)
def combat(self, enemy):
while True:
self.attack(enemy)
if enemy.STATUS['dead']:
return True
enemy.attack(self)
if self.STATUS['dead']:
break
return False
def equip_armor(self, armor_piece):
"""
Equip a piece of armor in the correspondent slot
:param armor_piece: armor piece to equip
:type armor_piece: object
:return:
"""
replace_equip = input(
"Would you like to replace %s with %s?(Y/N)" % (self.EQUIPMENT_SLOTS['head'], armor_piece.name))
if replace_equip.lower() == "y":
self.EQUIPMENT_SLOTS['head'] = armor_piece
return
else:
return
def list_inventory(self):
"""
list a character's inventory
:return:
"""
print("\t\t[%s] INVENTORY:\t\t\n" % self.name)
for item in self.inventory:
print("%s:\t%d" % (item, self.inventory[item]))
The Skill class:
# All basic skill implementation should be here
class BasicSkill:
SKILL_TYPE = {'passive': False, 'active': False, 'channeled': False}
def __init__(self, name, description, skill_type, skill_dmg=0, skill_range=0):
"""
Basic Skill class
:param name: skill name
:type name: string
:param description: description
:type description: string
:param skill_type: passive, active or channeled
:type skill_type: string
:param skill_dmg: default=0, meaning it's passive
:type skill_dmg: int
:param skill_range: default=0, meaning it's personal
:type skill_range: int
:return:
"""
self._name = name
self._description = description
self.SKILL_TYPE[skill_type] = True
self.skill_dmg = skill_dmg
self.skill_range = skill_range
class PassiveSkill(BasicSkill):
def __init__(self, name, description, skill_type='passive'):
super().__init__(name, description, skill_type)
class ActiveSkill(BasicSkill):
def __init__(self, name, description, skill_dmg, mp_use, cooldown=1, skill_type='active', skill_range=1):
super().__init__(name, description, skill_type, skill_dmg, skill_range)
self.mp_use = mp_use
self.cooldown = cooldown
class ChanneledSkill(BasicSkill):
def __init__(self, name, description, skill_dmg, channeling_time, cooldown=2, skill_type='chanelled',
skill_range=1):
super().__init__(name, description, skill_type, skill_dmg, skill_range)
self.channeling_time = channeling_time
self.cooldown = cooldown
and, as example, the Fighter class, all the classes/professions are implemented in similar way:
from player import Player
# TODO implement fighter stats and attacks
class Fighter(Player):
"""
Fighter class, a strong warrior that uses meele attacks to face his opponents
"""
def __init__(self):
super().__init__(armor_class=10)
print("\nYou have chosen the path of strength!")
self._strength += 2
self._constitution += 2
self.atk_bonus += 1
self.atk_dmg += 3
def hack_and_slash(self):
from skills import ActiveSkill
hack_and_slash = ActiveSkill("Hack n' Slash", "A powerful double attack that has a chance of causing bleeding.",
8, 3)
self.mp -= hack_and_slash.mp_use
def __str__(self):
super().__str__()
Any ideas are welcome, as well as code improvement tips. I tried to figure out a good way to make this skill implementation system, but I couldn't exactly find one. The only alternative I tough was a dictionary of skills, but I don't know how would it work. Anyway, thanks for the help.

Can I 'import' information to a class object from a previously defined function?

So i'm trying to make a very simple text-based RPG as my semester project for my programming class. I just recently learned (which is probably pretty apparent by my code) how to define classes and felt they would work much better than a function.
However, i'm having trouble with the 'character' class. Instead of having the player name predefined, I want the user to be able input their own name, which i've done in the 'Intro' function. Now my problem is taking the variable 'pName' and setting as the player's name, which I havent been able to do.
My questions are:
1. Can I do this? (use a function variable as a class attribute?)
2. Is there a better, more efficient way of doing this? And
3. Is there any additional information you guys could give me about classes or about how I can go about finishing this program?
Anything is well-appreciated and thanks in advance for the help!
import random, time
#I'm not at all hellbent on keeping this same format, it's just
#the way i've been taught and i'm most comfortable with.
def Intro():
print('Welcome puny warrior, to my Dungeon of DOOM, and stuff.')
pName = input('Tell me your name, puny warrior: ')
playResponse = input('Do you want to begin puny warrior who calls himself ' + pName + '? Y/N: ')
playResponse = playResponse.upper()
if playResponse[0:1] == 'Y':
pass
else:
print('You die now', pName)
class character(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
#this part obviously doesn't work, but I decided to leave it as a visual aid
player = character(pName, 25, 5)
#should I just make this class a child of the 'character' class?
class foes(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
zombie = foes('Zombie', 10, 3)
dragon = foes('Dragon',20, 5)
skeleton = foes('Skeleton', 8, 4)
You are trying to call internal Intro() variable - fast fix could be like this:
def Intro():
print('Welcome puny warrior, to my Dungeon of DOOM, and stuff.')
pName = input('Tell me your name, puny warrior: ')
playResponse = input('Do you want to begin puny warrior who calls himself ' + pName + '? Y/N: ')
playResponse = playResponse.upper()
if playResponse[0:1] == 'Y':
pass
else:
print('You die now', pName)
return pName
class character(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
#this part obviously doesn't work, but I decided to leave it as a visual aid
player = character(Intro(), 25, 5)
#should I just make this class a child of the 'character' class?
class foes(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
zombie = foes('Zombie', 10, 3)
dragon = foes('Dragon',20, 5)
skeleton = foes('Skeleton', 8, 4)
Some Pointers
Take a look at PEP8, the Style Guide for Python.
Use the Character Class to define both the player, and foes.
Implement a main() method and use it appropriately. For more information about this topic, visit this discussion.
Make use of Python dictionaries, as they are very powerful.
#!/usr/bin/env python
"""Simple, text-based RPG."""
import random # Generate pseudo-random numbers.
import time # Time access and conversions.
class Character(object):
"""Define the character."""
def __init__(self, name, health, attack):
self.name = str(name) # Expecting a string.
self.health = int(health) # Expecting an integer.
self.attack = int(attack) # Expecting an integer.
def properties(self):
"""Returns dictionary containing the character properties."""
# Because using dictionaries is awesome.
characteristics = {
'name': self.name,
'health': self.health,
'attack': self.attack
}
return characteristics # Returns a dictionary
def get_player():
"""Acquire the name of the player, and begin the game."""
# Newline characters should improve readability on the command line.
print('\nWelcome puny warrior, to my Dungeon of DOOM, and stuff.\n')
# Variable names should be easy to read and understand.
player_name = input(
"""
Welcome puny warrior, to my Dungeon of DOOM... and stuff.
Tell me your name: """
)
# Get and store the player's response.
player_response = input(
"""
Do you want to begin puny warrior who calls himself %s? (Y/N): \
""" % player_name
)
if player_response.upper() == "Y":
pass
else:
print('\nYou die now\n', player_name)
return player_name
def score():
"""Assign a score between 1 and 100."""
return random.randint(1, 100)
def main():
"""Where the automagic happens."""
# Assuming you want random integers.
player = Character(get_player(), score(), score()).properties()
zombie = Character('Zombie', score(), score()).properties()
dragon = Character('Dragon', score(), score()).properties()
skeleton = Character('Skeleton', score(), score()).properties()
# Since you're working with a dictictionary, you can now do things like:
print(player['name'])
print(zombie['name'], zombie['health'])
print("%s: %s, %s" % (dragon['name'], dragon['health'], dragon['attack']))
# The correct methodology.
if __name__ == "__main__":
main()
I'm not really sure what your question is. There's no difference to Python between using a variable like pName and a string like "Zombie" in a class instantiation. The only thing wrong with your code
is that you're doing that instantiation outside Intro(), so pName is not defined.

Categories