3
\$\begingroup\$

I have been creating a scoring program for a game of hearts. If there is a an even number of players then players will pass across. And an odd number there is no pass across. I created these 2 functions that work fine. But I keep scratching my head trying to simplify them into only 1 function. I know its possible but I just can't get it right. This is the block of code I'm working with:

#this function is for an odd number of players
def no_across():
    global pass_counter
    if pass_counter == 1:
        print("WE ARE PASSING LEFT")
        pass_counter = pass_counter + 1
    elif pass_counter == 2:
        print("WE ARE PASSING RIGHT")
        pass_counter = pass_counter + 1
    elif pass_counter == 3:
        print("WE ARE HOLDING")
        pass_counter = pass_counter - 2

#this function is for an even number of players
def yes_across():
    global pass_counter
    if pass_counter == 1:
        print("WE ARE PASSING LEFT")
        pass_counter = pass_counter + 1
    elif pass_counter == 2:
        print("WE ARE PASSING RIGHT")
        pass_counter = pass_counter + 1
    elif pass_counter == 3:
        print("WE ARE PASSING ACROSS")
        pass_counter = pass_counter + 1
    elif pass_counter == 4:
        print("WE ARE HOLDING")
        pass_counter = pass_counter - 3

#determines which of the 2 previous functions to call
    if player_count % 2 == 0:
        yes_across()
    else:
        no_across()

As you can see in the first block and second block I have repeated code but I know it can be simplified. All help is appreciated.


EDIT: Below is the complete code. Don't judge me because I just whipped it together last night as a test to see if it would work. Also I am integrating this into a different project I have that will use a GUI. I just needed a rough copy of the program to work.

#this program is meant to keep track of score during a game of hearts

#displays a greeting
print("Welcome to the player score keeper!")

#score limit reached allows game to continue until changed
score_limit_reached = False

#these are the players names
John = 0
Elaine = 0
Austin = 0
Bridgette = 0
Rocky = 0

#these following variables help determine if the program should have certain
#passing in game. also keeps track of score limit.
player_count = 0
player_count = int(input("How many players are going to play? "))
score_limit = 0
pass_counter = 1
score_limit = int(input("What is the score limit? "))

#this function is for an odd number of players
def no_across():
    global pass_counter
    if pass_counter == 1:
        print("WE ARE PASSING LEFT")
        pass_counter = pass_counter + 1
    elif pass_counter == 2:
        print("WE ARE PASSING RIGHT")
        pass_counter = pass_counter + 1
    elif pass_counter == 3:
        print("WE ARE HOLDING")
        pass_counter = pass_counter - 2

#this function is for an even number of players
def yes_across():
    global pass_counter
    if pass_counter == 1:
        print("WE ARE PASSING LEFT")
        pass_counter = pass_counter + 1
    elif pass_counter == 2:
        print("WE ARE PASSING RIGHT")
        pass_counter = pass_counter + 1
    elif pass_counter == 3:
        print("WE ARE PASSING ACROSS")
        pass_counter = pass_counter + 1
    elif pass_counter == 4:
        print("WE ARE HOLDING")
        pass_counter = pass_counter - 3

#here begins the actual scoring section of the game
while score_limit_reached == False:
#determines which of the 2 previous functions to call
    if player_count % 2 == 0:
        yes_across()
    else:
        no_across()
#user input keeps track of score
    John = John + int(input("john: "))
    Elaine = Elaine + int(input("elaine: "))
    Austin = Austin + int(input("austin: "))
    Bridgette = Bridgette + int(input("bridgette: "))
    Rocky = Rocky + int(input("rocky: "))
#prints current score of each player
    print("\n\nJohn's score - " + str(John))
    print("Elaine's score - " + str(Elaine))
    print("Austin's score - " + str(Austin))
    print("Bridgette's score - " + str(Bridgette))
    print("Rocky's score - " + str(Rocky) + "\n\n")
#if the score limit has been reached it will end the while loop
    if John >= score_limit:
        score_limit_reached = True
    if Elaine >= score_limit:
        score_limit_reached = True
    if Austin >= score_limit:
        score_limit_reached = True
    if Bridgette >= score_limit:
        score_limit_reached = True
    if Rocky >= score_limit:
        score_limit_reached = True

#prints the final score for each player
print("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nJohn - " + str(John))
print("Elaine - " + str(Elaine))
print("Austin - " + str(Austin))
print("Bridgette - " + str(Bridgette))
print("Rocky - " + str(Rocky) + "\n")

#if any number of players has lost will declare each
#player a poopy, followed by which score was the highest
if John >= score_limit:
    print("John is a poopy!")
if Elaine >= score_limit:
    print("Elaine is a poopy!")
if Austin >= score_limit:
    print("Austin is a poopy!")
if Bridgette >= score_limit:
    print("Bridgette is a poopy!")
if Rocky >= score_limit:
    print("Rocky is a poopy!")

