5
\$\begingroup\$

The goal is this "simple game".

The game is all about that you choose a "boom number" and then count one by one from 1. Every time that the player hits a number that can be divided by the "boom number" resulting in an integer.or that the "boom number" is a part of the number, the current player must shout "BUM!".

So If the "boom number" was 3, the game could proceed like the following example:

1, 2, BUM!, 4, 5 BUM!, 7 ,8 BUM!, 10, 11, BUM!, BUM!, 14, BUM!

Here, 3,6,9, 12 and 15 cause "BUM!" because they can all be divided by 3, and 3, because 3 is a part of that number.

Each player can choose to count one number up to three times meaning that each player will be asked up two times if they want to keep counting( the current player must say/write the first time). Example - The user chooses 3 players - Player one says 1 (the must), gets asked if we want to keep counting and says no - now it is player 2's turn he must say 2 and counts the two optional times so now the number is 3 - now it is player 3's turn and he must count the first time but the "boom number" was set to 4 so instead he would say/write "BUM!" and he is out - now it is player 1's turn...

Specifications:

  • implement a loop that counts from 1 to 100

  • Implement the function "checkForBum()" that accepts two integers, "num" and "boomnum".

  • The function must return:

    a. "BUM!" if "boomnum" can be divided with "num" resulting in an integer or if "num" is a part of "boomnum"

    b. The first parameter "num" unchanged

  • Use the function "checkForNum()" in the loop, so that the program plays the Boom Game

  • Let the user choose the "boom number"

  • Let the user have the option to choose an extra "boom number" and update the program so that both "boom numbers" counts

  • The game can be played 1-4 players, where if 1 player is chosen, the player will play against a very simple AI that counts one by one a random number of times (same rules as for the players).

Edit: It is up to you what way to ask the user what he/she wants to count up to.

How would you accomplish this? The thing is that I am normally programming in C++ and set up this task to see how it could be accomplished in Python.. and I have tried to do some of the things that I wanted (only about the half of the task) but since I do not know that much about python, it does not feel optimal. Hence, I would love to see somebody accomplish this task to have a look at how such things can be solved in Python when you really know about the language.

How can this code be optimized?(an accomplishment of the task/game stated above would be preferable though)

Code:

def checkForBum(tal, bummer):
    x = 0
    while x <= 100:
        if(x+int(bummer) <= 100):
            x+=int(bummer)
            if(int(tal) == x):
                return "BUM!"
        else:
            break

    return tal

def getInput(text):
    var = input(text)
    while not var.isdigit() and int(var) > 0:
        var = input("Whoops, it has to be a valid number - Try again: ")
    return int(var)

def gameon():
    bumtal = getInput("Choose the bum: ")
    extrabt = getInput("Extra bumtal(0 = none): ")
    tal = 0
    pnum = getInput("Number of player(s) (2+=2):  ")
    oner = 0
    twoer = 0
    turns = 1
    countbool = ""
    #recursion - prob overhead from function calls on the stack.. (negligible)..
    #could mean less code though
    for x in range(100):
            if(oner == twoer):
                countbool = input("1 player's turn - Count(Y/N)?")
                if (countbool == "N" or turns > 2):
                    turns = 0
                    oner += 1
            elif(oner > twoer):
                if(pnum > 1):
                    countbool = input("2 player's turn - Count(Y/N)?")
                else:
                    countbool = input("Bot's turn: Hit enter to let him count")
                if (countbool == "N" or turns > 2):
                    twoer += 1
                    turns = 0
            if(countbool == "Y"):
                turns += 1
                tal += 1
            for num in range(tal):
                isit = checkForBum(num+1, bumtal)
                if(int(extrabt) > 0 and isit != "BUM!"):
                    isit = checkForBum(num+1, extrabt)
                print(isit)



gameon()

Update: Please take a look at this linked question.

