Codificación para principiantes con Python: desarrolle un simulador de tirada de dados
Entonces, es la noche de los juegos de rol de mesa, todos los demás tienen su bolsa de dados y tú sacas... ¿tu computadora portátil? Bueno, es el momento perfecto para mostrar ese simulador de tirada de dados que creaste en Python. Este proyecto para principiantes debería enseñarte los conceptos básicos de las tiradas de dados y la aleatorización.
Configurando nuestro proyecto
Esta será una configuración bastante simple. Ya describí cómo configuramos Visual Studio para codificar con Python, así que lo usaremos para nuestra configuración básica. Mantendremos esto lo más simple posible, así que comenzaremos con nuestras importaciones:
import tkinter as tk
from tkinter import ttk
import random
Usamos Tkinter antes cuando creamos nuestro sencillo rastreador de gastos; en este caso, es la solución más ligera. La biblioteca nos permite crear una interfaz GUI simple, moderna pero elegante. La biblioteca aleatoria nos permite la funcionalidad de aleatorizar números entre dos valores, que es la esencia de una tirada de dado. Hagamos las cosas un poco más bonitas.
Consideraciones de diseño con nuestras caras de dados
Vamos a simular un juego de dados RPG. Para darle un toque estilístico, hice que los dados de cuatro caras (d4) y los dados de seis caras (d6) fueran un poco elegantes para demostrar el manejo del arte ASCII con Tkinter.
def __init__(self, root):
self.root = root
self.root.title("Dice Simulator")
self.root.geometry("400x500")
# Dice face configurations using ASCII art
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└─────────┘"
}
Opté por esto porque sentí que una GUI sería mucho más atractiva. Cuando hicimos nuestra aplicación de prueba, nos quedamos con la terminal, pero me gustaría darle un poco de estilo a esta aplicación. No te preocupes demasiado por este fragmento de código. Todo lo que hace es mostrar nuestro d6 con una cara como un d6 real, y lo mismo para un d4. Este es también el comienzo de nuestra GUI, con el título de la aplicación "Dice Simulator" que se muestra en la parte superior de la ventana. Veamos cómo vamos a hacer nuestra entrada.
Queremos un lugar para que el usuario ingrese la cantidad y el tipo de dados que desea tirar. Para simplificar las cosas, maximizaremos los dados a cinco, por lo que no manejaremos nada más que eso. Por lo tanto, nuestra interfaz de usuario tendrá un selector de dados (para el tipo de dado) y una cantidad de entradas de dados si tiramos más de uno.
También quiero implementar un sistema de historial para nuestras tiradas, así que lo pondré en la parte inferior y lo actualizaré cada vez que lancemos un nuevo conjunto de dados. Nuestro fragmento de código debería verse así:
# Create and configure the main frame
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))
# Number of dice selector
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)
# Dice type selector
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)
# Roll button
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)
# Results display
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)
# History display
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 = []
# Create and configure the main frame
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))
# Number of dice selector
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)
# Dice type selector
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)
# Roll button
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)
# Results display
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)
# History display
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 = []
Esto nos brinda una configuración de interfaz de usuario básica para nuestra aplicación de lanzamiento de dados. Pasemos a simular los rollitos.
Tirar dados a través de una computadora
Si alguna vez has jugado algún videojuego de rol, habrás experimentado tiradas de dados simuladas. Los dados dan forma a cómo se juegan los juegos de rol de mesa, sus flujos y reflujos y lo que les sucede a los personajes. Una tirada de dado es un número aleatorio producido entre dos valores, mínimo y máximo. En Python, podemos usar la biblioteca aleatoria para simular esas tiradas. Aquí está nuestro código:
def roll_single_die(self, sides):
"""Simulate rolling a single die with given number of sides."""
return random.randint(1, sides)
def get_dice_face(self, value, dice_type):
"""Get ASCII art representation of a die face."""
if dice_type == 4:
return self.d4_faces.get(value, str(value))
elif dice_type == 6:
return self.d6_faces.get(value, str(value))
return str(value)
def roll_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:]) # Extract number from 'd6', 'd20' etc.
# Clear previous results
self.result_text.delete(1.0, tk.END)
# Roll the dice and calculate total
rolls = [self.roll_single_die(dice_type) for _ in range(num_dice)]
total = sum(rolls)
# Display results
if dice_type in [4, 6]: # Show ASCII art for d4 and d6
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}")
# Update history
roll_record = f"{num_dice}d{dice_type}: {rolls} = {total}"
self.roll_history.append(roll_record)
if len(self.roll_history) > 5: # Keep only last 5 rolls
self.roll_history.pop(0)
# Update history display
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, "Please enter valid numbers")
Esto nos proporciona el código básico para simular nuestras tiradas de dados. Para que el programa se ejecute, solo necesitamos llamar a nuestra función principal:
if __name__ == "__main__":
root = tk.Tk()
app = DiceSimulator(root)
root.mainloop()
Y eso debería bastar. ¡Ahora tenemos un simulador de tirada de dados!
Sugerencias para mejoras y actualizaciones
Cuando ejecutas la aplicación, deberías ver algo como esto:
La aplicación tiene algunas limitaciones que omití intencionalmente para ofrecerle algo con lo que ampliar la aplicación. No hay soporte para d10 o d100 (que se pueden simular tirando 2d10 o 1d100). Puede agregarlos usted mismo si desea expandir la aplicación. Además, podrías extender esto a un rodillo de dados en red y hacer que la interfaz de usuario sea un poco más bonita con un poco de diseño. Como siempre, todo el código de esta aplicación está disponible en mi GitHub. Cualquier comentario sobre el código o mi enfoque siempre es bienvenido.