biggest_poopy = str(max(John, Elaine, Austin, Bridgette, Rocky))

print("\nThe biggest poopy is " + biggest_poopy + "!")
\$\endgroup\$
3
  • \$\begingroup\$ Can you post more of your code please =) Make it to a state where we can run your code \$\endgroup\$ Commented Jan 28, 2022 at 20:06
  • 2
    \$\begingroup\$ I have updated the post to include the entire code I am working with. \$\endgroup\$
    – crazyrocky
    Commented Jan 28, 2022 at 20:51
  • \$\begingroup\$ @crazyrocky I removed the '''s since those start a multi-line comment in python, though it seemed the intention was for a code fence to delimit a code block. Code fences are three or more backticks (i.e. `) or tildes (i.e. ~) \$\endgroup\$ Commented Jan 28, 2022 at 21:49

1 Answer 1

4
\$\begingroup\$

Yes, it can be easily done. Just pass some indication of parity into the function. The most straightforward way is to check player_count in the function like this:

def both_across():
    global pass_counter
    global player_count 
    if pass_counter == 1:
        print("WE ARE PASSING LEFT")
        pass_counter = pass_counter + 1
    elif pass_counter == 2:
        print("WE ARE PASSING RIGHT")
        pass_counter = pass_counter + 1
    elif pass_counter == 3 and player_count%2==0:
        print("WE ARE PASSING ACROSS")
        pass_counter = pass_counter + 1
    elif pass_counter == 3 and player_count%2==1:
        print("WE ARE HOLDING")
        pass_counter = pass_counter - 2
    elif pass_counter == 4:
        print("WE ARE HOLDING")
        pass_counter = pass_counter - 3

But this code is still bad in many ways; the most obvious bad thing is using globals instead of arguments and returning a value.

def both_across(counter, players):
    if counter == 1:
        print("WE ARE PASSING LEFT")
        return counter + 1
    elif counter == 2:
        print("WE ARE PASSING RIGHT")
        return counter + 1
    elif counter == 3 and players%2==0:
        print("WE ARE PASSING ACROSS")
        return counter + 1
    elif counter == 3 and players%2==1:
        print("WE ARE HOLDING")
        return counter - 2
    elif counter == 4:
        print("WE ARE HOLDING")
        return counter - 3
#somewhere down the code...
pass_counter = both_across(pass_counter, player_count)

Now, examining the function gives me an insight: in every branch we can find the returning value without calculations. If we know the value of counter, we can find the value of counter+1, counter-2 etc.:

def both_across(counter, players):
    if counter == 1:
        print("WE ARE PASSING LEFT")
        return 2
    elif counter == 2:
        print("WE ARE PASSING RIGHT")
        return 3
    elif counter == 3 and players%2==0:
        print("WE ARE PASSING ACROSS")
        return 4
    elif counter == 3 and players%2==1:
        print("WE ARE HOLDING")
        return 1
    elif counter == 4:
        print("WE ARE HOLDING")
        return 1

The last two elifs can be combined, probably in the same else:

    ...
    elif counter == 3 and players%2==0:
        print("WE ARE PASSING ACROSS")
        return 4
    else:
        print("WE ARE HOLDING")
        return 1

At this point it becomes clear that pass_counter values are in fact not integers but some symbols. They can be, say, strings - and even that strings we're printing:

pass_counter = "HOLDING"
def both_across(counter, players):
    if counter == "HOLDING":
        print("WE ARE PASSING LEFT")
        return "PASSING LEFT"
    elif counter == "PASSING LEFT":
        print("WE ARE PASSING RIGHT")
        return "PASSING RIGHT"
    elif counter == 3 and players%2==0:
        print("WE ARE PASSING ACROSS")
        return "PASSING ACROSS"
    else:
        print("WE ARE HOLDING")
        return "HOLDING"

And now, we can combine all prints:

pass_counter = "HOLDING"
def both_across(counter, players):
    if counter == "HOLDING":
        return "PASSING LEFT"
    elif counter == "PASSING LEFT":
        return "PASSING RIGHT"
    elif counter == 3 and players%2==0:
        return "PASSING ACROSS"
    else:
        return "HOLDING"

while not score_limit_reached:
    pass_counter = both_across(pass_counter, player_count)
    print("WE ARE", pass_counter)

Good enough?

For the rest of the code: Use functions. Don't leave the code just lying around, gather it into some functions and call them. Use lists and loops. You have the code for five names repeated - gather that into lists like

scores = [0,0,0,0,0]
names = ["John", "Elaine", "Austin", "Bridgette", "Rocky"]
...
for i in range(5):
    scores[i] += int(input(names[i]+': '))
...
for i in range(5):
    print(names[i] + " - " + str(scores[i]))

Use format strings. The last line can be rewritten as

    print(f'{names[i]} - {scores[i]}')

Don't stop learning. Learn classes. This code will make much more sense with classes.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.