Pesquisa de site

Codificação para iniciantes com Python: desenvolva um simulador de lançamento de dados


Então, é noite de RPG de mesa, todo mundo está com seu saco de dados e você traz – seu laptop? Bem, é o momento perfeito para mostrar aquele simulador de lançamento de dados que você construiu em Python. Este projeto para iniciantes deve ensinar o básico sobre lançamento de dados e randomização.

Configurando Nosso Projeto

Esta será uma configuração bastante simples. Já descrevi como configuramos o Visual Studio para codificação com Python, então usaremos isso em nossa configuração básica. Manteremos isso o mais simples possível, então começaremos com nossas importações:

import tkinter as tk
from tkinter import ttk
import random

Já usamos o Tkinter antes, quando construímos nosso rastreador de despesas simples; neste caso, é a solução mais leve. A biblioteca nos permite criar uma interface GUI simples, moderna e elegante. A biblioteca aleatória nos permite a funcionalidade de randomizar números entre dois valores, que é a essência de uma jogada de dado. Vamos deixar as coisas um pouco mais bonitas.

Considerações de design com nossas faces de dados

Vamos simular um conjunto de dados de RPG. Para um toque estilístico, tornei os dados de quatro lados (d4) e os dados de seis lados (d6) um pouco sofisticados para demonstrar o manuseio da arte ASCII com o 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└─────────┘"
        }

Optei por isso porque achei que uma GUI seria muito mais atraente. Quando fizemos nosso aplicativo de teste, ficamos no terminal, mas eu gostaria de um pouco de talento neste aplicativo. Não fique muito preocupado com esse trecho de código. Tudo o que ele faz é exibir nosso d6 com uma face semelhante a um d6 real, e o mesmo para um d4. Este também é o início da nossa GUI, com o título do aplicativo "Dice Simulator" mostrado na parte superior da janela. Vejamos como faremos nossa contribuição.

Queremos um local para o usuário inserir o número e o tipo de dado que deseja lançar. Para manter as coisas simples, estaremos maximizando os dados em cinco, então não vamos lidar com nada além disso. Nossa interface de usuário terá, portanto, um seletor de dados (para o tipo de dado) e uma série de entradas de dados se lançarmos mais de um.

Também quero implementar um sistema de histórico para nossas jogadas, então colocarei isso na parte inferior e atualizarei sempre que lançarmos um novo conjunto de dados. Nosso trecho de código deve ser parecido com isto:

        # 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 = []

Isso nos dá uma configuração básica de UI para nosso aplicativo de lançamento de dados. Vamos passar à simulação dos rolos.

Jogando dados em um computador

Se você já jogou algum videogame RPG, já experimentou jogadas de dados simuladas. Os dados moldam a forma como os RPGs de mesa são jogados, seus altos e baixos e o que acontece com os personagens. Um lançamento de dado é um número aleatório produzido entre dois valores, mínimo e máximo. Em Python, podemos usar a biblioteca aleatória para simular essas jogadas. Aqui está nosso 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")

Isso nos fornece o código básico para simular nossos lançamentos de dados. Para fazer o programa rodar, só precisamos chamar nossa função principal:

if __name__ == "__main__":
    root = tk.Tk()
    app = DiceSimulator(root)
    root.mainloop()

E isso deve bastar. Agora temos um simulador de lançamento de dados!

Sugestões para atualizações e atualizações

Ao executar o aplicativo, você deverá ver algo assim:




O aplicativo tem algumas limitações que deixei de fora intencionalmente para oferecer algo para expandir o aplicativo. Não há suporte para d10s ou d100s (que podem ser simulados rolando 2d10 ou 1d100). Você mesmo pode adicioná-los se quiser expandir o aplicativo. Além disso, você pode estender isso para um rolo de dados em rede e tornar a interface do usuário um pouco mais bonita com um pouco de skin. Como de costume, todo o código deste aplicativo está disponível no meu GitHub. Quaisquer comentários sobre o código ou minha abordagem são sempre bem-vindos.

Artigos relacionados: