2
\$\begingroup\$

I did another exercise, this time it simulates n games of craps(dice game) and outputs the wins and win percentage!

#craps.py -- Simulates multiple games of craps
#   and estimates the probability that the player wins.

import random

def output(wins, total):
    winrate = wins / total
    print("The player won {0} of {1} games ({2:0.2%})".format(wins, total, winrate))

def rolldies():
    dice1 = random.randint(1, 6)
    dice2 = random.randint(1, 6)
    roll = dice1 + dice2
    return roll

def simOneGame():
    initial_roll = rolldies()
    if initial_roll == 2 or initial_roll == 3 or initial_roll == 12:
        return True     # won
    elif initial_roll == 7 or initial_roll == 11:
        return False    #lost
    else:
        #Roll until roll is 7 or initial roll
        roll = rolldies()
        while roll != 7 and roll != initial_roll:
            roll = rolldies()
        if roll == 7:
            return True     #won
        else:
            #roll is inital_roll
            return False    #lost

def simNGames(games_to_sim):
    wins = 0
    for i in range(games_to_sim):
        if simOneGame():
            wins += 1
    return wins

def main():
    games_to_sim = int(input("How many games to simulate? "))
    wins = simNGames(games_to_sim)
    output(wins, games_to_sim)

if __name__ == "__main__":
    main()

Always open for suggestions on what to improve/do differently or more pythonic.

\$\endgroup\$
2
\$\begingroup\$

First, Python has an official style-guide, PEP8, which programmers are encouraged to follow. This makes it easier for other Python programmers to read your code. PEP8 recommends using lower_case both for variable and function names, which I have used in the code below.

In your output function you use string formatting (good). However note that if you just want to paste the values in order, there is no need to explicitly mention the indices. So you could write "The player won {} of {} games ({:0.2%})".format(wins, total, winrate). But there is an even easier way in Python 3.6+: f-strings. I also think this is not enough to put it into a function, I would just leave it as a single line in main:

print(f"The player won {wins} of {total} games ({wins/total:0.2%})")

Your rolldies function could be made more general if it accepted a parameter telling it how many dice to roll. You can also use a list comprehension here, or random.choices:

def roll_dice(n=2):
    return sum(random.randint(1, 6) for _ in range(n))

DIE = [1, 2, 3, 4, 5, 6]
def roll_dice(n=2):
    return sum(random.choices(DIE, k=n))

Note that the singular is "die" and the plural is "dice", not "dies".

In your simOneGame function, instead of chaining a lot of comparisons with or, just use in:

def sim_game():
    initial_roll = roll_dice()
    if initial_roll in {2, 3, 12}:
        return True     # won
    elif initial_roll in {7, 11}:
        return False    # lost

Your rolling in the else branch can also be a bit shortened:

    else:
        #Roll until roll is 7 or initial roll
        roll = roll_dice()
        while roll not in {7, initial_roll}:
            roll = roll_dice()
        return roll == 7    # 7 wins

You could theoretically replace the while loop with a single random.choices call, but you would have to manually set the probabilities to the right values.

The simNGames function can be shortened a lot by using the fact that False == 0 and True == 1:

def sim_n_games(n):
    return sum(sim_game() for _ in range(n))
\$\endgroup\$
  • \$\begingroup\$ What is the difference between "in (2,3,12)" and "in {2,3,12}" ? \$\endgroup\$ – Meph- Feb 20 at 9:40
  • \$\begingroup\$ @Meph-: The first one is a tuple and the latter one a set. For small collections there will be basically no difference, but a set has O(1) membership testing, while a tuple is slightly faster to construct, but has O(n) membership testing. On my machine 1 in (2, 3, 12) takes 53.9 ns ± 0.361 ns and 1 in {2, 3, 12} takes 55.2 ns ± 2.07 ns. \$\endgroup\$ – Graipher Feb 20 at 9:42
  • \$\begingroup\$ @Meph-: And for completeness sake (and to show that it is better than chaining or), 1 == 2 or 1 == 3 or 1 == 12 takes 73.3 ns ± 0.658 ns. \$\endgroup\$ – Graipher Feb 20 at 9:47
  • 1
    \$\begingroup\$ I'll put the formatting changes to use! Thanks. As to using "in", i simply havent learned that yet(tuples, sets),it will probably come later in my programming book, but I'll implement it right away. Really seems to be the better choice. Sorry for the die/dice mixup haha. \$\endgroup\$ – Kevin Feb 20 at 9:59
  • \$\begingroup\$ Okay, it seems like print(f"The player won {wins} of {total} games ({wins/total:0.2%})") doesn't work. i think you can't calculate anything in there \$\endgroup\$ – Kevin Feb 20 at 10:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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