Calculate score in a pyramid score system - python

I am trying to calculate gamescores for a bunch over users and I haven't really got it yet. It is a pyramid game where you can invite people, and the people you invite is placed beneth you in the relations tree.
So if i invite X and X invites Y i get kickback from both of them. Let's say 10%^steps...
So from X i get 10% of his score and 1% from Y, and X get 10% from Y.
So to calculate this i was thinking that each "player" had a function that calculated his total score. This function had to be recursive and "know" how far in the tree it was so that it would kick back the right values.
def get_score(player):
if children:
score = player.points
for child in children:
score += child.points*math.pow(.1, get_ancestors(child))
score += get_score(child)
return score
else:
return player.points
But this doesnt work proper, it gives what i believe is the right values in some levels but not in others. So think my function is broken. Anybody got an idea on how to solve this?

I doubt these two lines
score += child.points*math.pow(.1, get_ancestors(child))
score += get_score(child)
this is a simple recursive structure so i think something like below will suffice
score += get_score(child)*.1
and recursive beauty will take care of itself
you also do not need 'if children:' check
so does it help
def get_score(player):
score = player.points
for child in children:
score += get_score(child)*.1
return score

This can have very different implementations, depending on the way the score must be calculating :
Do you need to propagate the result of each gain, in real time ? In that case , you start from the bottom of the pyramid and give the feedback until the top.
Can you afford the result to be calculated at the end of the game for everybody ? In that cas you can just set a method on each player and only call the one at the top.
E.G for the second option
You used a functional approach. While this is valid, I am more into OO so I'll go this way :
class Player(object) :
def __init__(self) :
self.score = 0;
self.children = []
def updateScore(self) :
self.score = self.score + sum(((children.score * 10 / 100) for children in self.children))
class Pyramid(object) :
def __init__(self) :
self.top_child = Player()
def updateScore(self, player = None) :
if player == None :
player = self.top_child
for child in player.children :
self.updateScore(child)
child.updateScore()
You may want to use itertools to make it less CPU and memory intensive.

Related

Making a method generate a new number every time it is called?

goal: I'm trying to make it so when self.damage is called it generates a random number between 1 and the argument "damage". However this random number is only being picked the first time, and then used throughout the entire code. My current code looks like this:
import random
class PC(object):
def __init__(self, health, damage):
self.health = health
self.damage = random.randint(1,damage)
Player_character = PC(10,5)
Spider = PC(2,3)
print(Player_character.health)
def fightspider(spiderturn):
rounds = 0
while spiderturn == 1 and rounds < 2:
Spider.damage
Player_character.health = Player_character.health - Spider.damage
rounds += 1
print(Spider.damage)
print(Player_character.health)
def fightplayer(playerturn):
if playerturn == 1:
Spider.damage
fightspider(1)
When I say Spider.damage inside of the while loop I want it to select a new random number, currently it is only using the first one and not picking any new ones.
I'm not sure why this is happening or how to fix, can I get any leads on where to go with this?
This is my first time using classes so I'm still only halfway sure on how exactly they work!
Thanks for any help.
The damage is only be called in the initalizer of the class random.randint(1, damage) (in the __init__). I recommend you only initialize the max_damage in your initalizer then create a new function that will generate the damage for example:
import random
class PC(object):
def __init__(self, health, damage):
self.health = health
self.max_damage = max_damage
def get_damage():
return random.randint(1, self.max_damage)
Then instead of Spider.damage you can use Spider.get_damage() to get the damage generated everytime. Hope this makes sense, good luck on your programming endeavors!
You've tagged this question with "python-3.x", so I presume you're using 3.x
You're usingSpider.damage in two different ways:
You're using it as an expression that returns the current value stored in the damage attribute.
You're also expectng it to act as a function call that will somehow call random.randint() and re-assign a new random number to the damage attribute.
The first usage is ok. The second won't work.
I suggest you write a method called set_damage(), that will internally call
random.randint(), and update the damage attribute.

Python list doesn't seem to be updating

