# A Tiny Challenge
Let's do a tiny coding challenge that requires not only the bits and pieces we have learnt but also some research on new function/module.
# Assignment 14
Create a Python script named dice_rolling.py
that prompts the user for the number of times to roll the two dices. Calculate the sum of the two dice values and count the percentage of occurrence for each sum value.
The script then prints the result in a tabular format and saves it in a text file.
Compare your actual results with the expectation.
A sample run looks like the following.
python dice_rolling.py
Enter Number of Times to Roll the Dices: 200
Sum Actual % Expected %
2 3.50 2.78
3 7.50 5.56
4 6.00 8.33
5 11.00 11.11
6 14.00 13.89
7 16.00 16.67
8 14.00 13.89
9 8.50 11.11
10 12.00 8.33
11 4.00 5.56
12 3.50 2.78
2
3
4
5
6
7
8
9
10
11
12
13
14
Finally, create a public repository named dice_rolling
in your GitHub account and push the dice_rolling.py
script in it.
Here's a list of resources that may be helpful to you.
- Cartesian product (opens new window)
- Random Integer with in range (opens new window)
- General Purpose Counter (opens new window)
- A Guide to f-string Formatting in Python (opens new window)
- Concatenate a list of Strings (opens new window)
- Open a file for input/output (opens new window)
What happens if roll the dice many more times, say 1 million times?
Sample Solution
from collections import Counter
from itertools import product
from random import randint
def get_two_dices():
num1 = randint(1, 6)
num2 = randint(1, 6)
return (num1, num2)
def summarize(rolls):
result = Counter()
for num1, num2 in rolls:
result[num1 + num2] += 1
return result
number_of_rolls = int(input("Enter Number of Times to Roll the Dices: "))
dice_numbers = range(1, 7)
actual = summarize([get_two_dices() for _ in range(number_of_rolls)])
expected = summarize(product(dice_numbers, dice_numbers))
outputs = []
outputs.append(f"{'Sum':>10}{'Actual %':>20}{'Expected %':>20}")
for num in range(2, 13):
actual_percent = 100 * actual[num] / number_of_rolls
expected_percent = 100 * expected[num] / 36
outputs.append(f"{num:10.0f}{actual_percent:20.2f}{expected_percent:20.2f}")
print("\n".join(outputs))
with open("result.txt", "w") as f:
f.write("\n".join(outputs))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# A generic counter
If you are given a list of numbers and told to count the number of appearances for each unique value, what would you do?
Sometimes you may know all the possible values beforehand. For example, you are told that the numbers are integers from 1 to 4 inclusively. It's very tempting to create some counter variables to represent the number of appearances.
Say, the numbers are stored in a list some_numbers
, you may start with creating the following variables
number_of_ones = 0
number_of_twos = 0
number_of_threes = 0
number_of_fours = 0
2
3
4
then, iterate some_numbers
to determine the count for each value
for number in some_numbers:
if number == 1:
number_of_ones += 1
if number == 2:
number_of_twos += 1
if number == 3:
number_of_threes += 1
if number == 4:
number_of_fours += 1
2
3
4
5
6
7
8
9
The logic sounds Okay but you MUST know all the values and hopefully there are NOT a lot of values. What if there are one thousand or one million possible values in the list? The code would become a nightmare to maintain.
Now let's step back and distill the core logic behind the counting exercise. It's simply doing one thing, which is to increment the counter value by one if that number appears.
One edge case you would have to handle is when that number appears for the first time. In this case, you can set the counter value to 1.
And since the outcome keeps track of a count for each number, we can use dictionary
as the data type to store the information.
Remind ourselves that a dictionary
is a collection of key/value pairs.
In the counting use case, we can use a key
to represent the unique number and the associated value
to represent the count.
To put the idea in code, you may have
result = {}
for number in some_numbers:
if number not in result:
result[number] = 1
else:
result[number] += 1
2
3
4
5
6
or, you can consider initializing the value to 0 then increasing by 1
result = {}
for number in some_numbers:
if number not in result:
result[number] = 0
result[number] += 1
2
3
4
5
In this way, the code is a lot shorter and works in general, with or without the knowledge about what values are possible.
And counting items is a very common action, Python provides a class in the standard library for such purpose.
from collections import Counter
result = Counter(some_numbers)
2
3
# Formatting with f-string
We can use f-string to naturally construct message.
name = "Jack"
age = 20
print (f'My name is {name} and I am {age} years old.')
2
3
4
It also works with functions.
def greeting(name):
return f"Hello, I'm {name}"
print(f"{greeting('Jill')}")
print(f"{name.lower()}")
2
3
4
5
Or simply values.
print(f"{2**10}")
print(f"{'Hi there'}")
2
We can apply specifiers to better format a string.
- Length of output and alignment
print(f"{'a':10} **")
print(f"{'a':>10} **")
print(f"{'a':^10} **")
print(f"{100:10} **")
print(f"{100:<10} **")
print(f"{100:^10} **")
- Decimal points
import math
print(f"{math.pi}")
print(f"{math.pi:.3f}")
print(f"{math.pi:.6f}")
2
3
4
5
- Using exponent notation
print(f"{math.pi:e}")
print(f"{2**100:.3e}")
2
- Percentage
print(f"{3/8:%}")
print(f"{1/8:.3%}")
2
# Working with files
We have done a lot print()
statement to write values in the console. Let's see how we can save the results in a textual file.
To do so, we need a file object by using the open()
function.
"result.txt"
is the name of file we are going to write some content into while "w"
specifies the mode to be write
for us to perform writing.
The mode is by default "r"
which only supports read.
f = open("result.txt", "w")
f.write("Hello World!")
f.close()
2
3
TIP
It's always a good practice to close any file object you open.
We can also use a context manager to automatically close the file object.
with open("result.txt", "w") as f:
f.write("Hello World!")
2
Notice that we don't need to manually call close()
function anymore since the context manager would handle for us.
We can also gather information from a textual file, instead of depending on user input from terminal.
with open("result.txt") as f:
content = f.read()
2
The read()
function would everything into a string. If we need to breakdown the reading per line, we can use readlines()
.
with open("result.txt") as f:
content = f.readlines()
2
Also note that the file object f
can be iterated.
with open("result.txt") as f:
for line in f:
print(line)
2
3