The program I am trying to create involves writing a method called monster_fight(monster1,monster2) to have the two monsters "Fight". However I am having one issue retrieving the damage value stored in each of the monster object dictionaries named 'self.attacks'.
I am trying to retrieve a value from dictionary 'monster1.attacks' to reduce the hp of the monster2 object. However with the current code I have in place, the program does not recognize the value of the keys when I call the dictionary. Can anybody show what I am doing wrong?
Thanks!
class Monster():
def __init__(self, name, max_hp = 20, hp=20):
self.name = name
self.type = type
self.current_hp = max_hp
self.attacks = {'wait': 0}
self.possible_attacks = {'sneak_attack': 1,
'slash': 2,
'ice_storm': 3,
'fire_storm': 3,
'whirlwind': 3,
'earthquake': 2,
'double_hit': 4,
'wait': 0}
self.exp = 0
def add_attack(self, attack_name):
if attack_name in self.possible_attacks:
self.attacks[attack_name] = self.possible_attacks.get(attack_name)
return True
else:
return False
if attack_name in self.attacks:
return False
def remove_attack(self, attack_name):
if attack_name in self.attacks:
self.attacks.pop(attack_name)
if len(self.attacks) == 0:
self.attacks['wait'] = 0
return True
else:
return False
def win_fight(self):
self.exp += 5
self.current_hp = self.max_hp
def lose_fight(self):
self.exp += 1
self.current_hp = self.max_hp
def monster_fight(monster1,monster2):
round1 = 0
moves1 = []
moves2 = []
list1 = []
list2 = []
for i in monster1.attacks:
values = ''
values = monster1.attacks.get(i)
list1.append(values)
for i in range(0,len(monster2.attacks)):
values = monster2.attacks.get(i)
list2.append(values)
while monster1.current_hp > 0 or monster2.current_hp > 0:
round1 += 1
monster1_attack = int(monster1.attacks[list1[(round1-1)%len(list1)]])
monster2.current_hp -= monster1_attack
moves1.append(list1[(round1-1)%len(list1)])
monster2_attack= monster2.attacks[list2[(round1-1)%len(list2)]]
monster1.current_hp -= monster2_attack
moves2.append(list2[(round1-1)%len(list2)])
if monster1.current_hp <= 0:
monster1.lose_fight()
monster2.win_fight()
return round1, monster2.name, moves2
elif monster1.current_hp <= 0:
monster2.lose_fight()
monster1.win_fight()
return round1,monster1.name, moves1
else:
return -1,"None","None"
a = Monster("a", 9)
b = Monster("b", 9)
a.add_attack("ice_storm")
b.add_attack("ice_storm")
b.remove_attack("wait")
a.remove_attack("wait")
round1, winner, moves = monster_fight(a, b)
print(round1)
print(winner.name)
print(moves)
monster1_attack = int(monster1.attacks[list1[(round1-1)%len(list1)]])
KeyError: 3
Well, let's see. You're calling:
monster1_attack = int(monster1.attacks[list1[(round1-1)%len(list1)]])
monster1 is a Monster object, created from:
a = Monster("a", 9)
a.add_attack("ice_storm")
a.remove_attack("wait")
So monster1.attacks looks like:
{
'ice_storm': 3,
}
You're trying to access that dictionary using key dervied from list1[(round1-1)%len(list1)].
list1 is set here:
for i in monster1.attacks:
values = ''
values = monster1.attacks.get(i)
list1.append(values)
After the above code runs, list1 is a list that looks like:
[3]
(Because you ask for monster1.attacks.get(i), which will return the value associated with key i.)
So when you ask for list1[(round1-1)%len(list1)], you get the value 3, which means you're asking for monster1.attacks[3].
There is no key named 3 in monster1.attacks. As we saw earlier, the only key is ice_storm which has the value 3. It looks like you're trying to figure out the damage that monster1's attack will do. That is actually what you have in list1, so in theory you could just write:
monster1_attack = list1[(round1-1)%len(list1)]
monster2.current_hp -= monster1_attack
I think your logic here may be a bit convoluted. You should probably think carefully about exactly what you're trying to accomplish and try to simplify your code with that goal in mind. Using the Python debugger to see the value of your variables prior to the error -- or using print statements to accomplish the same thing -- can help diagnose this sort of problem.
Related
First of all, thank you for reading it. I am new to Python and learning something new every day.
I wrote a function where inputs are 4 variables and output are 4 variables.
My problem is variables are getting defined somewhere else and it's calling the script. Now, what if the user only defines 3 variables in that case I automatically want a 4th variable to get assigned zero value. I also included another code that I am trying to use for the same purpose.
volsum = vol1 + vol2 + vol3 + vol4
if vol1n == missing:
vol1n = 0
else:
vol1n=vol1/volsum
if vol2n == missing:
vol2n = 0
else:
vol2n=vol2/volsum
if vol3n = missing:
vol3n = 0
else:
vol3n=vol3/volsum
if vol4n == missing:
vol4n = 0
else:
vol4n=vol4/volsum
or maybe using a function
def vol(vol1,vol2,vol3,vol4):
volsum = vol1 + vol2
vol1n=vol1/volsum
vol2n=vol2/volsum
vol3n=vol3/volsum
vol4n=vol4/volsum
return vol1n, vol2n,vol3n,vol4n
Try Assigning Values when you Define the Fucntion:
def vol(vol1 = 0,vol2 = 0,vol3 = 0,vol4 = 0):
volsum = vol1 + vol2
vol1n = vol1 / volsum
vol2n = vol2 / volsum
vol3n = vol3 / volsum
vol4n = vol4 / volsum
return vol1n, vol2n,vol3n,vol4n
Now suppose you want to skip vol2:
variable_assigned = vol(vol1 = 5,vol3 = 2,vol4 = 5)
If you require a One-Line (I love creating one):
#defining vol()
def vol(*kwargs): return [i/sum(kwargs) for i in kwargs]
# using vol()
#example
vol (1,2,3)
#returns
[0.16666666666666666, 0.3333333333333333, 0.5]
# so to assign variables,
#if you give 3 values,
vol1n, vol2n, vol3n = vol(1,2,3)
# if 4,
vol1n, vol2n, vol3n, vol4n = vol(1,2,3,5)
A little Fun (Not recommended):
Lets say your vol1 =1, vol2 = 5 vol3 = 3 ...voln = 10
So let:
vol = [ 1, 5, 3, ... n values, 10]
Now if you run the following code:
for j in range(len(vol)): exec(f'vol{j+1}n = [i/sum(vol) for i in vol][j]')
This code will automatically create your vol1n, vol2n, ... volnn variables automatically.
Rather than a 0, could it be forgotten? Personally, I don't like typing things out when they don't need to be. Try the *args feature out.
def vol_func(*args):
volumesum = sum(args)
vals = []
for i in range(len(args)):
vals.append(args[i] / volumesum)
return vals
volsumlst = vol_func(2.3, 4)
You can check if a variable is defined or not using 'locals' function. Alternatively if variables are defined globally, then you should use 'globals' function.
if 'vol1n' not in locals():
vol1n = 0
else:
vol1n=vol1/volsum
if 'vol2n' not in locals():
vol2n = 0
else:
vol2n=vol2/volsum
if 'vol3n' not in locals():
vol3n = 0
else:
vol3n=vol3/volsum
if 'vol4n' not in locals():
vol4n = 0
else:
vol4n=vol4/volsum
I have a class for a Dialogue system as follows
class DIALOGUE(object):
def __init__(self, place, who, sTime, eTime, isActive, mood, menuText, func, repeatable, num):
self.place = place
self.who = who
self.sTime = sTime
self.eTime = eTime
self.isActive = isActive
self.mood = mood
self.menuText = menuText
self.func = func
self.repeatable = repeatable
self.num = num
#property
def ACheck(self):
global Date
if self.sTime == "none":
return True
else:
tHour,tMin = self.sTime.split(":")
if tHour >= Date.Hour and tMin <= Date.Minute:
tHour,tMin = self.eTime.split(":")
if tHour < Date.Hour and tMin < Date.Minute:
return True
return False
#property
def BCheck(self):
global Act
if self.who == Act:
return True
else:
return False
#property
def CCheck(self):
global Location
if self.place == Location:
return True
if self.place == "none":
return True
return False
#property
def DCheck(self):
if self.repeatable:
return True
else:
if self.num > 0:
return False
else:
return True
#property
def CanChat(self):
if self.isActive and self.ACheck and self.BCheck and self.CCheck and self.DCheck:
return True
else:
return False
def SetActive(self):
self.isActive = True
def Do(self):
self.num += 1
renpy.call(self.func)
Most of this should be self explanatory but I parse an XML file into a list of Instances of this class.
The user is presented with a list of available dialogues based on what Location they are in, what time of day it is and what NPC they have selected. If the dialogue is not repeatable The DCheck method looks at whether or not the dialogue has been completed before i.e if the dialogue is not repeatable and self.num > 0 the method will return False
Essentially it loops through all the dialogues and carries out i.CanChat and if this value returns True, the Dialogue is added to the menu
The issue I'm having is that the Check methods aren't returning the correct value. Specifically DCheck is returning True all the time, regardless of whether the Dialogue is repeatable or not, and ignoring the value of self.num
The class is created in an init python: block and then the xml file is parsed in a separate python block which is called from inside the start label
It's probably something really simple but I can't figure it out.
The list of instances is parsed as follows
Dialogues = []
for j in Dialo:
JPlace = j.find('Place').text
JWho = j.find('Who').text
JsTime = j.find('Start').text
JeTime = j.find('End').text
JMood = int(j.find('Mood').text)
JText = j.find('Text').text
JFunc = j.find('Func').text
JRep = j.find('Rep').text
if JRep == "True":
Jrep = True
else:
Jrep = False
Dialogues.append(DIALOGUE(JPlace, JWho, JsTime, JeTime, False, JMood, JText, JFunc, JRep, 0))
The method for creating the menu is as follows
def TalkCheck():
talks = []
talks.append(("Nevermind.", "none"))
for i, q in enumerate(Dialogues):
if q.CanChat:
talks.append((q.menuText,i))
renpy.say(None, "", interact=False)
talkchoice = renpy.display_menu(talks)
if talkchoice <> "none":
talkchoice = int(talkchoice)
Dialogues[talkchoice].Do()
Your question is incomplete - you didn't post a MCVE, we don't know the effective values for "repeatble" and "num" that leads to this behaviour, and we don't even know if it's using Python 2.x or Python 3.x - so we can just try and guess. Now since you mention that you "parse an XML file into a list of instances", I stronly suspect you are running Python 2.x and passing those values as strings instead of (resp.) boolean and int. In Python 2, "-1" (string) compares greater than 0 (int) - it raises a TypeError in Python 3.x -, and in both cases a non-empty string evals to True in a boolean context (bool('False') == True). Since there's no obvious logical error in your method implementation, that's the only explanation I can think of.
BTW, expressions have a boolean values and return exits the function, so you can simplify your code:
#property
def DCheck(self):
if self.repeatable:
return True
return self.num > 0
I'm trying to create two subclasses based on the same parent class, so that they each have their own versions of the same variables defined in the parent object. However I realized that changing these variables in one of these subclasses will cause the versions in the other subclass to change as well. I know I am probably not fully understanding the idea of Inheritance. Please help!
import random
class PlayerParent():
id = 1
# Cooperate: True; Betrayal: False
opponent_moves_history = {}
self_moves_history = {}
def append_opponent_history(self, round_num, c_true, misunderstand=0.0):
# randomly change the result based on probability given in misunderstand
random_num = random.uniform(0, 1)
if random_num <= misunderstand:
c_true = not c_true
self.opponent_moves_history[round_num] = c_true
def append_self_history(self, round_num, c_true, misunderstand=0.0):
# randomly change the result based on probability given in misunderstand
random_num = random.uniform(0, 1)
if random_num <= misunderstand:
c_true = not c_true
self.self_moves_history[round_num] = c_true
score = int(0)
def score_keeper(self, round_num):
if (self.opponent_moves_history[round_num] == True) and (self.self_moves_history[round_num] == False):
self.score += 7
if (self.opponent_moves_history[round_num] == True) and (self.self_moves_history[round_num] == True):
self.score += 5
if (self.opponent_moves_history[round_num] == False) and (self.self_moves_history[round_num] == True):
self.score += 1
if (self.opponent_moves_history[round_num] == False) and (self.self_moves_history[round_num] == False):
self.score += 2
def get_score(self):
return self.score
class TitForTat(PlayerParent):
def rule(self, round_num):
if len(self.opponent_moves_history) == 0:
return True
else:
return self.opponent_moves_history[round_num - 1]
class Random(PlayerParent):
def rule(self, round_num):
random_num = random.uniform(0, 1)
if random_num >= 0.5:
return True
else:
return False
Random = Random()
Random.id = 1
TitForTat = TitForTat()
TitForTat.id = 2
def match(a, b):
game_counter = 1
# while game_counter <= 10:
#a_result = a.rule(game_counter)
# b_result = b.rule(game_counter)
# print(a_result, b_result)
# a.append_self_history(game_counter, a_result)
# b.append_opponent_history(game_counter, a_result)
# b.append_self_history(game_counter, b_result)
# a.append_opponent_history(game_counter, b_result)
# a.score_keeper(game_counter)
# b.score_keeper(game_counter)
# game_counter += 1
# print(a.get_score(), b.get_score())
a.self_moves_history[1] = True
print(a.self_moves_history, '\n', b.self_moves_history)
match(Random, TitForTat)
Resulting a.self_moves_history and b.self_moves_history is identical even though no alteration has been done to the b class variable.
I commented out chunks of the codes just to test where went wrong.
You are making opponent_moves_history a class variable, so naturally any change to it is class-wide.
In your case you should make opponent_moves_history, along with self_moves_history and id instance variables instead, so that changes made to them are specific to the instances.
class PlayerParent():
def __init__(self):
self.id = 1
self.opponent_moves_history = {}
self.self_moves_history = {}
This question already has answers here:
New instance of class with a non-None class attribute?
(4 answers)
Closed 5 years ago.
I've been working on an implementation for a problem from HackerRank. Namely this one: https://www.hackerrank.com/challenges/battleship1p/problem
My language of choice is Python3 and it will be a good fit, I guess. Now here is a basic construct on which my plan was, to build the project upon:
class Cell():
# - status open
# h hit
# m miss
neighboring = []
status = None
cell_x = None
cell_y = None
def __init__(self, cell_x, cell_y):
self.cell_x = cell_x
self.cell_y = cell_y
status = '-'
def check_status(self):
return self.status
def check_ut(self):
if (self.status == "-"):
return True
def check_hit(self):
if (self.status == "h"):
return True
def check_miss(self):
if (self.status == "m"):
return True
def add_neighboring(self, c):
self.neighboring.append(c)
def check_neighboring(self):
for x in self.neighboring:
if (x.check_ut()):
return x
def check_neighboring_is_hit(self):
if self.check_hit():
for x in self.neighboring:
if (x.check_ut()):
return x
class Row():
Cells = []
y = None
def __init__(self, y):
for i in range(10):
self.Cells.append(Cell(i, y))
class Board():
Rows = None
def populate_neighbors(self):
for l in self.Rows:
for c in l.Cells:
if (c.cell_x > 0):
prev_cell = l.Cells[c.cell_x - 1]
prev_cell.add_neighboring(c)
c.add_neighboring(prev_cell)
if (c.cell_y > 0):
above_cell = self.Rows[c.cell_y - 1].Cells[c.cell_x]
above_cell.add_neighboring(c)
c.add_neighboring(above_cell)
print("test")
def NewRow(self):
self.Rows.append(Row(len(self.Rows)))
def __init__(self, rows):
self.Rows = []
for i in range(rows):
self.NewRow()
list_ships = [1, 1, 2, 2, 3, 4, 5]
z = Board(10)
z.populate_neighbors()
It tries to rebuild a Board of a player, which I initalise with 10 rows Board(10) and it should also create 10 of Cells per row. But because of something happening in the background it seems to create 100 fields per row, at least my debugger says that. I would appreciate if you can give me a hind where duplication or recreation or something like that happens.
In populate_neighbors, my goal was to find all adjacent cells for a particular cell, by iterating through first all rows and then cell by cell, to add to the previous the current selected and to the current selected the previous, same for the height of the gamefield. Now I hope you understood what this intentionally was about.
Well, in your code Row.Cells is a class attribute and is shared between all of the instances of Row. The loop always append to the same list that's why we have 100 cells. To fix you will need:
class Row():
Cells = [] # this is not a field declaration like Java
y = None
def __init__(self, y):
self.Cells = [] # need this
for i in range(10):
self.Cells.append(Cell(i, y))
I'd recommend you to re-read the basics of Python.
Why is it giving me an error " 'int' object is not subscriptable " when i run the program? I looked if i was doing anything wrong, i understand it has to be an integer on line 24, but when I'm changing capacity[1] to capacity(int[1]) , it gives me the same error. Any hint would be appreciated.
class Bag():
__slots__=('name', 'weight', 'value')
def mkBag(name, weight, value):
thisBag = Bag()
thisBag.name = name
thisBag.weight = weight
thisBag.value = value
return thisBag
def ratio(treasure):
print(treasure)
print(treasure)
return treasure[2]//treasure[1]
def plunder(treasure, capacity):
treasure = sorted(treasure, key=ratio, reverse=True)
bagLst = []
current = 0
while current < capacity:
if capacity != 0:
if capacity > current[1]:
bagLst.append(mkBag(treasure[0],weight[1],current[2]))
capacity = capacity - current[1]
else:
bagLst.append(mkBag(current[0], capacity, (current[2]/current[1]), capacity))
capacity = 0
return bagLst
def main():
capacity = 10
name = ''
weight = 0
value = 0
treasure = [('silver', 20, 100), ('platinum', 10, 400), ('paladium',10,800), ('diamonds',5,900), ('gold', 10,60)]
bagLst = plunder(treasure, capacity)
for line in bagLst:
print('bagLst')
current is an int:
current = 0
but you are trying to use it as a list:
if capacity > current[1]:
bagLst.append(mkBag(treasure[0],weight[1],current[2]))
capacity = capacity - current[1]
else:
bagLst.append(mkBag(current[0], capacity, (current[2]/current[1]), capacity))
everywhere you use current[index] you are trying to index the integer value.
If you expected current to be a sequence instead, you'd need to set it to one.
I suspect you want to inspect the current treasure to add to the bag; you didn't pick any treasure item however. Something along the lines of:
current = 0
while capacity and current < len(treasure):
item = treasure[current]
current += 1
if capacity > item[1]:
bagLst.append(mkBag(item[0], item[1], item[2]))
capacity = capacity - item[1]
else:
bagLst.append(mkBag(item[0], capacity, (item[2]/item[1]), capacity))
capacity = 0
"int" object not subscriptable means you're trying to do 1234[1]. That doesn't make any sense! You can subscript a string ('abcdefg'[1] == 'b') and a list ([1,2,3,4,5][1] == 2) but you can't get the "nth element" of an integer.
In your line:
# in def plunder(...):
if capacity > current[1]:
You're trying to access the 2nd element of current, which is currently equal to the integer 0. Are you trying to make that a list? What are you expecting to be in current[1]?
Here's a substantially better way to accomplish this
Hey there, so I figured you meant that current[1] was actually item[1], meaning the weight of the item you were looking at. Instead, current was intended to be the running-weight of the bag. Understood! That said, I wrote up a better solution for this: take a look see!
class Treasure(object):
def __init__(self,name,weight=0,value=0,id_=0):
self.name = name
self.weight = weight
self.value = value
self.id = id_ # bootstrap for further development
#property
def ratio(self):
return self.value/self.weight
class BagFullError(ValueError):
pass
class Bag(object):
def __init__(self,owner=None,capacity=10):
self.owner = owner
self.capacity = capacity
self.contents = list()
def __str__(self):
return_value = "CONTENTS:"
for item in self.contents:
return_value += "\n ${0.value:4} {0.name:10}{0.weight} lb".format(item)
return return_value
def add(self,other):
if not isinstance(other,Treasure):
raise TypeError("Must pick up Treasure")
if self.weight + other.weight > self.capacity:
raise BagFullError("Bag cannot fit {}({} lb) ({} lb/{} lb)".format(
other.name,other.weight,self.weight,self.capacity))
self.contents.append(other)
def remove(self,other):
self.contents.remove(other)
# may throw ValueError if `other` not in `self.contents`
#property
def weight(self):
return sum(item.weight for item in self.contents)
treasure = [Treasure('silver', 20, 100), Treasure('platinum', 10, 400),
Treasure('paladium',10,800), Treasure('diamonds',5,900),
Treasure('gold', 10,60)]
## map(lambda x: Treasure(*x), [('silver',20,100), ... ])
def plunder(treasure_list,bag=None):
_bag = bag or Bag()
treasures = sorted(treasure_list,
key = lambda x: x.ratio,
reverse = True)
while True:
for treasure in treasures:
try: _bag.add(treasure)
except BagFullError as e:
print(e)
return _bag
bag = Bag("Adam",100)
print(bag)
plunder(treasure,bag)
print(bag)
print("Total Value: {}".format(sum(item.value for item in bag.contents)))