I'm fairly new to python and I'm trying to create blackjack. However, when I'm trying to print out what the player's hand is, I run into a few difficulties.
This is my code for hitting (drawing a card):
def hit(card, deck):
global money, choice
choice = input("How much would you like to bet?\n")
money -= int(choice)
print("You have decided to bet $" + str(choice))
card = card.drawCard(deck.deck)
card.getPt()
deck.addScore(card)
deck.addCard(card)
c = str(card)
p = str(deck)
print("You have drawn: " + str(c) + "\n")
print("The player has:\n" + str(p) + "\n")
print("Total score:", deck.score)
And this is my code for printing my cards out:
def __str__(self):
for i in range(0, len(self.deck)):
self.print = self.print + "\n" + str(self.deck[i])
return self.print
The first thing my code does is draw two cards for the dealer and the player, which runs fine. However, after the player draws a card is where it gets a bit wonky. The output is something like this:
The player has drawn Card A
The player has drawn Card B
Total score: number
How much would you like to bet?
number
You have bet number
You have drawn Card B
Player has:
Card A
Card B
Card A
Card B
Card B
When I draw a new card, the card doesn't change, it stays the last card I drew. Then, when I print my deck, it prints my old deck and my new deck. However, the score is correct, which indicates that my list is only three cards long. What is going on, and why is it printing five cards?
Full code
Example output
Well, to get to the answer in short: you never reset Deck.print. So it keeps accumulating at each call to __str__
But in general this code could be improved significantly
E.g. your __str__ function is far from being pythonic. Something along the lines of
return '\n'.join(self.deck)
would look much better.
In general there is no need to prepend each variable with "self." if they are used within the function only. In most cases in a class method you either update an object variable (self.something) or return some value from the function, but not both.
There may, of course, be exceptions to this, but it seems in your case this is the rule and not the exception.
Using globals is also something you should avoid whenever possible.

Self Limiting Repition Function

I'm writing a program that is basically a study guide/ practice test for the current section of my A&P class (it keeps me more engaged than just rereading notes over and over). The test works without any problems, but I have an issue where some of my questions use an "enterbox" input, I can have the question loop if the answer is incorrect, but I can't get it to break without a correct answer.
I figured out a way to make it work by putting the entire function back into the initial "else" tree, so that right or wrong you advance to the next question but it looks incredibly ugly and I can't believe there isn't a better way.
So my "solution" looks like such:
def question82():
x = "which type of metabolism provides the maximum amount of ATP needed for contraction?"
ques82 = enterbox(msg = x, title = version)
#version is a variable defined earlier
if ques82.lower() in ["aerobic"]:
add() #a function that is explained in the example further below
question83()
else:
loss() #again its a housecleaning function shown below
ques82b = enterbox(msg = x, title = version)
if ques82b.lower() in ["aerobic"]:
add()
question83()
else:
loss()
question83()
Okay so it worked, but using a nested if tree for each "enterbox" question looks kinda sloppy. I'm self taught so it may be the only solution but if there is something better I would love to learn about it.
So here is a complete section from my program:
from easygui import *
import sys
version = 'A&P EXAM 3 REVIEW'
points = 0
def add():
global points
msgbox("Correct", title = version)
points = points + 1
def loss():
global points
msgbox("Try Again", title = version)
points = points - 1
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version)
if ques81.lower() in ["creatine"]:
add()
question82()
else:
loss()
question81()
It works as is so any errors from what's provided are probably my fault from copy and pasting.
Also I'm running it in python 2.7rc1 if that helps.
Thanks for any help in advance.
I don't know if there is a way to combine "enterbox" that has a button for "skip" as that would also be a solution.
Consider the following approach:
We define a list of question and answer pairs. We do this in one place so it's easy to maintain and we don't have to search all over the file to make changes or re-use this code for a different questionset.
We create an ask_question function that we can call for all of our questions. This way, if we want to make a change about how we implement our question logic, we only have to make it in one spot (and not in each of the questionXX functions).
We compare user input to our answer using == and not in (in will do something else, not what you expect).
We create an object to keep track of our answer results. Here, it's an instance of ResultsStore, but it can be anything really, let's just try to get away from global variables.
Use a loop when prompting for answers. The loop will repeat if the answer given was incorrect (and if retry_on_fail is False).
Allow for the user to enter some "skip" keyword to skip the question.
Display the results once the "test" is complete. Here, we do that by defining and calling the store.display_results() method.
So, what about:
from easygui import enterbox
question_answer_pairs = [
("1 + 1 = ?", "2"),
("2 * 3 = ?", "6"),
("which type of metabolism provides the maximum amount of ATP needed for contraction?", "aerobic")
]
VERSION = 'A&P EXAM 3 REVIEW'
class ResultStore:
def __init__(self):
self.num_correct = 0
self.num_skipped = 0
self.num_wrong = 0
def show_results(self):
print("Results:")
print(" Correct:", self.num_correct)
print(" Skipped:", self.num_skipped)
print(" Wrong: ", self.num_wrong)
def ask_question(q, a, rs, retry_on_fail=True):
while True:
resp = enterbox(msg=q, title=VERSION)
# Force resp to be a string if nothing is entered (so .lower() doesn't throw)
if resp is None: resp = ''
if resp.lower() == a.lower():
rs.num_correct += 1
return True
if resp.lower() == "skip":
rs.num_skipped += 1
return None
# If we get here, we haven't returned (so the answer was neither correct nor
# "skip"). Increment num_wrong and check whether we should repeat.
rs.num_wrong += 1
if retry_on_fail is False:
return False
# Create a ResultsStore object to keep track of how we did
store = ResultStore()
# Ask questions
for (q,a) in question_answer_pairs:
ask_question(q, a, store)
# Display results (calling the .show_results() method on the ResultsStore object)
store.show_results()
Now, the return value currently doesn't do anything, but it could!
RES_MAP = {
True: "Correct!",
None: "(skipped)",
False: "Incorrect" # Will only be shown if retry_on_fail is False
}
for (q,a) in question_answer_pairs:
res = ask_question(q, a, store)
print(RES_MAP[res])
Quick and dirty solution could be using the default value "skip" for the answer:
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version, default = "skip")
if ques81.lower() == 'creatine':
add()
question82()
elif ques81 == 'skip':
# Do something
else:
loss()
question81()
But you should really study the answer given by jedwards. There's a lot to learn about
program design. He's not giving you the fish, he's teaching you to fish.

