Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
Razor Shark
So it’s tabletop RPG night, and everyone else has their bag of dice, and you get out your laptop? Well, it’s a perfect time to show off that dice rolling simulator you built in Python. This beginner project should teach you the basics of dice rolling and randomization.
Setting up our project
This will be a fairly simple setup. I have already described it how we set up Visual Studio for coding with Pythonso we’ll use that for our basic configuration. We’ll keep this as simple as possible, so we’ll start with our import:
import tkinter as tk
from tkinter import ttk
import random
We used Tkinter before when we built our simple expense tracker; in this case this is the lightest solution. With the library we can simple, modern yet stylish GUI interface. The random library provides us with the functionality of randomizing numbers between two values, which is the essence of a dice roll. Let’s make things a little nicer.
Design considerations with our die faces
We are going to simulate a set of RPG dice. For stylistic flair, I made the four-sided dice (d4) and the six-sided dice (d6) a bit fancy to demonstrate Tkinter’s handling of ASCII art.
def__init__(self, root):
self.root = root
self.root.title("Dice Simulator")
self.root.geometry("400x500")
self.d4_faces = {
1: " \n 1 \n \n‾‾‾‾‾‾‾",
2: " \n 2 \n \n‾‾‾‾‾‾‾",
3: " \n 3 \n \n‾‾‾‾‾‾‾",
4: " \n 4 \n \n‾‾‾‾‾‾‾"
}
self.d6_faces = {
1: "\n \n \n \n",
2: "\n \n \n \n",
3: "\n \n \n \n",
4: "\n \n \n \n",
5: "\n \n \n \n",
6: "\n \n \n \n"
}
I chose this because I thought a GUI would be much more attractive. When we did our quiz appwe stayed at the terminal, but I would like a little flair in this app. Don’t worry too much about this piece of code. All it does is render our d6 with a face like a real d6, and the same for a d4. This is also the start of our GUI, with the app title “Dice Simulator” at the top of the window. Let’s see how we’re going to make our input.
We want a place where the user can enter the number and type of dice they want to roll. To keep it simple, we will set the dice to a maximum of five, so we won’t do more than that. Our UI will therefore have a dice selector (for the dice type) and a number of dice input if we roll more than one.
I also want to implement a history system for our rolls, so I’ll put that at the bottom and update it every time we roll a new set of dice. Our code snippet should look something like this:
self.main_frame = ttk.Frame(self.root, padding="10")
self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
ttk.Label(self.main_frame, text="Number of Dice:").grid(row=0, column=0, pady=5)
self.num_dice = ttk.Spinbox(self.main_frame, from_=1, to=5, width=5)
self.num_dice.set(1)
self.num_dice.grid(row=0, column=1, pady=5)
ttk.Label(self.main_frame, text="Dice Type:").grid(row=1, column=0, pady=5)
self.dice_type = ttk.Combobox(self.main_frame, values=("d4", "d6", "d8", "d12", "d20"), width=5)
self.dice_type.set("d6")
self.dice_type.grid(row=1, column=1, pady=5)
self.roll_button = ttk.Button(self.main_frame, text="Roll Dice!", command=self.roll_dice)
self.roll_button.grid(row=2, column=0, columnspan=2, pady=10)
self.result_text = tk.Text(self.main_frame, height=15, width=40, font=('Courier', 10))
self.result_text.grid(row=3, column=0, columnspan=2, pady=10)
ttk.Label(self.main_frame, text="Roll History:").grid(row=4, column=0, columnspan=2, pady=5)
self.history_text = tk.Text(self.main_frame, height=5, width=40)
self.history_text.grid(row=5, column=0, columnspan=2, pady=5)
self.roll_history = ()
self.main_frame = ttk.Frame(self.root, padding="10")
self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
ttk.Label(self.main_frame, text="Number of Dice:").grid(row=0, column=0, pady=5)
self.num_dice = ttk.Spinbox(self.main_frame, from_=1, to=5, width=5)
self.num_dice.set(1)
self.num_dice.grid(row=0, column=1, pady=5)
ttk.Label(self.main_frame, text="Dice Type:").grid(row=1, column=0, pady=5)
self.dice_type = ttk.Combobox(self.main_frame, values=("d4", "d6", "d8", "d12", "d20"), width=5)
self.dice_type.set("d6")
self.dice_type.grid(row=1, column=1, pady=5)
self.roll_button = ttk.Button(self.main_frame, text="Roll Dice!", command=self.roll_dice)
self.roll_button.grid(row=2, column=0, columnspan=2, pady=10)
self.result_text = tk.Text(self.main_frame, height=15, width=40, font=('Courier', 10))
self.result_text.grid(row=3, column=0, columnspan=2, pady=10)
ttk.Label(self.main_frame, text="Roll History:").grid(row=4, column=0, columnspan=2, pady=5)
self.history_text = tk.Text(self.main_frame, height=5, width=40)
self.history_text.grid(row=5, column=0, columnspan=2, pady=5)
self.roll_history = ()
This gives us a basic UI setup for our dice rolling app. Let’s move on to simulating the roles.
Throwing dice via a computer
If you’ve ever played an RPG video game, you’ve experienced simulated dice rolls. Dice determine how tabletop RPGs playtheir ebb and flow and what happens to characters. A die roll is a random number produced between two values, minimum and maximum. In Python we can use the random library to simulate those roles. Here is our code:
defroll_single_die(self, sides):
"""Simulate rolling a single die with given number of sides."""
return random.randint(1, sides)defget_dice_face(self, value, dice_type):
"""Get ASCII art representation of a die face."""
if dice_type == 4:
returnself.d4_faces.get(value, str(value))
elif dice_type == 6:
returnself.d6_faces.get(value, str(value))
return str(value)
defroll_dice(self):
"""Handle the dice rolling action and update the display."""
try:
num_dice = int(self.num_dice.get())
dice_type = int(self.dice_type.get()(1:))
self.result_text.delete(1.0, tk.END)
rolls = (self.roll_single_die(dice_type) for _ in range(num_dice))
total = sum(rolls)
if dice_type in (4, 6):
for roll in rolls:
self.result_text.insert(tk.END, self.get_dice_face(roll, dice_type) + "\n")
else:
roll_str = ", ".join(str(roll) for roll in rolls)
self.result_text.insert(tk.END, f"Rolls: {roll_str}\n")
self.result_text.insert(tk.END, f"\nTotal: {total}")
roll_record = f"{num_dice}d{dice_type}: {rolls} = {total}"
self.roll_history.append(roll_record)
if len(self.roll_history) > 5:
self.roll_history.pop(0)
self.history_text.delete(1.0, tk.END)
for record in self.roll_history:
self.history_text.insert(tk.END, record + "\n")
except ValueError:
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, "Pleaseentervalidnumbers")
This gives us the basic code for simulating our dice rolls. To make the program work, we just need to call our core function:
if __name__ == "__main__":
root = tk.Tk()
app = DiceSimulator(root)
root.mainloop()
And that should do it. We now have a dice rolling simulator!
Suggestions for upgrades and updates
When you run the app, you should see something like this:
The app has some limitations that I deliberately left out to give you something to expand the app with. There is no support for d10s or d100s (which can be simulated by rolling 2d10 or 1d100). You can add these yourself if you want to expand the app. Additionally, you could extend this to a networked dice roller and make the UI a bit nicer with some skinning. As usual all code for this app is available on my GitHub. Any comments about the code or my approach are always welcome.