diff --git a/main.py b/main.py index 073150a..6da3a95 100644 --- a/main.py +++ b/main.py @@ -5,81 +5,110 @@ import questionary from rich.console import Console from rich.text import Text from rich.progress import Progress, TextColumn, BarColumn -from pprint import pprint +# Validate if a string can be converted to a float +def can_be_float(s): + try: + float(s) + return True + except ValueError: + return False + +# Generate random data with a specified size and maximum value def generate_random_data(size=1024, max_value=100): - random_data = [] + # Calculate the number of bytes required to represent the max value max_value_bytes = (max_value.bit_length() + 7) // 8 max_int_for_bytes = 2**(max_value_bytes * 8) - 1 - min_bytes_needed = max_value_bytes * size + # Define the cutoff to ensure uniform distribution mod_cutoff = max_int_for_bytes - (max_int_for_bytes % max_value) - 1 - # Populate the 'random_data' array while len(random_data) < size: - hex_data = rndo.get(length=min_bytes_needed) - hex_chunks = list((hex_data[0+i:2 * max_value_bytes+i] for i in range(0, len(hex_data), 2 * max_value_bytes))) - for i in hex_chunks: - num = int(i, 16) - if num <= mod_cutoff and len(random_data) < size: - random_data.append( num % (max_value + 1) ) - + # Generate random hexadecimal data + hex_data = rndo.get(length=max_value_bytes * size) + # Process the hex data in chunks + for i in (hex_data[j:j + 2 * max_value_bytes] for j in range(0, len(hex_data), 2 * max_value_bytes)): + num = int(i, 16) # Convert hex chunk to integer + if num <= mod_cutoff: + # Add the value to random_data if within the cutoff + random_data.append(num % (max_value + 1)) + # Break early if enough data is collected + if len(random_data) >= size: + break return random_data -def pull_number(numbers: set, top: int, amount: int): - dataset = generate_random_data(top, top-1) +# Analyze and select numbers based on Z-score +def pull_number(numbers: set, top: int, amount: int, z_score: float): + + # Generate and sort a dataset of random numbers + dataset = generate_random_data(3000, 999) dataset.sort() - pprint(dataset) - # calculate the anomalies in the data + + # Analyze the dataset using Lyagushka zhaba = Lyagushka(dataset) - analysis_results = json.loads(zhaba.search(1.0, 3)) - pprint(analysis_results) - pick = [obj for obj in analysis_results if obj['num_elements'] == max(obj['num_elements'] for obj in analysis_results)] - for p in pick: - if len(numbers) < amount: - numbers.add(int(p['centroid'] + 1)) + analysis_results = json.loads(zhaba.search(4.0, 30)) + + # Filter results for valid Z-scores + results = [obj for obj in analysis_results if obj['z_score'] is not None] + max_z_score = max(obj['z_score'] for obj in results) + + for obj in results: + # Check if the object has the maximum Z-score and meets the threshold + if obj['z_score'] == max_z_score and obj['z_score'] >= z_score: + # Ensure the centroid is not an x.5 and add the scaled value + if len(numbers) < amount and (obj['centroid'] % 1 != 0.5): + numbers.add(int((obj['centroid'] / 999) * top) + 1) return numbers +# Main function to drive the lottery draw process def main(): - console = Console() + # Display an introduction to the lottery process + console.print("\nEach lottery ball is selected by analyzing random values.\n", style="italic green") - description = "\nEach lottery ball is selected by generating hundreds of random values within the provided range. The data is analyzed to identify the most frequently occurring number, which is then chosen as the lottery ball. This process repeats until all balls are drawn. The entire method is a one-dimensional analogy to how attractor points are calculated in Randonautica.\n" - console.print(description, style="italic green") - - num_lotto_balls = questionary.text( + # Get the number of lottery balls to draw + num_lotto_balls = int(questionary.text( "Amount of balls to draw? (default: 5)", validate=lambda val: val.isdigit() or "Please enter a valid integer.", default="5" - ).ask() - num_lotto_balls = int(num_lotto_balls) + ).ask()) - highest_number = questionary.text( + # Get the highest possible number in the lottery + highest_number = int(questionary.text( "Amount of balls in the lottery? (default: 70)", validate=lambda val: val.isdigit() or "Please enter a valid integer.", default="70" - ).ask() - highest_number = int(highest_number) + ).ask()) - highest_extra_ball = questionary.text( + # Get the highest number for the extra draw + highest_extra_ball = int(questionary.text( "Amount of balls in the extra draw? (default: 25)", validate=lambda val: val.isdigit() or "Please enter a valid integer.", default="25" - ).ask() - highest_extra_ball = int(highest_extra_ball) + ).ask()) + + # Get the Z-score threshold for anomaly detection + z_score_threshold = float(questionary.text( + "Minimum Z-Score Threshold? (default: 4.0)", + validate=lambda val: can_be_float(val) or "Please enter a valid value (1.0 - 5.0).", + default="4.0" + ).ask()) numbers = set() + megaball = set() - # Animation before displaying the results + # Show a progress bar during the number drawing process with Progress(TextColumn("[progress.description]{task.description}"), BarColumn(), transient=True) as progress: - task = progress.add_task("Drawing numbers...", total=num_lotto_balls+1) + task = progress.add_task("Drawing numbers...", total=num_lotto_balls + 1) + # Draw main lottery numbers while len(numbers) < num_lotto_balls: - numbers = pull_number(numbers, highest_number, num_lotto_balls) - progress.update(task, completed=(len(numbers)/(num_lotto_balls+1)*num_lotto_balls+1)) - megaball = pull_number(set(), highest_extra_ball, 1) - progress.stop() + numbers = pull_number(numbers, highest_number, num_lotto_balls, z_score_threshold) + progress.update(task, completed=len(numbers)) + # Draw the extra ball + while len(megaball) < 1: + megaball = pull_number(megaball, highest_extra_ball, 1, z_score_threshold) - # Print the results as styled lottery balls + # Display the results console.print("\nLottery numbers:\n", style="bold green") lottery_balls = [Text(f" {n} ", style="bold black on white") for n in sorted(numbers)] bonus_ball = Text(f" {list(megaball)[0]} ", style="bold black on yellow")