Answer Error, Only outputting Zero

I am coding in python and I cannot seem to figure out why when the amount of tickets sold is entered it does not calculate the full price of the tickets that were sold. Any help is appreciated, Thanks.
aLimit=300
bLimit=500
cLimit=100
aPrice=20
bPrice=15
cPrice=10
ticketSold=1
totalIncome=0
def Main():
getTickets(aLimit)
sectionIncome=calcIncome(ticketSold,aPrice)
sectionIncome+=totalIncome
print("The theater generated this much money from section A "+
str(sectionIncome))
getTickets(bLimit)
sectionIncome=calcIncome(ticketSold,bPrice)
sectionIncome+=totalIncome
print("The theater generated this much money from section B "+
str(sectionIncome))
getTickets(cLimit)
sectionIncome=calcIncome(ticketSold,cPrice)
sectionIncome+=totalIncome
print("The theater generated this much money from section C "+
str(sectionIncome))
print("The Theater generated "+str(totalIncome)+" total in ticket sales.")
def getTickets(limit):
ticketSold=int(input("How many tickets were sold? "))
if (ticketsValid(ticketSold,limit)==True):
return ticketSold
else:
getTickets(limit)
def ticketsValid(Sold,limit):
while (Sold>limit or Sold<0):
print("ERROR: There must be tickets less than "+
str(limit)+" and more than 0")
return False
return True
def calcIncome(ticketSold,price):
return ticketSold*price
Main()
How to debug your (small) program
Okay so let's start from the top and go line-by-line. There's a lot of issues here.
aLimit=300
bLimit=500
cLimit=100
aPrice=20
bPrice=15
cPrice=10
ticketSold=1
totalIncome=0
These are all globals since you defined them in the module scope. That's a Bad Thing. Don't do it. If they're constants, use CAPS to mention that, but they should still not be global.
def Main(): # I mentioned in my comment, but Capitals are classes
# by convention, use def main() instead
getTickets(aLimit)
Let's stop and look at getTickets() so we can follow execution
def getTickets(limit): # camelCase is not advised per PEP8, but it's still around
# so I wouldn't worry about this one so much as Capitalized
ticketSold=int(input("How many tickets were sold? "))
# perfect implementation, though be prepared for users who
# will type forty instead of 40!
if (ticketsValid(ticketSold,limit)==True):
return ticketSold
# so any time you write `if ___ == True`, stop and realize that the compare
# is unnecessary. if ticketsValid(ticketSold,limit) works just as well!
else:
getTickets(limit)
# wha-? If the tickets aren't valid, we're RECURSING??! This is an infinite loop.
# are you doing this to prompt for more input if tickets aren't valid? That's Bad
Okay so you invoked ticketsValid in there, so let's look there now...
def ticketsValid(Sold,limit): # another Capital here!
while Sold > limit or Sold < 0: # this should be an if??
print ("...")
return False
return True
# Since you have a set amount, this is MUCH easier written as:
## def ticketsValid(sold,limit):
## return 0 < sold < limit
# but should this be <=?
Alright, back to main--ahem--Main....
def Main():
...
sectionIncome = calcIncome(ticketSold,aPrice)
And hop back to calcIncome
def calcIncome(ticketSold,price):
return ticketSold*price # why not just substitute this???
Main again
def Main():
...
sectionIncome += totalIncome
# this sets sectionIncome equal to the current value of sectionIncome
# plus the current value of totalIncome, which is currently zero.
Then basically the whole thing gets repeated down the function. There's your issue, the += adds zero to sectionIncome instead of adding sectionIncome to totalIncome!
The better way to do this!
Here's the problem. You're trying to use functional programming to do object-oriented tasks. Most of these kind of issues are when new programmers who are interested in video games think that the best task to learn programming is a text adventure. Unfortunately, the best languages for text adventures (those that easily implement a Finite State Machine) are not usually those that beginners start with, so it's hard to implement WELL!
In your case, you should be creating objects to do the workload for you. Python does this elegantly, but it's rarely in beginning tutorials. As an example, I wrote out a bit that does exactly what you did (defines three sections of seating in a theater and sells one ticket per section)
class Section(object):
def __init__(self,price,limit):
self.price = price
self.tickets_sold = 0
self.limit = limit
#property
def sales(self):
return self.price*self.tickets_sold
def sell(self,qty=1):
if not isinstance(qty,int): raise TypeError("Must sell an int of tickets")
if qty < 1: raise ValueError("Must sell positive tickets")
qty = min(qty,self.limit-self.tickets_sold)
self.tickets_sold += qty
# optional print statement for the user
class Theater(object):
def __init__(self,sections):
self.sections = sections
#property
def sales(self):
return sum(section.sales for section in self.sections)
theater = Theater([Section(20,300),
Section(15,500),
Section(10,100)])
for section in theater.sections:
section.sell(1)
print(theater.sales)
The big problem with this is just that you don't know how to do it. Creating an object that will stay constant, then throw several instances of it around with specific attributes is precisely the approach I would favor in this circumstance.
read up on the += operator. totalIncome is only ever set to 0.
You need to replace sectionIncome+=totalIncome with totalIncome+=sectionIncome
Code
aLimit=300
bLimit=500
cLimit=100
aPrice=20
bPrice=15
cPrice=10
ticketSold=1
totalIncome=0
def Main():
getTickets(aLimit)
sectionIncome=calcIncome(ticketSold,aPrice)
totalIncome=sectionIncome
print("The theater generated this much money from section A "+str(sectionIncome))
getTickets(bLimit)
sectionIncome=calcIncome(ticketSold,bPrice)
totalIncome+=sectionIncome
print("The theater generated this much money from section B "+str(sectionIncome))
getTickets(cLimit)
sectionIncome=calcIncome(ticketSold,cPrice)
totalIncome+=sectionIncome
print("The theater generated this much money from section C "+str(sectionIncome))
print("The Theater generated "+str(totalIncome)+" total in ticket sales.")
def getTickets(limit):
ticketSold=int(input("How many tickets were sold? "))
if (ticketsValid(ticketSold,limit)==True):
return ticketSold
else:
getTickets(limit)
def ticketsValid(Sold,limit):
while (Sold>limit or Sold<0):
print ("ERROR: There must be tickets less than "+str(limit)+" and more than 0")
return False
return True
def calcIncome(ticketSold,price):
return ticketSold*price
Main()
sectionIncome+=totalIncome
This is the wrong way around. To add to totalIncome the value of sectionIncome, the syntax is
totalIncome+=sectionIncome
You will allso need to add global totalIncome to the beginning of Main -- or, better yet, do away with the global variables completely, like you have often been told before.
Finally, your getTickets function returns a calculated value, but the code which calls it doesn't store that result anywhere. Instead, you simply set ticketSold=1 in the global initialization, then use that as the multiplier instead of the actual number of tickets input by the user.
As a general observation, you have a lot of repetitive code, while the functions you do have are not very useful (they do not encapsulate complex behavior, nor repeated functionality). If you are a beginner, concentrate on a straightforward flow, then factor out complex repeating operations to loops or functions. Perhaps something like this:
def income ():
total = 0
for ticket_type, limit, price in [('A', 300, 20),
('B', 500, 15),
('C', 100, 10)]:
print "How many %s tickets sold? (Max %d):" % (ticket_type, limit),
sold = -1
while sold < 0 or sold > limit:
if sold != -1:
print "Sorry. Try again:",
sold = raw_input()
try:
sold = int(sold)
except:
pass
total += sold*price
print "total %d" % total
income()
Please also note the user-friendly gesture of indicating the maximum allowed for each category in the input prompt (-:
The loop driver is a bit of a wart; a slicker way to do that would be to declare a class TicketCategory and have three instances A, B, and C with methods to report back their name, price, and available amounts, but that's for later.

Python Data Structure Selection

Let's say I have a list of soccer players. For now, I only have four players. [Messi, Iniesta, Xavi, Neymar]. More players will be added later on. I want to keep track of the number of times these soccer players pass to each other during the course of a game. To keep track of the passes, I believe I'll need a data structure similar to this
Messi = {Iniesta: 4, Xavi: 5 , Neymar: 8}
Iniesta = {Messi: 4, Xavi: 10 , Neymar: 5}
Xavi = {Messi: 5, Iniesta: 10 , Neymar: 6}
Neymar = {Messi: 8, Iniesta: 5 , Xavi: 6}
Am I right to use a dictionary? If not, what data structure would be better suited? If yes, how do I approach this using a dictionary though? How do I address the issue of new players being included from time to time, and creating a dictionary for them as well.
As an example, If I get the first element in the list, List(i) in the first iteration is Messi, how do i use the value stored in it to create a dictionary with the name Messi. That is how do i get the line below.
Messi = [Iniesta: 4, Xavi: 5 , Neymar: 8]
It was suggested I try something like this
my_dynamic_vars = dict()
string = 'someString'
my_dynamic_vars.update({string: dict()})
Python and programming newbie here. Learning with experience as I go along. Thanks in advance for any help.
This is a fun question, and perhaps a good situation where something like a graph might be useful. You could implement a graph in python by simply using a dictionary whose keys are the names of the players and whose values are lists players that have been passed the ball.
passes = {
'Messi' : ['Iniesta', 'Xavi','Neymar', 'Xavi', 'Xavi'],
'Iniesta' : ['Messi','Xavi', 'Neymar','Messi', 'Xavi'],
'Xavi' : ['Messi','Neymar','Messi','Neymar'],
'Neymar' : ['Iniesta', 'Xavi','Iniesta', 'Xavi'],
}
To get the number of passes by any one player:
len(passes['Messi'])
To add a new pass to a particular player:
passes['Messi'].append('Xavi')
To count the number of times Messi passed to Xavi
passes['Messi'].count('Xavi')
To add a new player, just add him the first time he makes a pass
passes['Pele'] = ['Messi']
Now, he's also ready to have more passes 'appended' to him
passes['Pele'].append['Xavi']
What's great about this graph-like data structure is that not only do you have the number of passes preserved, but you also have information about each pass preserved (from Messi to Iniesta)
And here is a super bare-bones implementation of some functions which capture this behavior (I think a beginner should be able to grasp this stuff, let me know if anything below is a bit too confusing)
passes = {}
def new_pass(player1, player2):
# if p1 has no passes, create a new entry in the dict, else append to existing
if player1 not in passes:
passes[player1] = [player2]
else:
passes[player1].append(player2)
def total_passes(player1):
# if p1 has any passes, return the total number; otherewise return 0
total = len(passes[player1]) if player1 in passes else 0
return total
def total_passes_from_p1_to_p2(player1, player2):
# if p1 has any passes, count number of passes to player 2; otherwise return 0
total = passes[player1].count(player2) if player1 in passes else 0
return total
Ideally, you would be saving passes in some database that you could continuously update, but even without a database, you can add the following code and run it to get the idea:
# add some new passes!
new_pass('Messi', 'Xavi')
new_pass('Xavi', 'Iniesta')
new_pass('Iniesta', 'Messi')
new_pass('Messi', 'Iniesta')
new_pass('Iniesta', 'Messi')
# let's see where we currently stand
print total_passes('Messi')
print total_passes('Iniesta')
print total_passes_from_p1_to_p2('Messi', 'Xavi')
Hopefully you find this helpful; here's some more on python implementation of graphs from the python docs (this was a fun answer to write up, thanks!)
I suggest you construct a two dimensional square array. The array should have dimensions N x N. Each index represents a player. So the value at passes[i][j] is the number of times the player i passed to player j. The value passes[i][i] is always zero because a player can't pass to themselves
Here is an example.
players = ['Charles','Meow','Rebecca']
players = dict( zip(players,range(len(players)) ) )
rplayers = dict(zip(range(len(players)),players.keys()))
passes = []
for i in range(len(players)):
passes.append([ 0 for i in range(len(players))])
def pass_to(f,t):
passes[players[f]][players[t]] += 1
pass_to('Charles','Rebecca')
pass_to('Rebecca','Meow')
pass_to('Charles','Rebecca')
def showPasses():
for i in range(len(players)):
for j in range(len(players)):
print("%s passed to %s %d times" % ( rplayers[i],rplayers[j],passes[i][j],))
showPasses()

Categories

Resources