\$\endgroup\$
7
  • \$\begingroup\$ If I run the code, is it doing what you want, and you simply want to see if there is a more "Pythonic" or better way to do it? \$\endgroup\$
    – srattigan
    Sep 1, 2017 at 11:28
  • \$\begingroup\$ Well from what is described in the task description, I want the user to play against an AI if the chose singleplayer and the user also has the possibility of choosing to play multiplayer. I have actually tried my best to word what I want in the description above,, \$\endgroup\$
    – Ukula Udan
    Sep 1, 2017 at 11:31
  • \$\begingroup\$ My code does not have AI and does not the the user play with more than 2 players. Also, there are some other requirements that I have missed \$\endgroup\$
    – Ukula Udan
    Sep 1, 2017 at 11:32
  • \$\begingroup\$ But yea, also the cleanest and smartest'/most effecient way to do it :)) \$\endgroup\$
    – Ukula Udan
    Sep 1, 2017 at 11:35
  • 2
    \$\begingroup\$ Questions asking about Code not yet written are out of scope in this question. You may get a review about what you have currently written, but do not expect any answers regarding code that you haven't yet written yourself \$\endgroup\$
    – Ludisposed
    Sep 1, 2017 at 12:38

3 Answers 3

3
\$\begingroup\$

This code overlooks the modulo operator.

def checkForBum(tal, bummer):
x = 0
while x <= 100:
    if(x+int(bummer) <= 100):
        x+=int(bummer)
        if(int(tal) == x):
            return "BUM!"
    else:
        break

return tal

Your game is essentially a simplified version of fizzBuzz. If you want to check if a number is in the "boom" set you can check if it is divisible by your "boom" number. In your example you use the number 3.

The set will contain numbers: {3,6,9,12,15,18 ......} until the limit.

This is where the modulo operator comes in, it gives you the remainder of a division. So if you divide any number in the set by the "boom" number you will have a 0 remainder. (e.g. 18/3 = 6 R0). Any number not in the set will have a non-zero remainder.

Another really cool feature in python is that booleans and integers are pretty much the same thing. Any number not 0 is "true" and the number 0 is "false.

for example, 5%2 results in 1

10%5 has a truth value of "False"

checkForBum(tal, bum):
    return not tal%bum

would be a very good substitute.

A lot of the code in the gameon() function is redundant, why do you need two counters for player turns? Instead you should use a boolean and set it to True or false. Your high level logic will look like:

/* While Loop */
    if PlayerOneTurm:
        #do player one turn
    else: 
        #it's not player one's turn, do player two turn
    PlayerOneTurn = not PlayerOneTurn #flip the logic so that we execute the other block

Also I would consider creating a function for a human turn and a function for an AI turn. That way you don't clutter the "main" funciton (in this case it's gameon()) with all of the logic. A very basic rule that normally is good for first time programmers is to keep each function under 10 lines. Normally I would not pass on this advice because it can lead to bad practices but I think in this specific code case you should aim for this. It helps with readability of code and it allows you to start thinking in a more procedural way.

\$\endgroup\$
5
  • \$\begingroup\$ Uhh it is starting to look very nice! But for completeness sake, how would you create the function(s) for handling the user and the ai? \$\endgroup\$
    – Ukula Udan
    Aug 30, 2017 at 18:03
  • \$\begingroup\$ Also, the your "checkForBum" kinda breaks the "rules" that I set.. :) \$\endgroup\$
    – Ukula Udan
    Aug 30, 2017 at 18:05
  • \$\begingroup\$ the remaining implementation has been left as an exercise for the OP ;) \$\endgroup\$
    – Erich
    Aug 30, 2017 at 20:26
  • \$\begingroup\$ All right, i'll try. But can you take a look at it afterwards? \$\endgroup\$
    – Ukula Udan
    Aug 31, 2017 at 8:34
  • \$\begingroup\$ Hi again Erich, can you please check this follow-up question codereview.stackexchange.com/questions/174592/… \$\endgroup\$
    – Ukula Udan
    Sep 1, 2017 at 20:55
1
\$\begingroup\$

I'm not a professional coder but here are some things that are useless and or improved:

  • the if statement in the while loop is useless ( you can have only the x+= and the status check, i.e.: the nested if)
  • you don't need to initiate countbool and you could remove the oner and twoer variable and replace them with a single index int or a loop, something along the lines of:

    for player in range(pnum):
        countbol=input(" player number % 's turn, do you wish to count(Y/N)" % (player+1))
        if countbol.upper()=='Y':
            ...
    

