From 565914b1bdbb5bd6fb136577547a05f0e4bcbe4a Mon Sep 17 00:00:00 2001 From: Daniele Fiocca Date: Sun, 30 Nov 2025 12:54:18 +0100 Subject: [PATCH] first solution 001-lotto-game --- .../python/bill_generator/__init__.py | 0 .../python/bill_generator/bill.py | 28 ++++++ .../python/bill_generator/bill_generator.py | 83 +++++++++++++++++ .../python/bill_generator/controller.py | 93 +++++++++++++++++++ .../python/bill_generator/user_interface.py | 62 +++++++++++++ projects/001-lotto-game/python/main.py | 5 + 6 files changed, 271 insertions(+) create mode 100644 projects/001-lotto-game/python/bill_generator/__init__.py create mode 100644 projects/001-lotto-game/python/bill_generator/bill.py create mode 100644 projects/001-lotto-game/python/bill_generator/bill_generator.py create mode 100644 projects/001-lotto-game/python/bill_generator/controller.py create mode 100644 projects/001-lotto-game/python/bill_generator/user_interface.py diff --git a/projects/001-lotto-game/python/bill_generator/__init__.py b/projects/001-lotto-game/python/bill_generator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/projects/001-lotto-game/python/bill_generator/bill.py b/projects/001-lotto-game/python/bill_generator/bill.py new file mode 100644 index 0000000..e662e0d --- /dev/null +++ b/projects/001-lotto-game/python/bill_generator/bill.py @@ -0,0 +1,28 @@ +class Bill: + """Represents a single Lotto ticket. + + Attributes: + giocata (str): Type of bet (ambata, ambo, etc.). + ruota (str): Wheel on which the game is played. + numbers (List[int]): Numbers generated for the ticket. + """ + + def __init__(self, giocata: str, ruota: str, numbers: list[int]): + """Initializes a Lotto bet slip. + + Args: + giocata (str): Type of bet. + ruota (str): Selected wheel. + numbers (List[int]): List of generated numbers. + """ + self.giocata = giocata + self.ruota = ruota + self.numbers = numbers + + def __str__(self): + """Returns a textual representation of the ticket. + + Returns: + str: String containing the type of giocata, numbers, and ruota. + """ + return f"{self.giocata} {self.ruota} {self.numbers} " diff --git a/projects/001-lotto-game/python/bill_generator/bill_generator.py b/projects/001-lotto-game/python/bill_generator/bill_generator.py new file mode 100644 index 0000000..e43140a --- /dev/null +++ b/projects/001-lotto-game/python/bill_generator/bill_generator.py @@ -0,0 +1,83 @@ +import random +from typing import List + + +class BillGenerator: + """Class to generate numbers, wheels, and play selections.""" + + @staticmethod + def get_numbers(n_numbers: int) -> list[int]: + """Generates a list of unique random numbers between 1 and 90. + + Args: + n_numbers (int): Number of numbers to generate. + + Returns: + List[int]: List of generated numbers. + """ + numbers: list[int] = [] + while len(numbers) < n_numbers: + n = random.randint(1, 90) + if n not in numbers: + numbers.append(n) + return numbers + + @staticmethod + def get_ruota() -> str: + """Displays available wheels and allows the user to select one. + + Returns: + str: Wheel selected by the user. + """ + print("\nChoose a 'ruota' from the list below:\n") + ruote: List[str] = [ + "Bari", + "Cagliari", + "Firenze", + "Genova", + "Milano", + "Napoli", + "Palermo", + "Roma", + "Torino", + "Venezia", + "Tutte", + ] + + while True: + for i, route in enumerate(ruote, start=1): + print(f"{i}. {route}") + + if (choice := input("Select a route: ")).isdigit(): + choice = int(choice) + if 1 <= choice <= len(ruote): + return ruote[choice - 1] + + print("Invalid 'ruota', try again.\n") + + @staticmethod + def giocata() -> tuple[str, int] | None: + """Displays the available types of play and allows selection. + + Returns: + Tuple[str, int]: Name of the play and minimum number of numbers required. + """ + print("\nChoose a 'giocata' from the list below:\n") + giocate_list: dict[str, int] = { + "ambata": 2, + "ambo": 2, + "terno": 3, + "quaterna": 4, + "cinquina": 5, + } + keys: List[str] = list(giocate_list.keys()) + + for i, play in enumerate(keys, start=1): + print(f"{i}. {play}") + + while (choice := input("Select a 'giocata': ")) is not None: + if choice.isdigit() and 1 <= int(choice) <= len(keys): + selected = keys[int(choice) - 1] + return selected, giocate_list[selected] + print("Invalid choice, select a number from the list.\n") + return None diff --git a/projects/001-lotto-game/python/bill_generator/controller.py b/projects/001-lotto-game/python/bill_generator/controller.py new file mode 100644 index 0000000..bfd889e --- /dev/null +++ b/projects/001-lotto-game/python/bill_generator/controller.py @@ -0,0 +1,93 @@ +import sys +from typing import List + +from bill_generator.bill import Bill +from bill_generator.bill_generator import BillGenerator +from bill_generator.user_interface import UserInterface + + +class Controller: + """Controller class to manage ticket generation and display.""" + + def __init__(self): + """Initializes the controller and the ticket generator.""" + self.bill_generator: BillGenerator = BillGenerator() + self.bills: dict[int, Bill] = {} + + def how_many_bills(self) -> int: + """Asks the user how many tickets to generate. + + Returns: + int: Number of tickets to generate. + """ + while True: + try: + if ( + num_bills := int( + input( + "How many bills would you like to generate? (1–5, 0 to exit): " + ) + ) + ) == 0: + sys.exit("Thank you for using this program") + + if 1 <= num_bills <= 5: + return num_bills + + print("Invalid choice. Please select a number between 1 and 5.") + + except ValueError: + print("Invalid input, insert only numbers.") + + def how_many_numbers_for_bills(self, min_required: int) -> int: + """Asks the user how many numbers to generate for a ticket. + + Args: + min_required (int): Minimum number of numbers required for the selected play. + + Returns: + int: Number of numbers chosen by the user. + """ + while True: + try: + if ( + numbers := int( + input( + f"How many numbers would you like to generate? (min {min_required}): " + ) + ) + ) >= min_required: + return numbers + + print(f"You must insert at least {min_required} numbers.") + + except ValueError: + print("Invalid input, numbers only.") + + def create_bills(self) -> None: + """Generates all tickets requested by the user.""" + numbers_of_bills: int = self.how_many_bills() + + for number in range(numbers_of_bills): + print(f"Generating bill number {number + 1}...") + giocata_name, min_numbers = self.bill_generator.giocata() + + numbers_in_bills: int = self.how_many_numbers_for_bills(min_numbers) + ruota: str = self.bill_generator.get_ruota() + numbers: List[int] = self.bill_generator.get_numbers(numbers_in_bills) + bill: Bill = Bill(giocata_name, ruota, numbers) + self.bills[number] = bill + + def print_bills(self): + """Prints all generated tickets.""" + if not self.bills: + print("No bills were generated.") + for number, bill in enumerate(self.bills.values(), start=1): + UserInterface.print_bill(number, bill.giocata, bill.ruota, bill.numbers) + + def run(self): + """Runs the full program flow.""" + UserInterface.show_intro() + UserInterface.show_description() + self.create_bills() + self.print_bills() diff --git a/projects/001-lotto-game/python/bill_generator/user_interface.py b/projects/001-lotto-game/python/bill_generator/user_interface.py new file mode 100644 index 0000000..7b63e9e --- /dev/null +++ b/projects/001-lotto-game/python/bill_generator/user_interface.py @@ -0,0 +1,62 @@ +from typing import List + + +class UserInterface: + """Handles displaying tickets on screen.""" + + def show_intro(): + print("Welcome to Lotto Bill Generator") + + def show_description(): + print("With this software you can generate betting slips for the 'Lotto game'") + print( + "\nDISCLAIMER: There is no scientific proof of the correlation between the tickets generated and the probability of winning.\n" + ) + + @staticmethod + def print_bill( + bill_number: int, giocata: str, ruota: str, numbers: List[int] + ) -> None: + """Prints a ticket in a dynamic centered table. + + Args: + bill_number (int): Number of the generated ticket. + giocata (str): Type of play. + ruota (str): Selected wheel. + numbers (List[int]): Generated numbers for the ticket. + """ + numbers_str: str = " ".join(str(n) for n in numbers) + headers: List[str] = ["Bill", "Giocata", "Ruota", "Generate Numbers"] + row: List[str] = [f"Bill {bill_number}", giocata, ruota, numbers_str] + + col_widths: List[int] = [ + max(len(headers[i]), len(str(row[i]))) + 2 for i in range(len(headers)) + ] + + def make_line(left: str, fill: str, sep: str, right: str) -> str: + return left + sep.join(fill * w for w in col_widths) + right + + def center_text(text: str, width: int) -> str: + return text.center(width) + + def make_row(items: List[str]) -> str: + cells: List[str] = [ + center_text(items[i], col_widths[i]) for i in range(len(items)) + ] + return "║" + "║".join(cells) + "║" + + top: str = make_line("╔", "═", "╦", "╗") + sep: str = make_line("╠", "═", "╬", "╣") + bottom: str = make_line("╚", "═", "╩", "╝") + + bill: str = f""" + + + {top} + {make_row(headers)} + {sep} + {make_row(row)} + {bottom} + + """ + print(bill) diff --git a/projects/001-lotto-game/python/main.py b/projects/001-lotto-game/python/main.py index e69de29..064c2c2 100644 --- a/projects/001-lotto-game/python/main.py +++ b/projects/001-lotto-game/python/main.py @@ -0,0 +1,5 @@ +from bill_generator.controller import Controller + +if __name__ == "__main__": + controller = Controller() + controller.run()