The +1 is simply to offset the 0 start.

If you want to use more of a C or C++ style coding you should look into Cython.

\$\endgroup\$
1
\$\begingroup\$

A game class with player classes would seem to be a more optimal approach, or at least that's my first instinct, but it would be a different approach and need almost a complete re-write. Just looking at your specifications, I wrote the following for the checkForBum and getInput.

checkForBum() uses the modulo to check for equal division (as mentioned by another) and a rather nice way to check if the bum num is in the number being checked, by using strings.

getInput now takes two args, as you may not always want the number to be greater than 0 (see code to see what I mean).

I also included some case insensitivity for the user input (Y/N).

You should try to use docstrings in every function to convey both the types of data being input, and the return type from the function- when this is needed. I just put in a brief description, but try to use good documentation.

As an aside, and in line with Python convention, these could be modified to be bum_check() and get_input(), rather than the Java-style camelCase convention.

I am sure that the main function gameon() (or game_on()) could be improved but it would be a complete re-write, imo. Hopefully you will find the incremental improvements below both understandable, succinct and useful on your Python journey. :)

def checkForBum(num, boomnum):
    """
    must return: "BUM!" if "boomnum" can be divided with "num" 
    resulting in an integer or if "num" is a part of "boomnum" 
    """
    if (num % boomnum == 0) or str(num) in str(boomnum):
        return "BUM!"
    return num


def getInput(prompt, limit):  #
    """
    asks user for a boom number
    """
    var = input(prompt)
    while not var.isdigit() and int(var) > limit:
        var = input("Whoops, it has to be a valid number - Try again: ")
    return int(var)


def gameon():
    """
    Main game function
    """
    bumtal = getInput("You have to choose the bum (0 = none)", 0)  # num must be greater than 0
    extrabt = getInput("You can choose an extra bum", -1)  # allows zero to be chosen in getInput
    tal = 0
    pnum = getInput("Number of player(s) (2+=2):  ", 1)  # at least one player
    oner = 0
    twoer = 0
    turns = 1
    countbool = ""
    #recursion - prob overhead from function calls on the stack.. (negligible)..
    #could mean less code though
    for x in range(100):
            if(oner == twoer):
                countbool = input("1 player's turn - Count(Y/N)?")
                if (countbool.lower() == "N" or turns > 2):
                    turns = 0
                    oner += 1
            elif(oner > twoer):
                if(pnum > 1):
                    countbool = input("2 player's turn - Count(Y/N)?")
                else:
                    countbool = input("Bot's turn: Hit enter to let him count")
                if (countbool.lower() == "N" or turns > 2):  # account for case
                    twoer += 1
                    turns = 0
            if(countbool.lower() == "Y"):
                turns += 1
                tal += 1
            for num in range(tal):
                isit = checkForBum(num+1, bumtal)
                if(int(extrabt) > 0 and isit != "BUM!"):
                    isit = checkForBum(num+1, extrabt)
                print(isit)


if __name__ == '__main__':
    gameon()

This last part is just good practise, and means the code will not run if it were to be imported into another Python module.

\$\endgroup\$
3
  • \$\begingroup\$ when setting pnum, with the 1 being sent as an arg to the getInput function, it will not allow you to have only one player. Set this to 0 if you want to allow a single player game. \$\endgroup\$
    – srattigan
    Aug 31, 2017 at 23:45
  • \$\begingroup\$ thanks! But how would you implement AI and multiplayer based on what the user chose to play? Also, what would your version on "gameon" look like? I still have not seen a full accomplishment of the task. :( \$\endgroup\$
    – Ukula Udan
    Sep 1, 2017 at 11:24
  • \$\begingroup\$ Hello, can you please check the follow-up question codereview.stackexchange.com/questions/174592/… \$\endgroup\$
    – Ukula Udan
    Sep 1, 2017 at 20:55

Your Answer

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

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