import gradio as gr from huggingface_hub import whoami import os import math import pandas as pd import json from itertools import combinations import random from roulette_data import ( EVEN_MONEY, DOZENS, COLUMNS, STREETS, CORNERS, SIX_LINES, SPLITS, NEIGHBORS_EUROPEAN, LEFT_OF_ZERO_EUROPEAN, RIGHT_OF_ZERO_EUROPEAN ) # New: Initialize betting category mappings for faster lookups BETTING_MAPPINGS = {} def initialize_betting_mappings(): """Initialize a mapping of numbers to their betting categories for efficient lookups.""" global BETTING_MAPPINGS BETTING_MAPPINGS = {i: {"even_money": [], "dozens": [], "columns": [], "streets": [], "corners": [], "six_lines": [], "splits": []} for i in range(37)} # Convert lists to sets and map numbers to categories for name, numbers in EVEN_MONEY.items(): numbers_set = set(numbers) for num in numbers_set: BETTING_MAPPINGS[num]["even_money"].append(name) for name, numbers in DOZENS.items(): numbers_set = set(numbers) for num in numbers_set: BETTING_MAPPINGS[num]["dozens"].append(name) for name, numbers in COLUMNS.items(): numbers_set = set(numbers) for num in numbers_set: BETTING_MAPPINGS[num]["columns"].append(name) for name, numbers in STREETS.items(): numbers_set = set(numbers) for num in numbers_set: BETTING_MAPPINGS[num]["streets"].append(name) for name, numbers in CORNERS.items(): numbers_set = set(numbers) for num in numbers_set: BETTING_MAPPINGS[num]["corners"].append(name) for name, numbers in SIX_LINES.items(): numbers_set = set(numbers) for num in numbers_set: BETTING_MAPPINGS[num]["six_lines"].append(name) for name, numbers in SPLITS.items(): numbers_set = set(numbers) for num in numbers_set: BETTING_MAPPINGS[num]["splits"].append(name) # Line 1: Start of updated update_scores_batch function def update_scores_batch(spins): """Update scores for a batch of spins and return actions for undo.""" # UNCHANGED: Initialize action log for undo action_log = [] # CHANGED: Directly update state dictionaries and build minimal action_log for spin in spins: spin_value = int(spin) action = {"spin": spin_value, "increments": {}} # Get all betting categories for this number from precomputed mappings categories = BETTING_MAPPINGS[spin_value] # Update even money scores for name in categories["even_money"]: state.even_money_scores[name] += 1 action["increments"].setdefault("even_money_scores", {})[name] = 1 # Update dozens scores for name in categories["dozens"]: state.dozen_scores[name] += 1 action["increments"].setdefault("dozen_scores", {})[name] = 1 # Update columns scores for name in categories["columns"]: state.column_scores[name] += 1 action["increments"].setdefault("column_scores", {})[name] = 1 # Update streets scores for name in categories["streets"]: state.street_scores[name] += 1 action["increments"].setdefault("street_scores", {})[name] = 1 # Update corners scores for name in categories["corners"]: state.corner_scores[name] += 1 action["increments"].setdefault("corner_scores", {})[name] = 1 # Update six lines scores for name in categories["six_lines"]: state.six_line_scores[name] += 1 action["increments"].setdefault("six_line_scores", {})[name] = 1 # Update splits scores for name in categories["splits"]: state.split_scores[name] += 1 action["increments"].setdefault("split_scores", {})[name] = 1 # Update straight-up scores state.scores[spin_value] += 1 action["increments"].setdefault("scores", {})[spin_value] = 1 # Update side scores if spin_value in current_left_of_zero: state.side_scores["Left Side of Zero"] += 1 action["increments"].setdefault("side_scores", {})["Left Side of Zero"] = 1 if spin_value in current_right_of_zero: state.side_scores["Right Side of Zero"] += 1 action["increments"].setdefault("side_scores", {})["Right Side of Zero"] = 1 action_log.append(action) # UNCHANGED: Return the action log for undo functionality return action_log def validate_roulette_data(): """Validate that all required constants from roulette_data.py are present and correctly formatted.""" required_dicts = { "EVEN_MONEY": EVEN_MONEY, "DOZENS": DOZENS, "COLUMNS": COLUMNS, "STREETS": STREETS, "CORNERS": CORNERS, "SIX_LINES": SIX_LINES, "SPLITS": SPLITS } required_neighbors = { "NEIGHBORS_EUROPEAN": NEIGHBORS_EUROPEAN, "LEFT_OF_ZERO_EUROPEAN": LEFT_OF_ZERO_EUROPEAN, "RIGHT_OF_ZERO_EUROPEAN": RIGHT_OF_ZERO_EUROPEAN } errors = [] for name, data in required_dicts.items(): if not isinstance(data, dict): errors.append(f"{name} must be a dictionary.") continue for key, value in data.items(): if not isinstance(key, str) or not isinstance(value, (list, set, tuple)) or not all(isinstance(n, int) for n in value): errors.append(f"{name}['{key}'] must map to a list/set/tuple of integers.") for name, data in required_neighbors.items(): if name == "NEIGHBORS_EUROPEAN": if not isinstance(data, dict): errors.append(f"{name} must be a dictionary.") continue for key, value in data.items(): if not isinstance(key, int) or not isinstance(value, tuple) or len(value) != 2 or not all(isinstance(n, (int, type(None))) for n in value): errors.append(f"{name}['{key}'] must map to a tuple of two integers or None.") else: if not isinstance(data, (list, set, tuple)) or not all(isinstance(n, int) for n in data): errors.append(f"{name} must be a list/set/tuple of integers.") return errors if errors else None # In Part 1, replace the RouletteState class with the following: class RouletteState: def __init__(self): self.scores = {n: 0 for n in range(37)} self.even_money_scores = {name: 0 for name in EVEN_MONEY.keys()} self.dozen_scores = {name: 0 for name in DOZENS.keys()} self.column_scores = {name: 0 for name in COLUMNS.keys()} self.street_scores = {name: 0 for name in STREETS.keys()} self.corner_scores = {name: 0 for name in CORNERS.keys()} self.six_line_scores = {name: 0 for name in SIX_LINES.keys()} self.split_scores = {name: 0 for name in SPLITS.keys()} self.side_scores = {"Left Side of Zero": 0, "Right Side of Zero": 0} self.selected_numbers = set() self.last_spins = [] self.spin_history = [] self.casino_data = { "spins_count": 100, "hot_numbers": [], "cold_numbers": [], "even_odd": {"Even": 0.0, "Odd": 0.0}, "red_black": {"Red": 0.0, "Black": 0.0}, "low_high": {"Low": 0.0, "High": 0.0}, "dozens": {"1st Dozen": 0.0, "2nd Dozen": 0.0, "3rd Dozen": 0.0}, "columns": {"1st Column": 0.0, "2nd Column": 0.0, "3rd Column": 0.0} } self.hot_suggestions = "" self.cold_suggestions = "" self.use_casino_winners = False self.bankroll = 1000 self.initial_bankroll = 1000 self.base_unit = 10 self.stop_loss = -500 self.stop_win = 200 self.target_profit = 10 self.bet_type = "Even Money" self.progression = "Martingale" self.current_bet = self.base_unit self.next_bet = self.base_unit self.progression_state = None self.consecutive_wins = 0 self.message = f"Start with base bet of {self.base_unit} on {self.bet_type} ({self.progression})" self.status = "Active" self.status_color = "white" self.last_dozen_alert_index = -1 self.alerted_patterns = set() self.last_alerted_spins = None self.labouchere_sequence = "" self.victory_vortex_sequence = [1, 8, 11, 16, 24, 35, 52, 78, 116, 174, 260, 390, 584, 876, 1313, 1969] # --- NEW VARIABLES FOR DYNAMIC 17 ASSAULT --- self.d17_list = [] self.d17_locked = False def reset(self): use_casino_winners = self.use_casino_winners casino_data = self.casino_data.copy() self.scores = {n: 0 for n in range(37)} self.even_money_scores = {name: 0 for name in EVEN_MONEY.keys()} self.dozen_scores = {name: 0 for name in DOZENS.keys()} self.column_scores = {name: 0 for name in COLUMNS.keys()} self.street_scores = {name: 0 for name in STREETS.keys()} self.corner_scores = {name: 0 for name in CORNERS.keys()} self.six_line_scores = {name: 0 for name in SIX_LINES.keys()} self.split_scores = {name: 0 for name in SPLITS.keys()} self.side_scores = {"Left Side of Zero": 0, "Right Side of Zero": 0} self.selected_numbers = set(int(s) for s in self.last_spins if s.isdigit()) self.last_spins = [] self.spin_history = [] self.use_casino_winners = use_casino_winners self.casino_data = casino_data # Reset 17 Assault self.d17_list = [] self.d17_locked = False self.reset_progression() def calculate_aggregated_scores_for_spins(self, numbers): """Calculate Aggregated Scores for a list of numbers (simulated spins).""" even_money_scores = {name: 0 for name in EVEN_MONEY.keys()} dozen_scores = {name: 0 for name in DOZENS.keys()} column_scores = {name: 0 for name in COLUMNS.keys()} for number in numbers: if number == 0: continue for name, numbers_set in EVEN_MONEY.items(): if number in numbers_set: even_money_scores[name] += 1 for name, numbers_set in DOZENS.items(): if number in numbers_set: dozen_scores[name] += 1 for name, numbers_set in COLUMNS.items(): if number in numbers_set: column_scores[name] += 1 return even_money_scores, dozen_scores, column_scores def reset_progression(self): self.current_bet = self.base_unit self.next_bet = self.base_unit self.progression_state = None self.consecutive_wins = 0 self.is_stopped = False self.message = f"Progression reset. Start with base bet of {self.base_unit} on {self.bet_type} ({self.progression})" self.check_status() return ( self.bankroll, self.current_bet, self.next_bet, self.message, f'
{self.status}
' ) def check_status(self): profit = self.bankroll - self.initial_bankroll if profit <= self.stop_loss: self.status = "Stopped: Stop Loss Reached" self.status_color = "red" elif profit >= self.stop_win: self.status = "Stopped: Stop Win Reached" self.status_color = "green" else: self.status = "Active" self.status_color = "white" def reset_bankroll(self): self.bankroll = self.initial_bankroll self.is_stopped = False self.message = f"Bankroll reset to {self.initial_bankroll}." self.check_status() return ( self.bankroll, self.current_bet, self.next_bet, self.message, f'
{self.status}
' ) def update_bankroll(self, won): payout = {"Even Money": 1, "Dozens": 2, "Columns": 2, "Streets": 11, "Straight Bets": 35}.get(self.bet_type, 1) if won: self.bankroll += self.current_bet * payout else: self.bankroll -= self.current_bet profit = self.bankroll - self.initial_bankroll if profit <= self.stop_loss: self.is_stopped = True self.status = f"Stopped: Hit Stop Loss of {self.stop_loss}" self.status_color = "red" elif profit >= self.stop_win: self.is_stopped = True self.status = f"Stopped: Hit Stop Win of {self.stop_win}" self.status_color = "green" else: self.status_color = "white" def update_progression(self, won): if self.is_stopped: return ( self.bankroll, self.current_bet, self.next_bet, self.message, f'
{self.status}
' ) self.update_bankroll(won) if self.bankroll < self.current_bet: self.is_stopped = True self.status = "Stopped: Insufficient bankroll" self.status_color = "red" self.message = "Cannot continue: Bankroll too low." return ( self.bankroll, self.current_bet, self.next_bet, self.message, f'
{self.status}
' ) if self.progression == "Martingale": self.current_bet = self.next_bet self.next_bet = self.base_unit if won else self.current_bet * 2 self.message = f"{'Win' if won else 'Loss'}! Next bet: {self.next_bet}" elif self.progression == "Fibonacci": fib = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] if self.progression_state is None: self.progression_state = 0 self.current_bet = self.next_bet if won: self.progression_state = max(0, self.progression_state - 2) self.next_bet = fib[self.progression_state] * self.base_unit self.message = f"Win! Move back to {self.next_bet}" else: self.progression_state = min(len(fib) - 1, self.progression_state + 1) self.next_bet = fib[self.progression_state] * self.base_unit self.message = f"Loss! Next Fibonacci bet: {self.next_bet}" elif self.progression == "Victory Vortex V.2": if self.progression_state is None: self.progression_state = 0 self.current_bet = self.next_bet if won: self.progression_state = 0 self.next_bet = self.victory_vortex_sequence[0] * self.base_unit self.message = f"Win! Reset to {self.next_bet} (Victory Vortex V.2, Step 1)" else: self.progression_state = min(len(self.victory_vortex_sequence) - 1, self.progression_state + 1) self.next_bet = self.victory_vortex_sequence[self.progression_state] * self.base_unit self.message = f"Loss! Next bet: {self.next_bet} (Victory Vortex V.2, Step {self.progression_state + 1})" elif self.progression == "Triple Martingale": self.current_bet = self.next_bet self.next_bet = self.base_unit if won else self.current_bet * 3 self.message = f"{'Win' if won else 'Loss'}! Next bet: {self.next_bet}" elif self.progression == "Ladder": self.current_bet = self.next_bet if won: self.next_bet = self.base_unit self.message = f"Win! Reset to {self.next_bet}" else: self.next_bet = self.current_bet + self.base_unit self.message = f"Loss! Increase to {self.next_bet}" elif self.progression == "D’Alembert": self.current_bet = self.next_bet if won: self.next_bet = max(self.base_unit, self.current_bet - self.base_unit) self.message = f"Win! Decrease to {self.next_bet}" else: self.next_bet = self.current_bet + self.base_unit self.message = f"Loss! Increase to {self.next_bet}" elif self.progression == "Double After a Win": self.current_bet = self.next_bet if won: self.next_bet = self.current_bet * 2 self.message = f"Win! Double to {self.next_bet}" else: self.next_bet = self.base_unit self.message = f"Loss! Reset to {self.next_bet}" elif self.progression == "+1 Win / -1 Loss": self.current_bet = self.next_bet if won: self.next_bet = self.current_bet + self.base_unit self.message = f"Win! Increase to {self.next_bet}" else: self.next_bet = max(self.base_unit, self.current_bet - self.base_unit) self.message = f"Loss! Decrease to {self.next_bet}" elif self.progression == "+2 Win / -1 Loss": self.current_bet = self.next_bet if won: self.next_bet = self.current_bet + (self.base_unit * 2) self.message = f"Win! Increase by 2 units to {self.next_bet}" else: self.next_bet = max(self.base_unit, self.current_bet - self.base_unit) self.message = f"Loss! Decrease to {self.next_bet}" elif self.progression == "Double Loss / +50% Win": self.current_bet = self.next_bet if won: self.consecutive_wins += 1 if self.consecutive_wins >= 2: self.next_bet = self.base_unit self.message = f"Win! Resetting to base bet of {self.next_bet} after {self.consecutive_wins} wins." self.consecutive_wins = 0 else: self.next_bet = round(self.current_bet * 1.5, 2) self.message = f"Win! Increasing bet by 50% to {self.next_bet}." else: self.consecutive_wins = 0 self.next_bet = round(self.current_bet * 2, 2) self.message = f"Loss! Doubling bet to {self.next_bet}." # Check stop conditions profit = self.bankroll - self.initial_bankroll if profit <= self.stop_loss: self.is_stopped = True self.status = "Stopped: Stop Loss Reached" self.status_color = "red" self.message = f"Stop Loss reached at {profit}. Current bankroll: {self.bankroll}" elif profit >= self.stop_win: self.is_stopped = True self.status = "Stopped: Stop Win Reached" self.status_color = "green" self.message = f"Stop Win reached at {profit}. Current bankroll: {self.bankroll}" return ( self.bankroll, self.current_bet, self.next_bet, self.message, f'
{self.status}
' ) # Lines before (context, unchanged) state = RouletteState() state.last_spins = [] state.scores = {i: 0 for i in range(37)} state.casino_data = {"hot_numbers": [], "cold_numbers": []} # Validate roulette data at startup data_errors = validate_roulette_data() if data_errors: raise RuntimeError("Roulette data validation failed:\n" + "\n".join(data_errors)) # New: Initialize betting mappings initialize_betting_mappings() # Lines after (context, unchanged) current_table_type = "European" current_neighbors = NEIGHBORS_EUROPEAN current_left_of_zero = LEFT_OF_ZERO_EUROPEAN current_right_of_zero = RIGHT_OF_ZERO_EUROPEAN # Global scores dictionaries scores = {n: 0 for n in range(37)} even_money_scores = {name: 0 for name in EVEN_MONEY.keys()} dozen_scores = {name: 0 for name in DOZENS.keys()} column_scores = {name: 0 for name in COLUMNS.keys()} street_scores = {name: 0 for name in STREETS.keys()} corner_scores = {name: 0 for name in CORNERS.keys()} six_line_scores = {name: 0 for name in SIX_LINES.keys()} split_scores = {name: 0 for name in SPLITS.keys()} side_scores = {"Left Side of Zero": 0, "Right Side of Zero": 0} selected_numbers = set() last_spins = [] colors = { "0": "green", "1": "red", "3": "red", "5": "red", "7": "red", "9": "red", "12": "red", "14": "red", "16": "red", "18": "red", "19": "red", "21": "red", "23": "red", "25": "red", "27": "red", "30": "red", "32": "red", "34": "red", "36": "red", "2": "black", "4": "black", "6": "black", "8": "black", "10": "black", "11": "black", "13": "black", "15": "black", "17": "black", "20": "black", "22": "black", "24": "black", "26": "black", "28": "black", "29": "black", "31": "black", "33": "black", "35": "black" } # Lines before (context) def format_spins_as_html(spins, num_to_show, show_trends=True): """Format the spins as HTML with color-coded display, animations, and pattern badges.""" if not spins: return "

Last Spins

No spins yet.

" # Split the spins string into a list and reverse to get the most recent first spin_list = spins.split(", ") if spins else [] spin_list = spin_list[-int(num_to_show):] if spin_list else [] # Take the last N spins if not spin_list: return "

Last Spins

No spins yet.

" # Define colors for each number (matching the European Roulette Table) colors = { "0": "green", "1": "red", "3": "red", "5": "red", "7": "red", "9": "red", "12": "red", "14": "red", "16": "red", "18": "red", "19": "red", "21": "red", "23": "red", "25": "red", "27": "red", "30": "red", "32": "red", "34": "red", "36": "red", "2": "black", "4": "black", "6": "black", "8": "black", "10": "black", "11": "black", "13": "black", "15": "black", "17": "black", "20": "black", "22": "black", "24": "black", "26": "black", "28": "black", "29": "black", "31": "black", "33": "black", "35": "black" } # Pattern detection for consecutive colors, dozens, columns, even/odd, and high/low (only if show_trends is True) patterns_by_index = {} # Dictionary to store all patterns starting at each index if show_trends: for i in range(len(spin_list) - 2): if i >= len(spin_list): break # Check for consecutive colors if colors.get(spin_list[i], "") == colors.get(spin_list[i+1], "") == colors.get(spin_list[i+2], ""): color_name = colors.get(spin_list[i], '').capitalize() if color_name: # Ensure color_name is not empty if i not in patterns_by_index: patterns_by_index[i] = [] patterns_by_index[i].append(f"3 {color_name}s in a Row") # Check for consecutive dozens dozen_hits = [next((name for name, nums in DOZENS.items() if int(spin) in nums), None) for spin in spin_list[i:i+3]] if None not in dozen_hits and len(set(dozen_hits)) == 1: if i not in patterns_by_index: patterns_by_index[i] = [] patterns_by_index[i].append(f"{dozen_hits[0]} Streak") # Check for consecutive columns column_hits = [next((name for name, nums in COLUMNS.items() if int(spin) in nums), None) for spin in spin_list[i:i+3]] if None not in column_hits and len(set(column_hits)) == 1: if i not in patterns_by_index: patterns_by_index[i] = [] patterns_by_index[i].append(f"{column_hits[0]} Streak") # Check for consecutive even/odd even_odd_hits = [next((name for name, nums in EVEN_MONEY.items() if name in ["Even", "Odd"] and int(spin) in nums), None) for spin in spin_list[i:i+3]] if None not in even_odd_hits and len(set(even_odd_hits)) == 1: if i not in patterns_by_index: patterns_by_index[i] = [] patterns_by_index[i].append(f"3 {even_odd_hits[0]}s in a Row") # Check for consecutive high/low high_low_hits = [next((name for name, nums in EVEN_MONEY.items() if name in ["High", "Low"] and int(spin) in nums), None) for spin in spin_list[i:i+3]] if None not in high_low_hits and len(set(high_low_hits)) == 1: if i not in patterns_by_index: patterns_by_index[i] = [] patterns_by_index[i].append(f"3 {high_low_hits[0]}s in a Row") # Format each spin as a colored span html_spins = [] for i, spin in enumerate(spin_list): color = colors.get(spin.strip(), "black") # Default to black if not found # Apply flip, flash, and new-spin classes to the newest spin (last in the list) if i == len(spin_list) - 1: class_attr = f'fade-in flip flash new-spin spin-{color} {color}' else: class_attr = f'fade-in {color}' # Add all pattern badges for this spin if show_trends is True pattern_badges = "" if show_trends and i in patterns_by_index: for pattern_text in patterns_by_index[i]: pattern_badges += f'{pattern_text}' html_spins.append(f'{spin}{pattern_badges}') # Wrap the spins in a div with flexbox to enable wrapping, and add a title html_output = f'

Last Spins

{"".join(html_spins)}
' # Add JavaScript to remove fade-in, flash, flip, and new-spin classes after animations html_output += ''' ''' return html_output def render_sides_of_zero_display(): left_hits = state.side_scores["Left Side of Zero"] zero_hits = state.scores[0] right_hits = state.side_scores["Right Side of Zero"] # Calculate the maximum hit count for scaling max_hits = max(left_hits, zero_hits, right_hits, 1) # Avoid division by zero # Calculate progress percentages (0 to 100) left_progress = (left_hits / max_hits) * 100 if max_hits > 0 else 0 zero_progress = (zero_hits / max_hits) * 100 if max_hits > 0 else 0 right_progress = (right_hits / max_hits) * 100 if max_hits > 0 else 0 # Define the order of numbers for the European roulette wheel original_order = [5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26, 0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10] left_side = original_order[:18] # 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26 zero = [0] right_side = original_order[19:] # 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10 wheel_order = left_side + zero + right_side # Used for wheel SVG, now 5, ..., 26, 0, 32, ..., 10 # Define betting sections jeu_0 = [12, 35, 3, 26, 0, 32, 15] voisins_du_zero = [22, 18, 29, 7, 28, 12, 35, 3, 26, 0, 32, 15, 19, 4, 21, 2, 25] orphelins = [17, 34, 6, 1, 20, 14, 31, 9] tiers_du_cylindre = [27, 13, 36, 11, 30, 8, 23, 10, 5, 24, 16, 33] # Calculate hit counts for each betting section jeu_0_hits = sum(state.scores.get(num, 0) for num in jeu_0) voisins_du_zero_hits = sum(state.scores.get(num, 0) for num in voisins_du_zero) orphelins_hits = sum(state.scores.get(num, 0) for num in orphelins) tiers_du_cylindre_hits = sum(state.scores.get(num, 0) for num in tiers_du_cylindre) # Determine the winning section for Left/Right Side winning_section = "Left Side" if left_hits > right_hits else "Right Side" if right_hits > left_hits else None # Get the latest spin for bounce effect and wheel rotation latest_spin = int(state.last_spins[-1]) if state.last_spins else None latest_spin_angle = 0 has_latest_spin = latest_spin is not None if latest_spin is not None: index = original_order.index(latest_spin) if latest_spin in original_order else 0 latest_spin_angle = (index * (360 / 37)) + 90 # Adjust for zero at bottom # Prepare numbers with hit counts wheel_numbers = [(num, state.scores.get(num, 0)) for num in wheel_order] # Calculate maximum hits for scaling highlights max_segment_hits = max(state.scores.values(), default=1) # Hot & Cold Numbers Display with Ties Handling and Cap hot_cold_html = '
' if state.last_spins and len(state.last_spins) >= 1: # Use state.scores for consistency with Strongest Numbers Tables hit_counts = {n: state.scores.get(n, 0) for n in range(37)} # Hot numbers: Sort by score descending, number ascending sorted_hot = sorted(hit_counts.items(), key=lambda x: (-x[1], x[0])) # Take top 5, but include all tied numbers at the 5th position, capped at 28 hot_numbers = [] if len(sorted_hot) >= 5: fifth_score = sorted_hot[4][1] # Score of the 5th number for num, score in sorted_hot: if len(hot_numbers) < 5 or score == fifth_score: if score > 0: # Only include numbers with hits hot_numbers.append((num, score)) else: break else: hot_numbers = [(num, score) for num, score in sorted_hot if score > 0] hot_numbers = hot_numbers[:28] # Cap at 28 to keep display compact # Cold numbers: Sort by score ascending, number ascending sorted_cold = sorted(hit_counts.items(), key=lambda x: (x[1], x[0])) # Take top 5, but include all tied numbers at the 5th position, capped at 15 cold_numbers = [] if len(sorted_cold) >= 5: fifth_score = sorted_cold[4][1] # Score of the 5th number for num, score in sorted_cold: if len(cold_numbers) < 5 or score == fifth_score: cold_numbers.append((num, score)) else: break else: cold_numbers = [(num, score) for num, score in sorted_cold] cold_numbers = cold_numbers[:15] # Cap at 15 to prevent overflow # Hot numbers display hot_cold_html += '
' hot_cold_html += '🔥 Hot' hot_display = [] for num, hits in hot_numbers: hot_display.append( f'{num}{hits}' ) hot_cold_html += "".join(hot_display) if hot_display else 'None' hot_cold_html += '
' # Cold numbers display hot_cold_html += '
' hot_cold_html += '🧊 Cold' cold_display = [] for num, hits in cold_numbers: cold_display.append( f'{num}{hits}' ) hot_cold_html += "".join(cold_display) if cold_display else 'None' hot_cold_html += '
' else: hot_cold_html += '

No spins yet to analyze.

' hot_cold_html += '
' # Generate HTML for the number list def generate_number_list(numbers): if not numbers: return '
No numbers
' number_html = [] # Use left_side as is for display display_left_side = left_side # Already 5, 24, 16, ..., 26 display_wheel_order = display_left_side + zero + right_side # 5, ..., 26, 0, 32, ..., 10 display_numbers = [(num, state.scores.get(num, 0)) for num in display_wheel_order] for num, hits in display_numbers: color = colors.get(str(num), "black") badge = f'{hits}' if hits > 0 else '' class_name = "number-item" + (" zero-number" if num == 0 else "") + (" bounce" if num == latest_spin else "") number_html.append( f'{num}{badge}' ) return f'
{"".join(number_html)}
' number_list = generate_number_list(wheel_numbers) # Generate SVG for the roulette wheel wheel_svg = '
' wheel_svg += '' # Size unchanged # Add background arcs for Left Side and Right Side left_start_angle = 0 left_end_angle = 180 left_start_rad = left_start_angle * (3.14159 / 180) left_end_rad = left_end_angle * (3.14159 / 180) left_x1 = 170 + 145 * math.cos(left_start_rad) left_y1 = 170 + 145 * math.sin(left_start_rad) left_x2 = 170 + 145 * math.cos(left_end_rad) left_y2 = 170 + 145 * math.sin(left_end_rad) left_path_d = f"M 170,170 L {left_x1},{left_y1} A 145,145 0 0,1 {left_x2},{left_y2} L 170,170 Z" left_fill = "rgba(106, 27, 154, 0.5)" if winning_section == "Left Side" else "rgba(128, 128, 128, 0.3)" left_stroke = "#4A148C" if winning_section == "Left Side" else "#808080" wheel_svg += f'' right_start_angle = 180 right_end_angle = 360 right_start_rad = right_start_angle * (3.14159 / 180) right_end_rad = right_end_angle * (3.14159 / 180) right_x1 = 170 + 145 * math.cos(right_start_rad) right_y1 = 170 + 145 * math.sin(right_start_rad) right_x2 = 170 + 145 * math.cos(right_end_rad) right_y2 = 170 + 145 * math.sin(right_end_rad) right_path_d = f"M 170,170 L {right_x1},{left_y1} A 145,145 0 0,1 {right_x2},{right_y2} L 170,170 Z" right_fill = "rgba(244, 81, 30, 0.5)" if winning_section == "Right Side" else "rgba(128, 128, 128, 0.3)" right_stroke = "#D84315" if winning_section == "Right Side" else "#808080" wheel_svg += f'' # Add the wheel background wheel_svg += '' # Draw the wheel segments angle_per_number = 360 / 37 for i, num in enumerate(original_order): angle = i * angle_per_number color = colors.get(str(num), "black") hits = state.scores.get(num, 0) stroke_width = 2 + (hits / max_segment_hits * 3) if max_segment_hits > 0 else 2 opacity = 0.5 + (hits / max_segment_hits * 0.5) if max_segment_hits > 0 else 0.5 stroke_color = "#FF00FF" if hits > 0 else "#FFF" is_winning_segment = (winning_section == "Left Side" and num in left_side) or (winning_section == "Right Side" and num in right_side) class_name = "wheel-segment" + (" pulse" if hits > 0 else "") + (" winning-segment" if is_winning_segment else "") rad = angle * (3.14159 / 180) next_rad = (angle + angle_per_number) * (3.14159 / 180) x1 = 170 + 135 * math.cos(rad) y1 = 170 + 135 * math.sin(rad) x2 = 170 + 135 * math.cos(next_rad) y2 = 170 + 135 * math.sin(next_rad) x3 = 170 + 105 * math.cos(next_rad) y3 = 170 + 105 * math.sin(next_rad) x4 = 170 + 105 * math.cos(rad) y4 = 170 + 105 * math.sin(rad) path_d = f"M 170,170 L {x1},{y1} A 135,135 0 0,1 {x2},{y2} L {x3},{y3} A 105,105 0 0,0 {x4},{y4} Z" wheel_svg += f'' text_angle = angle + (angle_per_number / 2) text_rad = text_angle * (3.14159 / 180) text_x = 170 + 120 * math.cos(text_rad) text_y = 170 + 120 * math.sin(text_rad) wheel_svg += f'{num}' hit_text_x = 170 + 90 * math.cos(text_rad) hit_text_y = 170 + 90 * math.sin(text_rad) wheel_svg += f'{hits if hits > 0 else ""}' # Add labels for Left Side and Right Side left_label_angle = 90 left_label_rad = left_label_angle * (3.14159 / 180) left_label_x = 170 + 155 * math.cos(left_label_rad) left_label_y = 170 + 155 * math.sin(left_label_rad) wheel_svg += f'' wheel_svg += f'Left: {left_hits}' right_label_angle = 270 right_label_rad = right_label_angle * (3.14159 / 180) right_label_x = 170 + 155 * math.cos(right_label_rad) right_label_y = 170 + 155 * math.sin(right_label_rad) wheel_svg += f'' wheel_svg += f'Right: {right_hits}' wheel_svg += '' # Gold center wheel_svg += '' wheel_svg += f'
' wheel_svg += f'
' wheel_svg += f'' wheel_svg += '
' # Add static betting sections display below the wheel with enhanced effects betting_sections_html = '
' sections = [ ("jeu_0", "Jeu 0", jeu_0, "#228B22", jeu_0_hits), ("voisins_du_zero", "Voisins du Zero", voisins_du_zero, "#008080", voisins_du_zero_hits), ("orphelins", "Orphelins", orphelins, "#800080", orphelins_hits), ("tiers_du_cylindre", "Tiers du Cylindre", tiers_du_cylindre, "#FFA500", tiers_du_cylindre_hits) ] for section_id, section_name, numbers, color, hits in sections: # Generate the numbers list with colors and enhanced effects for numbers with hits numbers_html = [] for num in numbers: num_color = colors.get(str(num), "black") hit_count = state.scores.get(num, 0) is_hot = hit_count > 0 class_name = "section-number" + (" hot-number" if is_hot else "") badge = f'{hit_count}' if is_hot else '' numbers_html.append(f'{num}{badge}') numbers_display = "".join(numbers_html) # Create a static section instead of an accordion badge = f'{hits}' if hits > 0 else '' betting_sections_html += f'''
{section_name}{badge}
{numbers_display}
''' betting_sections_html += '
' # Convert Python boolean to JavaScript lowercase boolean js_has_latest_spin = "true" if has_latest_spin else "false" # HTML output with JavaScript to handle animations and interactivity return f"""

Dealer’s Spin Tracker (Can you spot Bias???) 🔍

{left_hits}
Left Side
{zero_hits}
Zero
{right_hits}
Right Side
{hot_cold_html} {number_list} {wheel_svg} {betting_sections_html}
""" # Line 1: Start of updated validate_spins_input function def validate_spins_input(spins_input): """Validate manually entered spins and update state.""" import gradio as gr import time start_time = time.time() # CHANGED: Added for performance logging # CHANGED: Enhanced logging with input details print(f"validate_spins_input: Processing spins_input='{spins_input}'") # UNCHANGED: Handle empty input if not spins_input or not spins_input.strip(): print("validate_spins_input: No spins input provided.") return "", "

Last Spins

No spins entered.

" # CHANGED: Split and clean spins, enforce max limit raw_spins = [s.strip() for s in spins_input.split(",") if s.strip()] if len(raw_spins) > 1000: error_msg = f"Too many spins ({len(raw_spins)}). Maximum allowed is 1000." gr.Warning(error_msg) print(f"validate_spins_input: Error - {error_msg}") return "", f"

Last Spins

{error_msg}

" # CHANGED: Batch validate spins valid_spins = [] errors = [] invalid_inputs = [] for spin in raw_spins: try: num = int(spin) if not (0 <= num <= 36): errors.append(f"'{spin}' is out of range (must be 0-36)") invalid_inputs.append(spin) else: valid_spins.append(str(num)) except ValueError: errors.append(f"'{spin}' is not a valid integer") invalid_inputs.append(spin) # CHANGED: Improved error handling and messaging if not valid_spins: error_msg = "No valid spins found:\n- " + "\n- ".join(errors) + "\nUse comma-separated integers between 0 and 36 (e.g., 5, 12, 0)." gr.Warning(error_msg) print(f"validate_spins_input: Errors - {error_msg}") return "", f"

Last Spins

{error_msg}

" # UNCHANGED: Update state and scores state.last_spins = valid_spins state.selected_numbers = set(int(s) for s in valid_spins) action_log = update_scores_batch(valid_spins) for i, spin in enumerate(valid_spins): state.spin_history.append(action_log[i]) # UNCHANGED: Limit spin history to 100 spins if len(state.spin_history) > 100: state.spin_history.pop(0) # UNCHANGED: Generate output spins_display_value = ", ".join(valid_spins) formatted_html = format_spins_as_html(spins_display_value, 36) # Default to showing all spins # CHANGED: Detailed success logging print(f"validate_spins_input: Processed {len(valid_spins)} valid spins, spins_display_value='{spins_display_value}', time={time.time() - start_time:.3f}s") if invalid_inputs: print(f"validate_spins_input: Ignored invalid inputs: {', '.join(invalid_inputs)}") # CHANGED: Include invalid inputs in warning if present if errors: warning_msg = f"Processed {len(valid_spins)} valid spins. Invalid inputs ignored:\n- " + "\n- ".join(errors) + "\nUse integers 0-36." gr.Warning(warning_msg) print(f"validate_spins_input: Warning - {warning_msg}") return spins_display_value, formatted_html # Line 1: Start of updated add_spin function def add_spin(number, current_spins, num_to_show): import gradio as gr import time start_time = time.time() # CHANGED: Added for performance logging # CHANGED: Enhanced logging with input details print(f"add_spin: Processing number='{number}', current_spins='{current_spins}', num_to_show={num_to_show}") # CHANGED: Split and deduplicate spins numbers = [n.strip() for n in number.split(",") if n.strip()] unique_numbers = list(dict.fromkeys(numbers)) # Preserve order, remove duplicates if not unique_numbers: gr.Warning("No valid input provided. Please enter numbers between 0 and 36.") print("add_spin: No valid numbers provided.") return current_spins, current_spins, "

Last Spins

Error: No valid numbers provided.

", update_spin_counter(), render_sides_of_zero_display() # CHANGED: Reuse validate_spins_input for validation spins_input = ", ".join(unique_numbers) spins_display_value, formatted_html = validate_spins_input(spins_input) # CHANGED: Check if validation failed if not spins_display_value: print(f"add_spin: Validation failed, returning error HTML: {formatted_html}") return current_spins, current_spins, formatted_html, update_spin_counter(), render_sides_of_zero_display() # CHANGED: Efficiently update current spins current_spins_list = current_spins.split(", ") if current_spins and current_spins.strip() else [] if current_spins_list == [""]: current_spins_list = [] # CHANGED: Append new spins only if not already processed by validate_spins_input new_spins = current_spins_list + unique_numbers new_spins_str = ", ".join(new_spins) # CHANGED: Log duplicates if any if len(unique_numbers) < len(numbers): duplicates = [n for n in numbers if numbers.count(n) > 1] print(f"add_spin: Removed duplicates: {', '.join(set(duplicates))}") # CHANGED: Log success print(f"add_spin: Added {len(unique_numbers)} spins, new_spins_str='{new_spins_str}', time={time.time() - start_time:.3f}s") # UNCHANGED: Return updated outputs return new_spins_str, new_spins_str, formatted_html, update_spin_counter(), render_sides_of_zero_display() # Line 3: Start of next function (unchanged) def clear_spins(): state.selected_numbers.clear() state.last_spins = [] state.spin_history = [] # Clear spin history as well state.side_scores = {"Left Side of Zero": 0, "Right Side of Zero": 0} # Reset side scores state.scores = {n: 0 for n in range(37)} # Reset straight-up scores return "", "", "Spins cleared successfully!", "

Last Spins

No spins yet.

", update_spin_counter(), render_sides_of_zero_display() # In Part 1, replace save_session and load_session with: import json import tempfile import os from datetime import datetime def save_session(session_name): """ Save the current session to a JSON file with a user-specified name. Args: session_name (str): The desired name for the session file (without extension). Returns: str: Path to the saved JSON file, or None if an error occurs. """ try: # Sanitize the session name to avoid invalid characters session_name = "".join(c for c in (session_name or "") if c.isalnum() or c in ('_', '-', ' ')).strip() if not session_name: session_name = "WheelPulse_Session" # Add timestamp to ensure uniqueness timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") file_name = f"{session_name}_{timestamp}.json" # Collect session data session_data = { "spins": state.last_spins, "spin_history": state.spin_history, "scores": state.scores, "even_money_scores": state.even_money_scores, "dozen_scores": state.dozen_scores, "column_scores": state.column_scores, "street_scores": state.street_scores, "corner_scores": state.corner_scores, "six_line_scores": state.six_line_scores, "split_scores": state.split_scores, "side_scores": state.side_scores, "casino_data": state.casino_data, "use_casino_winners": state.use_casino_winners } # Create a temporary file temp_dir = tempfile.gettempdir() if not os.access(temp_dir, os.W_OK): raise PermissionError(f"No write permission in temporary directory: {temp_dir}") with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8', dir=temp_dir) as temp_file: json.dump(session_data, temp_file, indent=4) temp_file_path = temp_file.name # Rename the temporary file to the desired name final_path = os.path.join(temp_dir, file_name) os.rename(temp_file_path, final_path) print(f"save_session: Generated file at {final_path}") return final_path except Exception as e: print(f"save_session: Error: {str(e)}") return None def load_session(file, strategy_name, neighbours_count, strong_numbers_count, *checkbox_args): try: if file is None: return ("", "", "Please upload a session file to load.", "", "", "", "", "", "", "", "", "", "", "", create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count), "") with open(file.name, "r") as f: session_data = json.load(f) # Load state data state.last_spins = session_data.get("spins", []) state.spin_history = session_data.get("spin_history", []) state.scores = session_data.get("scores", {n: 0 for n in range(37)}) state.even_money_scores = session_data.get("even_money_scores", {name: 0 for name in EVEN_MONEY.keys()}) state.dozen_scores = session_data.get("dozen_scores", {name: 0 for name in DOZENS.keys()}) state.column_scores = session_data.get("column_scores", {name: 0 for name in COLUMNS.keys()}) state.street_scores = session_data.get("street_scores", {name: 0 for name in STREETS.keys()}) state.corner_scores = session_data.get("corner_scores", {name: 0 for name in CORNERS.keys()}) state.six_line_scores = session_data.get("six_line_scores", {name: 0 for name in SIX_LINES.keys()}) state.split_scores = session_data.get("split_scores", {name: 0 for name in SPLITS.keys()}) state.side_scores = session_data.get("side_scores", {"Left Side of Zero": 0, "Right Side of Zero": 0}) state.casino_data = session_data.get("casino_data", { "spins_count": 100, "hot_numbers": [], # Load as list "cold_numbers": [], # Load as list "even_odd": {"Even": 0.0, "Odd": 0.0}, "red_black": {"Red": 0.0, "Black": 0.0}, "low_high": {"Low": 0.0, "High": 0.0}, "dozens": {"1st Dozen": 0.0, "2nd Dozen": 0.0, "3rd Dozen": 0.0}, "columns": {"1st Column": 0.0, "2nd Column": 0.0, "3rd Column": 0.0} }) state.use_casino_winners = session_data.get("use_casino_winners", False) new_spins = ", ".join(state.last_spins) spin_analysis_output = f"Session loaded successfully with {len(state.last_spins)} spins." even_money_output = "\n".join([f"{name}: {score}" for name, score in state.even_money_scores.items()]) dozens_output = "\n".join([f"{name}: {score}" for name, score in state.dozen_scores.items()]) columns_output = "\n".join([f"{name}: {score}" for name, score in state.column_scores.items()]) streets_output = "\n".join([f"{name}: {score}" for name, score in state.street_scores.items()]) corners_output = "\n".join([f"{name}: {score}" for name, score in state.corner_scores.items()]) six_lines_output = "\n".join([f"{name}: {score}" for name, score in state.six_line_scores.items()]) splits_output = "\n".join([f"{name}: {score}" for name, score in state.split_scores.items()]) sides_output = "\n".join([f"{name}: {score}" for name, score in state.side_scores.items()]) straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]).sort_values(by="Score", ascending=False) straight_up_html = straight_up_df.to_html(index=False, classes="scrollable-table") top_18_df = straight_up_df[straight_up_df["Score"] > 0].head(18) top_18_html = top_18_df.to_html(index=False, classes="scrollable-table") strongest_numbers_output = ", ".join([str(n) for n, s in straight_up_df.head(3).iterrows() if s["Score"] > 0]) or "No numbers have hit yet." return ( new_spins, new_spins, spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count), show_strategy_recommendations(strategy_name, neighbours_count, strong_numbers_count) ) except Exception as e: print(f"load_session: Error loading session: {str(e)}") return ("", "", f"Error loading session: {str(e)}", "", "", "", "", "", "", "", "", "", "", "", create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count), "") # Function to calculate statistical insights def statistical_insights(): if not state.last_spins: return "No spins to analyze yet—click some numbers first!" total_spins = len(state.last_spins) number_freq = {num: state.scores[num] for num in state.scores if state.scores[num] > 0} top_numbers = sorted(number_freq.items(), key=lambda x: x[1], reverse=True)[:5] output = [f"Total Spins: {total_spins}"] output.append("Top 5 Numbers by Hits:") for num, hits in top_numbers: output.append(f"Number {num}: {hits} hits") return "\n".join(output) # Function to create HTML table (used in analyze_spins) def create_html_table(df, title): if df.empty: return f"

{title}

No data to display.

" html = f"

{title}

" html += '' html += "" + "".join(f"" for col in df.columns) + "" for _, row in df.iterrows(): html += "" + "".join(f"" for val in row) + "" html += "
{col}
{val}
" return html def create_strongest_numbers_with_neighbours_table(): straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty: return "

Strongest Numbers with Neighbours

No numbers have hit yet.

" # Create the HTML table table_html = '' table_html += "" # Table header for _, row in straight_up_df.iterrows(): num = str(row["Number"]) left, right = current_neighbors.get(row["Number"], ("", "")) left = str(left) if left is not None else "" right = str(right) if right is not None else "" score = row["Score"] table_html += f"" table_html += "
HitLeft N.Right N.Score
{num}{left}{right}{score}
" return f"

Strongest Numbers with Neighbours

{table_html}" def highlight_even_money(strategy_name, sorted_sections, top_color, middle_color, lower_color): """Highlight even money bets for relevant strategies.""" if sorted_sections is None: return None, None, None, {} trending, second, third = None, None, None number_highlights = {} if strategy_name in ["Best Even Money Bets", "Best Even Money Bets + Top Pick 18 Numbers", "Best Dozens + Best Even Money Bets + Top Pick 18 Numbers", "Best Columns + Best Even Money Bets + Top Pick 18 Numbers"]: even_money_hits = [item for item in sorted_sections["even_money"] if item[1] > 0] if even_money_hits: trending = even_money_hits[0][0] second = even_money_hits[1][0] if len(even_money_hits) > 1 else None third = even_money_hits[2][0] if len(even_money_hits) > 2 else None elif strategy_name == "Hot Bet Strategy": trending = sorted_sections["even_money"][0][0] if sorted_sections["even_money"] else None second = sorted_sections["even_money"][1][0] if len(sorted_sections["even_money"]) > 1 else None elif strategy_name == "Cold Bet Strategy": sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1]) trending = sorted_even_money[0][0] if sorted_even_money else None second = sorted_even_money[1][0] if len(sorted_even_money) > 1 else None elif strategy_name in ["3-8-6 Rising Martingale", "Fibonacci To Fortune"]: # For Fibonacci To Fortune, highlight only the top even money bet trending = sorted_sections["even_money"][0][0] if sorted_sections["even_money"] else None return trending, second, third, number_highlights def highlight_dozens(strategy_name, sorted_sections, top_color, middle_color, lower_color): """Highlight dozens for relevant strategies.""" if sorted_sections is None: return None, None, {} trending, second = None, None number_highlights = {} if strategy_name in ["Best Dozens", "Best Dozens + Top Pick 18 Numbers", "Best Dozens + Best Even Money Bets + Top Pick 18 Numbers", "Best Dozens + Best Streets"]: dozens_hits = [item for item in sorted_sections["dozens"] if item[1] > 0] if dozens_hits: trending = dozens_hits[0][0] second = dozens_hits[1][0] if len(dozens_hits) > 1 else None elif strategy_name == "Hot Bet Strategy": trending = sorted_sections["dozens"][0][0] if sorted_sections["dozens"] else None second = sorted_sections["dozens"][1][0] if len(sorted_sections["dozens"]) > 1 else None elif strategy_name == "Cold Bet Strategy": sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1]) trending = sorted_dozens[0][0] if sorted_dozens else None second = sorted_dozens[1][0] if len(sorted_dozens) > 1 else None elif strategy_name in ["Fibonacci Strategy", "Fibonacci To Fortune"]: # For Fibonacci To Fortune, always highlight the top two dozens trending = sorted_sections["dozens"][0][0] if sorted_sections["dozens"] else None second = sorted_sections["dozens"][1][0] if len(sorted_sections["dozens"]) > 1 else None elif strategy_name == "1 Dozen +1 Column Strategy": trending = sorted_sections["dozens"][0][0] if sorted_sections["dozens"] and sorted_sections["dozens"][0][1] > 0 else None elif strategy_name == "Romanowksy Missing Dozen": trending = sorted_sections["dozens"][0][0] if sorted_sections["dozens"] and sorted_sections["dozens"][0][1] > 0 else None second = sorted_sections["dozens"][1][0] if len(sorted_sections["dozens"]) > 1 and sorted_sections["dozens"][1][1] > 0 else None weakest_dozen = min(state.dozen_scores.items(), key=lambda x: x[1], default=("1st Dozen", 0))[0] straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) weak_numbers = [row["Number"] for _, row in straight_up_df.iterrows() if row["Number"] in DOZENS[weakest_dozen]][:8] for num in weak_numbers: number_highlights[str(num)] = top_color return trending, second, number_highlights def highlight_columns(strategy_name, sorted_sections, top_color, middle_color, lower_color): """Highlight columns for relevant strategies.""" if sorted_sections is None: return None, None, {} trending, second = None, None number_highlights = {} if strategy_name in ["Best Columns", "Best Columns + Top Pick 18 Numbers", "Best Columns + Best Even Money Bets + Top Pick 18 Numbers", "Best Columns + Best Streets"]: columns_hits = [item for item in sorted_sections["columns"] if item[1] > 0] if columns_hits: trending = columns_hits[0][0] second = columns_hits[1][0] if len(columns_hits) > 1 else None elif strategy_name == "Hot Bet Strategy": trending = sorted_sections["columns"][0][0] if sorted_sections["columns"] else None second = sorted_sections["columns"][1][0] if len(sorted_sections["columns"]) > 1 else None elif strategy_name == "Cold Bet Strategy": sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1]) trending = sorted_columns[0][0] if sorted_columns else None second = sorted_columns[1][0] if len(sorted_columns) > 1 else None elif strategy_name in ["Fibonacci Strategy", "Fibonacci To Fortune"]: # For Fibonacci To Fortune, always highlight the top two columns trending = sorted_sections["columns"][0][0] if sorted_sections["columns"] else None second = sorted_sections["columns"][1][0] if len(sorted_sections["columns"]) > 1 else None elif strategy_name == "1 Dozen +1 Column Strategy": trending = sorted_sections["columns"][0][0] if sorted_sections["columns"] and sorted_sections["columns"][0][1] > 0 else None return trending, second, number_highlights def highlight_numbers(strategy_name, sorted_sections, top_color, middle_color, lower_color): """Highlight straight-up numbers for relevant strategies.""" if sorted_sections is None: return {} number_highlights = {} straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if strategy_name in ["Top Pick 18 Numbers without Neighbours", "Best Even Money Bets + Top Pick 18 Numbers", "Best Dozens + Top Pick 18 Numbers", "Best Columns + Top Pick 18 Numbers", "Best Dozens + Best Even Money Bets + Top Pick 18 Numbers", "Best Columns + Best Even Money Bets + Top Pick 18 Numbers"]: if len(straight_up_df) >= 18: top_18_numbers = straight_up_df["Number"].head(18).tolist() for i, num in enumerate(top_18_numbers): color = top_color if i < 6 else (middle_color if i < 12 else lower_color) number_highlights[str(num)] = color elif strategy_name == "Top Numbers with Neighbours (Tiered)": num_to_take = min(8, len(straight_up_df)) top_numbers = set(straight_up_df["Number"].head(num_to_take).tolist()) number_groups = [] for num in top_numbers: left, right = current_neighbors.get(num, (None, None)) group = [num] if left is not None: group.append(left) if right is not None: group.append(right) number_groups.append((state.scores[num], group)) number_groups.sort(key=lambda x: x[0], reverse=True) ordered_numbers = [] for _, group in number_groups: ordered_numbers.extend(group) ordered_numbers = ordered_numbers[:24] for i, num in enumerate(ordered_numbers): color = top_color if i < 8 else (middle_color if i < 16 else lower_color) number_highlights[str(num)] = color return number_highlights def highlight_other_bets(strategy_name, sorted_sections, top_color, middle_color, lower_color): """Highlight streets, corners, splits, and double streets for relevant strategies.""" if sorted_sections is None: return {} number_highlights = {} if strategy_name == "Hot Bet Strategy": for i, (street_name, _) in enumerate(sorted_sections["streets"][:9]): numbers = STREETS[street_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color for i, (corner_name, _) in enumerate(sorted_sections["corners"][:9]): numbers = CORNERS[corner_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color for i, (split_name, _) in enumerate(sorted_sections["splits"][:9]): numbers = SPLITS[split_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Cold Bet Strategy": sorted_streets = sorted(state.street_scores.items(), key=lambda x: x[1]) sorted_corners = sorted(state.corner_scores.items(), key=lambda x: x[1]) sorted_splits = sorted(state.split_scores.items(), key=lambda x: x[1]) for i, (street_name, _) in enumerate(sorted_streets[:9]): numbers = STREETS[street_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color for i, (corner_name, _) in enumerate(sorted_corners[:9]): numbers = CORNERS[corner_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color for i, (split_name, _) in enumerate(sorted_splits[:9]): numbers = SPLITS[split_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Best Streets": for i, (street_name, _) in enumerate(sorted_sections["streets"][:9]): numbers = STREETS[street_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name in ["Best Dozens + Best Streets", "Best Columns + Best Streets"]: for i, (street_name, _) in enumerate(sorted_sections["streets"][:9]): numbers = STREETS[street_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Best Double Streets": for i, (six_line_name, _) in enumerate(sorted_sections["six_lines"][:9]): numbers = SIX_LINES[six_line_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Best Corners": for i, (corner_name, _) in enumerate(sorted_sections["corners"][:9]): numbers = CORNERS[corner_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Best Splits": for i, (split_name, _) in enumerate(sorted_sections["splits"][:9]): numbers = SPLITS[split_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Non-Overlapping Double Street Strategy": non_overlapping_sets = [ ["1ST D.STREET – 1, 4", "3RD D.STREET – 7, 10", "5TH D.STREET – 13, 16", "7TH D.STREET – 19, 22", "9TH D.STREET – 25, 28"], ["2ND D.STREET – 4, 7", "4TH D.STREET – 10, 13", "6TH D.STREET – 16, 19", "8TH D.STREET – 22, 25", "10TH D.STREET – 28, 31"] ] set_scores = [] for idx, non_overlapping_set in enumerate(non_overlapping_sets): total_score = sum(state.six_line_scores.get(name, 0) for name in non_overlapping_set) set_scores.append((idx, total_score, non_overlapping_set)) best_set = max(set_scores, key=lambda x: x[1], default=(0, 0, non_overlapping_sets[0])) sorted_best_set = sorted(best_set[2], key=lambda name: state.six_line_scores.get(name, 0), reverse=True)[:9] for i, double_street_name in enumerate(sorted_best_set): numbers = SIX_LINES[double_street_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Non-Overlapping Corner Strategy": sorted_corners = sorted(state.corner_scores.items(), key=lambda x: x[1], reverse=True) selected_corners = [] selected_numbers = set() for corner_name, _ in sorted_corners: if len(selected_corners) >= 9: break corner_numbers = set(CORNERS[corner_name]) if not corner_numbers & selected_numbers: selected_corners.append(corner_name) selected_numbers.update(corner_numbers) for i, corner_name in enumerate(selected_corners): numbers = CORNERS[corner_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "3-8-6 Rising Martingale": top_streets = sorted_sections["streets"][:8] for i, (street_name, _) in enumerate(top_streets): numbers = STREETS[street_name] color = top_color if i < 3 else (middle_color if 3 <= i < 6 else lower_color) for num in numbers: number_highlights[str(num)] = color elif strategy_name == "Fibonacci To Fortune": # Highlight the best double street in the weakest dozen, excluding numbers from the top two dozens sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) weakest_dozen = min(state.dozen_scores.items(), key=lambda x: x[1], default=("1st Dozen", 0))[0] top_two_dozens = [item[0] for item in sorted_dozens[:2]] top_two_dozen_numbers = set() for dozen_name in top_two_dozens: top_two_dozen_numbers.update(DOZENS[dozen_name]) double_streets_in_weakest = [ (name, state.six_line_scores.get(name, 0)) for name, numbers in SIX_LINES.items() if set(numbers).issubset(DOZENS[weakest_dozen]) and not set(numbers).intersection(top_two_dozen_numbers) ] if double_streets_in_weakest: top_double_street = max(double_streets_in_weakest, key=lambda x: x[1])[0] for num in SIX_LINES[top_double_street]: number_highlights[str(num)] = top_color return number_highlights def highlight_neighbors(strategy_name, sorted_sections, neighbours_count, strong_numbers_count, top_color, middle_color): """Highlight neighbors for the Neighbours of Strong Number strategy.""" if sorted_sections is None: return {} number_highlights = {} if strategy_name == "Neighbours of Strong Number": sorted_numbers = sorted(state.scores.items(), key=lambda x: (-x[1], x[0])) numbers_hits = [item for item in sorted_numbers if item[1] > 0] if numbers_hits: strong_numbers_count = min(strong_numbers_count, len(numbers_hits)) top_numbers = set(item[0] for item in numbers_hits[:strong_numbers_count]) neighbors_set = set() for strong_number in top_numbers: current_number = strong_number for _ in range(neighbours_count): left, _ = current_neighbors.get(current_number, (None, None)) if left is not None: neighbors_set.add(left) current_number = left else: break current_number = strong_number for _ in range(neighbours_count): _, right = current_neighbors.get(current_number, (None, None)) if right is not None: neighbors_set.add(right) current_number = right else: break neighbors_set = neighbors_set - top_numbers for num in top_numbers: number_highlights[str(num)] = top_color for num in neighbors_set: number_highlights[str(num)] = middle_color return number_highlights # Function to create the dynamic roulette table with highlighted trending sections def calculate_trending_sections(): """Calculate trending sections based on current scores.""" if not any(state.scores.values()) and not any(state.even_money_scores.values()): return None # Indicates no data to process return { "even_money": sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True), "dozens": sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True), "columns": sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True), "streets": sorted(state.street_scores.items(), key=lambda x: x[1], reverse=True), "six_lines": sorted(state.six_line_scores.items(), key=lambda x: x[1], reverse=True), "corners": sorted(state.corner_scores.items(), key=lambda x: x[1], reverse=True), "splits": sorted(state.split_scores.items(), key=lambda x: x[1], reverse=True) } # Line 1: Start of apply_strategy_highlights function (updated) # Line 1: Start of apply_strategy_highlights function (updated) # Line 1: Start of apply_strategy_highlights function (updated with neighbor highlights) def apply_strategy_highlights(strategy_name, neighbours_count, strong_numbers_count, sorted_sections, top_color=None, middle_color=None, lower_color=None, suggestions=None): """Apply highlights based on the selected strategy with custom colors, passing suggestions for outside bets.""" if sorted_sections is None: return None, None, None, None, None, None, None, {}, "white", "white", "white", None # Set default colors unless overridden if strategy_name == "Cold Bet Strategy": top_color = "#D3D3D3" # Light Gray (Cold Top) middle_color = "#DDA0DD" # Plum (Cold Middle) lower_color = "#E0FFFF" # Light Cyan (Cold Lower) else: top_color = top_color if top_color else "rgba(255, 255, 0, 0.5)" # Yellow middle_color = middle_color if middle_color else "rgba(0, 255, 255, 0.5)" # Cyan lower_color = lower_color if lower_color else "rgba(0, 255, 0, 0.5)" # Green # Initialize highlight variables trending_even_money, second_even_money, third_even_money = None, None, None trending_dozen, second_dozen = None, None trending_column, second_column = None, None number_highlights = {} # Apply highlights based on strategy if strategy_name and strategy_name in STRATEGIES: strategy_info = STRATEGIES[strategy_name] if strategy_name == "Neighbours of Strong Number": result = strategy_info["function"](neighbours_count, strong_numbers_count) # Handle the tuple return value if isinstance(result, tuple) and len(result) == 2: recommendations, strategy_suggestions = result suggestions = suggestions if suggestions is not None else strategy_suggestions else: # Fallback in case the function doesn't return the expected tuple recommendations = result suggestions = None else: # Other strategies return a single string recommendations = strategy_info["function"]() suggestions = None # Delegate to helper functions em_trending, em_second, em_third, em_highlights = highlight_even_money(strategy_name, sorted_sections, top_color, middle_color, lower_color) dz_trending, dz_second, dz_highlights = highlight_dozens(strategy_name, sorted_sections, top_color, middle_color, lower_color) col_trending, col_second, col_highlights = highlight_columns(strategy_name, sorted_sections, top_color, middle_color, lower_color) num_highlights = highlight_numbers(strategy_name, sorted_sections, top_color, middle_color, lower_color) neighbor_highlights = highlight_neighbors(strategy_name, sorted_sections, neighbours_count, strong_numbers_count, top_color, middle_color) other_highlights = highlight_other_bets(strategy_name, sorted_sections, top_color, middle_color, lower_color) # Combine highlights trending_even_money = em_trending second_even_money = em_second third_even_money = em_third trending_dozen = dz_trending second_dozen = dz_second trending_column = col_trending second_column = col_second number_highlights.update(em_highlights) number_highlights.update(dz_highlights) number_highlights.update(col_highlights) number_highlights.update(num_highlights) number_highlights.update(neighbor_highlights) number_highlights.update(other_highlights) # Dozen Tracker Logic (When No Strategy is Selected) if strategy_name == "None": recent_spins = state.last_spins[-neighbours_count:] if len(state.last_spins) >= neighbours_count else state.last_spins dozen_counts = {"1st Dozen": 0, "2nd Dozen": 0, "3rd Dozen": 0} for spin in recent_spins: spin_value = int(spin) if spin_value != 0: for name, numbers in DOZENS.items(): if spin_value in numbers: dozen_counts[name] += 1 break sorted_dozens = sorted(dozen_counts.items(), key=lambda x: x[1], reverse=True) if sorted_dozens[0][1] > 0: trending_dozen = sorted_dozens[0][0] if sorted_dozens[1][1] > 0: second_dozen = sorted_dozens[1][0] return trending_even_money, second_even_money, third_even_money, trending_dozen, second_dozen, trending_column, second_column, number_highlights, top_color, middle_color, lower_color, suggestions # Line 1: Start of render_dynamic_table_html function (updated) def render_dynamic_table_html(trending_even_money, second_even_money, third_even_money, trending_dozen, second_dozen, trending_column, second_column, number_highlights, top_color, middle_color, lower_color, suggestions=None, hot_numbers=None, scores=None): """Generate HTML for the dynamic roulette table with improved visual clarity, using suggestions for highlighting outside bets.""" if all(v is None for v in [trending_even_money, second_even_money, third_even_money, trending_dozen, second_dozen, trending_column, second_column]) and not number_highlights and not suggestions: return "

Please analyze some spins first to see highlights on the dynamic table.

" # Define casino winners if highlighting is enabled, only for non-zero data casino_winners = {"hot_numbers": set(), "cold_numbers": set(), "even_money": set(), "dozens": set(), "columns": set()} if state.use_casino_winners: casino_winners["hot_numbers"] = set(state.casino_data["hot_numbers"].keys()) casino_winners["cold_numbers"] = set(state.casino_data["cold_numbers"].keys()) if any(state.casino_data["even_odd"].values()): casino_winners["even_money"].add(max(state.casino_data["even_odd"], key=state.casino_data["even_odd"].get)) if any(state.casino_data["red_black"].values()): casino_winners["even_money"].add(max(state.casino_data["red_black"], key=state.casino_data["red_black"].get)) if any(state.casino_data["low_high"].values()): casino_winners["even_money"].add(max(state.casino_data["low_high"], key=state.casino_data["low_high"].get)) if any(state.casino_data["dozens"].values()): casino_winners["dozens"] = {max(state.casino_data["dozens"], key=state.casino_data["dozens"].get)} if any(state.casino_data["columns"].values()): casino_winners["columns"] = {max(state.casino_data["columns"], key=state.casino_data["columns"].get)} print(f"Casino Winners Set: Hot={casino_winners['hot_numbers']}, Cold={casino_winners['cold_numbers']}, Even Money={casino_winners['even_money']}, Dozens={casino_winners['dozens']}, Columns={casino_winners['columns']}") # Initialize highlights for outside bets using suggestions (for Neighbours of Strong Number strategy) suggestion_highlights = {} if suggestions: # Parse suggestions to extract recommendations best_even_money = None best_bet = None play_two_first = None play_two_second = None for key, value in suggestions.items(): if key == "best_even_money" and "(Tied with" not in value: # Extract the even money bet (e.g., "Even: 5" -> "Even") best_even_money = value.split(":")[0].strip() elif key == "best_bet" and "(Tied with" not in value: # Extract the best bet (e.g., "2nd Column: 6" -> "2nd Column") best_bet = value.split(":")[0].strip() elif key == "play_two" and "(Tied with" not in value: # Extract the two options (e.g., "Play Two Columns: 2nd Column (6) and 1st Column (2)") parts = value.split(":", 1)[1].split(" and ") play_two_first = parts[0].split("(")[0].strip() # e.g., "2nd Column" play_two_second = parts[1].split("(")[0].strip() # e.g., "1st Column" # Apply highlights based on suggestions (yellow for top tier, green for second in Play Two) if best_even_money: suggestion_highlights[best_even_money] = top_color # Yellow for Best Even Money Bet if best_bet: suggestion_highlights[best_bet] = top_color # Yellow for Best Bet if play_two_first and play_two_second: # Ensure the first option in Play Two matches the Best Bet (if present) and gets yellow if best_bet and play_two_first == best_bet: suggestion_highlights[play_two_first] = top_color # Already set to yellow else: suggestion_highlights[play_two_first] = top_color # Yellow if not already set suggestion_highlights[play_two_second] = lower_color # Green for second option table_layout = [ ["", "3", "6", "9", "12", "15", "18", "21", "24", "27", "30", "33", "36"], ["0", "2", "5", "8", "11", "14", "17", "20", "23", "26", "29", "32", "35"], ["", "1", "4", "7", "10", "13", "16", "19", "22", "25", "28", "31", "34"] ] html = '' html += '' html += '' for _ in range(12): html += '' html += '' html += '' # Ensure hot_numbers is a set for consistent comparison hot_numbers = set(hot_numbers) if hot_numbers else set() # Debug scores to verify hit counts scores = scores if scores is not None else {} print(f"render_dynamic_table_html: Hot numbers={hot_numbers}, Scores={dict(scores)}") for row_idx, row in enumerate(table_layout): html += "" for num in row: if num == "": html += '' else: base_color = colors.get(num, "black") highlight_color = number_highlights.get(num, base_color) if num in casino_winners["hot_numbers"]: border_style = "3px solid #FFD700" # Gold, solid for consistent glow elif num in casino_winners["cold_numbers"]: border_style = "3px solid #C0C0C0" # Silver, solid for consistent glow else: border_style = "3px solid black" text_style = "color: white; font-weight: bold; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);" cell_class = "hot-number has-tooltip" if num in hot_numbers else "has-tooltip" hit_count = scores.get(num, scores.get(int(num), 0) if num.isdigit() else 0) tooltip = f"Hit {hit_count} times" html += f'' if row_idx == 0: bg_color = suggestion_highlights.get("3rd Column", top_color if trending_column == "3rd Column" else (middle_color if second_column == "3rd Column" else "white")) border_style = "3px dashed #FFD700" if "3rd Column" in casino_winners["columns"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" # Compute column score and progress bar col_score = state.column_scores.get("3rd Column", 0) max_col_score = max(state.column_scores.values(), default=1) or 1 # Avoid division by zero fill_percentage = (col_score / max_col_score) * 100 html += f'' elif row_idx == 1: bg_color = suggestion_highlights.get("2nd Column", top_color if trending_column == "2nd Column" else (middle_color if second_column == "2nd Column" else "white")) border_style = "3px dashed #FFD700" if "2nd Column" in casino_winners["columns"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" col_score = state.column_scores.get("2nd Column", 0) max_col_score = max(state.column_scores.values(), default=1) or 1 fill_percentage = (col_score / max_col_score) * 100 html += f'' elif row_idx == 2: bg_color = suggestion_highlights.get("1st Column", top_color if trending_column == "1st Column" else (middle_color if second_column == "1st Column" else "white")) border_style = "3px dashed #FFD700" if "1st Column" in casino_winners["columns"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" col_score = state.column_scores.get("1st Column", 0) max_col_score = max(state.column_scores.values(), default=1) or 1 fill_percentage = (col_score / max_col_score) * 100 html += f'' html += "" html += "" html += '' bg_color = suggestion_highlights.get("Low", top_color if trending_even_money == "Low" else (middle_color if second_even_money == "Low" else (lower_color if third_even_money == "Low" else "white"))) border_style = "3px dashed #FFD700" if "Low" in casino_winners["even_money"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" low_score = state.even_money_scores.get("Low", 0) max_even_money_score = max(state.even_money_scores.values(), default=1) or 1 fill_percentage = (low_score / max_even_money_score) * 100 html += f'' bg_color = suggestion_highlights.get("High", top_color if trending_even_money == "High" else (middle_color if second_even_money == "High" else (lower_color if third_even_money == "High" else "white"))) border_style = "3px dashed #FFD700" if "High" in casino_winners["even_money"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" high_score = state.even_money_scores.get("High", 0) fill_percentage = (high_score / max_even_money_score) * 100 html += f'' html += '' html += "" html += "" html += '' bg_color = suggestion_highlights.get("1st Dozen", top_color if trending_dozen == "1st Dozen" else (middle_color if second_dozen == "1st Dozen" else "white")) border_style = "3px dashed #FFD700" if "1st Dozen" in casino_winners["dozens"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" dozen_score = state.dozen_scores.get("1st Dozen", 0) max_dozen_score = max(state.dozen_scores.values(), default=1) or 1 fill_percentage = (dozen_score / max_dozen_score) * 100 html += f'' bg_color = suggestion_highlights.get("2nd Dozen", top_color if trending_dozen == "2nd Dozen" else (middle_color if second_dozen == "2nd Dozen" else "white")) border_style = "3px dashed #FFD700" if "2nd Dozen" in casino_winners["dozens"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" dozen_score = state.dozen_scores.get("2nd Dozen", 0) fill_percentage = (dozen_score / max_dozen_score) * 100 html += f'' bg_color = suggestion_highlights.get("3rd Dozen", top_color if trending_dozen == "3rd Dozen" else (middle_color if second_dozen == "3rd Dozen" else "white")) border_style = "3px dashed #FFD700" if "3rd Dozen" in casino_winners["dozens"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" dozen_score = state.dozen_scores.get("3rd Dozen", 0) fill_percentage = (dozen_score / max_dozen_score) * 100 html += f'' html += '' html += "" html += "" html += '' html += f'' bg_color = suggestion_highlights.get("Odd", top_color if trending_even_money == "Odd" else (middle_color if second_even_money == "Odd" else (lower_color if third_even_money == "Odd" else "white"))) border_style = "3px dashed #FFD700" if "Odd" in casino_winners["even_money"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" odd_score = state.even_money_scores.get("Odd", 0) max_even_money_score = max(state.even_money_scores.values(), default=1) or 1 fill_percentage = (odd_score / max_even_money_score) * 100 html += f'' bg_color = suggestion_highlights.get("Red", top_color if trending_even_money == "Red" else (middle_color if second_even_money == "Red" else (lower_color if third_even_money == "Red" else "white"))) border_style = "3px dashed #FFD700" if "Red" in casino_winners["even_money"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" red_score = state.even_money_scores.get("Red", 0) fill_percentage = (red_score / max_even_money_score) * 100 html += f'' bg_color = suggestion_highlights.get("Black", top_color if trending_even_money == "Black" else (middle_color if second_even_money == "Black" else (lower_color if third_even_money == "Black" else "white"))) border_style = "3px dashed #FFD700" if "Black" in casino_winners["even_money"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" black_score = state.even_money_scores.get("Black", 0) fill_percentage = (black_score / max_even_money_score) * 100 html += f'' bg_color = suggestion_highlights.get("Even", top_color if trending_even_money == "Even" else (middle_color if second_even_money == "Even" else (lower_color if third_even_money == "Even" else "white"))) border_style = "3px dashed #FFD700" if "Even" in casino_winners["even_money"] else "1px solid black" tier_class = "top-tier" if bg_color == top_color else "middle-tier" if bg_color == middle_color else "lower-tier" if bg_color == lower_color else "" even_score = state.even_money_scores.get("Even", 0) fill_percentage = (even_score / max_even_money_score) * 100 html += f'' html += f'' html += '' html += "" html += "
{num}3rd Column
2nd Column
1st Column
Low (1 to 18)
High (19 to 36)
1st Dozen
2nd Dozen
3rd Dozen
ODD
RED
BLACK
EVEN
" return html def update_casino_data(spins_count, even_percent, odd_percent, red_percent, black_percent, low_percent, high_percent, dozen1_percent, dozen2_percent, dozen3_percent, col1_percent, col2_percent, col3_percent, use_winners): """Parse casino data inputs, update state, and generate HTML output.""" try: state.casino_data["spins_count"] = int(spins_count) state.use_casino_winners = use_winners # Remove Hot/Cold Numbers parsing state.casino_data["hot_numbers"] = {} state.casino_data["cold_numbers"] = {} # Parse percentages from dropdowns def parse_percent(value, category, key): try: return float(value) if value != "00" else 0.0 except ValueError: raise ValueError(f"Invalid {category} percentage for {key}: {value}") # Even/Odd even_val = parse_percent(even_percent, "Even vs Odd", "Even") odd_val = parse_percent(odd_percent, "Even vs Odd", "Odd") state.casino_data["even_odd"] = {"Even": even_val, "Odd": odd_val} has_even_odd = even_val > 0 or odd_val > 0 # Red/Black red_val = parse_percent(red_percent, "Red vs Black", "Red") black_val = parse_percent(black_percent, "Red vs Black", "Black") state.casino_data["red_black"] = {"Red": red_val, "Black": black_val} has_red_black = red_val > 0 or black_val > 0 # Low/High low_val = parse_percent(low_percent, "Low vs High", "Low") high_val = parse_percent(high_percent, "Low vs High", "High") state.casino_data["low_high"] = {"Low": low_val, "High": high_val} has_low_high = low_val > 0 or high_val > 0 # Dozens d1_val = parse_percent(dozen1_percent, "Dozens", "1st Dozen") d2_val = parse_percent(dozen2_percent, "Dozens", "2nd Dozen") d3_val = parse_percent(dozen3_percent, "Dozens", "3rd Dozen") state.casino_data["dozens"] = {"1st Dozen": d1_val, "2nd Dozen": d2_val, "3rd Dozen": d3_val} has_dozens = d1_val > 0 or d2_val > 0 or d3_val > 0 # Columns c1_val = parse_percent(col1_percent, "Columns", "1st Column") c2_val = parse_percent(col2_percent, "Columns", "2nd Column") c3_val = parse_percent(col3_percent, "Columns", "3rd Column") state.casino_data["columns"] = {"1st Column": c1_val, "2nd Column": c2_val, "3rd Column": c3_val} has_columns = c1_val > 0 or c2_val > 0 or c3_val > 0 # Check for empty data when highlighting is enabled if use_winners and not any([has_even_odd, has_red_black, has_low_high, has_dozens, has_columns]): gr.Warning("Highlight Casino Winners is enabled, but no casino data is provided. Enter percentages to see highlights.") return "

Warning: No casino data provided for highlighting. Please enter percentages for Even/Odd, Red/Black, Low/High, Dozens, or Columns.

" # Generate HTML Output output = f"

Casino Data Insights (Last {spins_count} Spins):

" for key, name, has_data in [ ("even_odd", "Even vs Odd", has_even_odd), ("red_black", "Red vs Black", has_red_black), ("low_high", "Low vs High", has_low_high) ]: if has_data: winner = max(state.casino_data[key], key=state.casino_data[key].get) output += f"

{name}: " + " vs ".join( f"{v:.1f}%" if k == winner else f"{v:.1f}%" for k, v in state.casino_data[key].items() ) + f" (Winner: {winner})

" else: output += f"

{name}: Not set

" for key, name, has_data in [ ("dozens", "Dozens", has_dozens), ("columns", "Columns", has_columns) ]: if has_data: winner = max(state.casino_data[key], key=state.casino_data[key].get) output += f"

{name}: " + " vs ".join( f"{v:.1f}%" if k == winner else f"{v:.1f}%" for k, v in state.casino_data[key].items() ) + f" (Winner: {winner})

" else: output += f"

{name}: Not set

" print(f"Generated HTML Output: {output}") return output except ValueError as e: return f"

Error: {str(e)}

" except Exception as e: return f"

Unexpected error parsing casino data: {str(e)}

" def reset_casino_data(): """Reset casino data to defaults and clear UI inputs.""" state.casino_data = { "spins_count": 100, "hot_numbers": {}, "cold_numbers": {}, "even_odd": {"Even": 0.0, "Odd": 0.0}, "red_black": {"Red": 0.0, "Black": 0.0}, "low_high": {"Low": 0.0, "High": 0.0}, "dozens": {"1st Dozen": 0.0, "2nd Dozen": 0.0, "3rd Dozen": 0.0}, "columns": {"1st Column": 0.0, "2nd Column": 0.0, "3rd Column": 0.0} } state.use_casino_winners = False return ( "100", # spins_count_dropdown "", # hot_numbers_input "", # cold_numbers_input "", # even_odd_input "", # red_black_input "", # low_high_input "", # dozens_input "", # columns_input False, # use_winners_checkbox "

Casino data reset to defaults.

" # casino_data_output ) # Line 1: Start of create_dynamic_table function (updated) def create_dynamic_table(strategy_name=None, neighbours_count=2, strong_numbers_count=1, dozen_tracker_spins=5, top_color=None, middle_color=None, lower_color=None): try: print(f"create_dynamic_table called with strategy: {strategy_name}, neighbours_count: {neighbours_count}, strong_numbers_count: {strong_numbers_count}, dozen_tracker_spins: {dozen_tracker_spins}, top_color: {top_color}, middle_color: {middle_color}, lower_color: {lower_color}") print(f"Using casino winners: {state.use_casino_winners}, Hot Numbers: {state.casino_data['hot_numbers']}, Cold Numbers: {state.casino_data['cold_numbers']}") print("create_dynamic_table: Calculating trending sections") sorted_sections = calculate_trending_sections() print(f"create_dynamic_table: sorted_sections={sorted_sections}") # If no spins yet, initialize with default even money focus if sorted_sections is None and strategy_name == "Best Even Money Bets": print("create_dynamic_table: No spins yet, using default even money focus") trending_even_money = "Red" # Default to "Red" as an example second_even_money = "Black" third_even_money = "Even" trending_dozen = None second_dozen = None trending_column = None second_column = None number_highlights = {} top_color = top_color if top_color else "rgba(255, 255, 0, 0.5)" middle_color = middle_color if middle_color else "rgba(0, 255, 255, 0.5)" lower_color = lower_color if lower_color else "rgba(0, 255, 0, 0.5)" suggestions = None hot_numbers = [] # No hot numbers without spins else: print("create_dynamic_table: Applying strategy highlights") trending_even_money, second_even_money, third_even_money, trending_dozen, second_dozen, trending_column, second_column, number_highlights, top_color, middle_color, lower_color, suggestions = apply_strategy_highlights(strategy_name, int(dozen_tracker_spins) if strategy_name == "None" else neighbours_count, strong_numbers_count, sorted_sections, top_color, middle_color, lower_color) print(f"create_dynamic_table: Strategy highlights applied - trending_even_money={trending_even_money}, second_even_money={second_even_money}, third_even_money={third_even_money}, trending_dozen={trending_dozen}, second_dozen={second_dozen}, trending_column={trending_column}, second_column={second_column}, number_highlights={number_highlights}") # Determine hot numbers (top 5 with hits) sorted_scores = sorted(state.scores.items(), key=lambda x: x[1], reverse=True) hot_numbers = [str(num) for num, score in sorted_scores[:5] if score > 0] print(f"create_dynamic_table: Hot numbers={hot_numbers}, Scores={dict(state.scores)}") # If still no highlights and no sorted_sections, provide a default message if sorted_sections is None and not any([trending_even_money, second_even_money, third_even_money, trending_dozen, second_dozen, trending_column, second_column, number_highlights]): print("create_dynamic_table: No spins and no highlights, returning default message") return "

No spins yet. Select a strategy to see default highlights.

" print("create_dynamic_table: Rendering dynamic table HTML") html = render_dynamic_table_html(trending_even_money, second_even_money, third_even_money, trending_dozen, second_dozen, trending_column, second_column, number_highlights, top_color, middle_color, lower_color, suggestions, hot_numbers, scores=state.scores) print("create_dynamic_table: Table generated successfully") return html except Exception as e: print(f"create_dynamic_table: Error: {str(e)}") raise # Re-raise for debugging # Function to get strongest numbers with neighbors def get_strongest_numbers_with_neighbors(num_count): num_count = int(num_count) straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty: return "No numbers have hit yet." num_to_take = max(1, num_count // 3) top_numbers = straight_up_df["Number"].head(num_to_take).tolist() if not top_numbers: return "No strong numbers available to display." all_numbers = set() for num in top_numbers: neighbors = current_neighbors.get(num, (None, None)) left, right = neighbors all_numbers.add(num) if left is not None: all_numbers.add(left) if right is not None: all_numbers.add(right) sorted_numbers = sorted(list(all_numbers)) return f"Strongest {len(sorted_numbers)} Numbers (Sorted Lowest to Highest): {', '.join(map(str, sorted_numbers))}" # Function to analyze spins def analyze_spins(spins_input, strategy_name, neighbours_count, *checkbox_args): """Analyze the spins and return formatted results for all sections, always resetting scores.""" try: print(f"analyze_spins: Starting with spins_input='{spins_input}', strategy_name='{strategy_name}', neighbours_count={neighbours_count}, checkbox_args={checkbox_args}") # Handle empty spins case if not spins_input or not spins_input.strip(): print("analyze_spins: No spins input provided.") state.reset() # Always reset scores print("analyze_spins: Scores reset due to empty spins.") return ("Please enter at least one number (e.g., 5, 12, 0).", "", "", "", "", "", "", "", "", "", "", "", "", "", render_sides_of_zero_display()) raw_spins = [spin.strip() for spin in spins_input.split(",") if spin.strip()] spins = [] errors = [] for spin in raw_spins: try: num = int(spin) if not (0 <= num <= 36): errors.append(f"Error: '{spin}' is out of range. Use numbers between 0 and 36.") continue spins.append(str(num)) except ValueError: errors.append(f"Error: '{spin}' is not a valid number. Use whole numbers (e.g., 5, 12, 0).") continue if errors: error_msg = "\n".join(errors) print(f"analyze_spins: Errors found - {error_msg}") return (error_msg, "", "", "", "", "", "", "", "", "", "", "", "", "", render_sides_of_zero_display()) if not spins: print("analyze_spins: No valid spins found.") state.reset() # Always reset scores print("analyze_spins: Scores reset due to no valid spins.") return ("No valid numbers found. Please enter numbers like '5, 12, 0'.", "", "", "", "", "", "", "", "", "", "", "", "", "", render_sides_of_zero_display()) # Always reset scores state.reset() print("analyze_spins: Scores reset.") # Batch update scores for all spins print("analyze_spins: Updating scores batch") action_log = update_scores_batch(spins) print(f"analyze_spins: action_log={action_log}") # Update state.last_spins and spin_history state.last_spins = spins # Replace last_spins with current spins state.spin_history = action_log # Replace spin_history with current action_log # Limit spin history to 100 spins if len(state.spin_history) > 100: state.spin_history = state.spin_history[-100:] print(f"analyze_spins: Updated state.last_spins={state.last_spins}, spin_history length={len(state.spin_history)}") # Generate spin analysis output print("analyze_spins: Generating spin analysis output") spin_results = [] state.selected_numbers.clear() # Clear before rebuilding for idx, spin in enumerate(spins): spin_value = int(spin) hit_sections = [] action = action_log[idx] # Reconstruct hit sections from increments for name, increment in action["increments"].get("even_money_scores", {}).items(): if increment > 0: hit_sections.append(name) for name, increment in action["increments"].get("dozen_scores", {}).items(): if increment > 0: hit_sections.append(name) for name, increment in action["increments"].get("column_scores", {}).items(): if increment > 0: hit_sections.append(name) for name, increment in action["increments"].get("street_scores", {}).items(): if increment > 0: hit_sections.append(name) for name, increment in action["increments"].get("corner_scores", {}).items(): if increment > 0: hit_sections.append(name) for name, increment in action["increments"].get("six_line_scores", {}).items(): if increment > 0: hit_sections.append(name) for name, increment in action["increments"].get("split_scores", {}).items(): if increment > 0: hit_sections.append(name) if spin_value in action["increments"].get("scores", {}): hit_sections.append(f"Straight Up {spin}") for name, increment in action["increments"].get("side_scores", {}).items(): if increment > 0: hit_sections.append(name) # Add neighbor information if spin_value in current_neighbors: left, right = current_neighbors[spin_value] hit_sections.append(f"Left Neighbor: {left}") hit_sections.append(f"Right Neighbor: {right}") spin_results.append(f"Spin {spin} hits: {', '.join(hit_sections)}\nTotal sections hit: {len(hit_sections)}") state.selected_numbers = set(int(s) for s in state.last_spins if s.isdigit()) # Sync with last_spins spin_analysis_output = "\n".join(spin_results) print(f"analyze_spins: spin_analysis_output='{spin_analysis_output}'") even_money_output = "Even Money Bets:\n" + "\n".join(f"{name}: {score}" for name, score in state.even_money_scores.items()) print(f"analyze_spins: even_money_output='{even_money_output}'") dozens_output = "Dozens:\n" + "\n".join(f"{name}: {score}" for name, score in state.dozen_scores.items()) print(f"analyze_spins: dozens_output='{dozens_output}'") columns_output = "Columns:\n" + "\n".join(f"{name}: {score}" for name, score in state.column_scores.items()) print(f"analyze_spins: columns_output='{columns_output}'") streets_output = "Streets:\n" + "\n".join(f"{name}: {score}" for name, score in state.street_scores.items() if score > 0) print(f"analyze_spins: streets_output='{streets_output}'") corners_output = "Corners:\n" + "\n".join(f"{name}: {score}" for name, score in state.corner_scores.items() if score > 0) print(f"analyze_spins: corners_output='{corners_output}'") six_lines_output = "Double Streets:\n" + "\n".join(f"{name}: {score}" for name, score in state.six_line_scores.items() if score > 0) print(f"analyze_spins: six_lines_output='{six_lines_output}'") splits_output = "Splits:\n" if any(score > 0 for score in state.split_scores.values()) else "Splits: No hits yet.\n" splits_output += "\n".join(f"{name}: {score}" for name, score in state.split_scores.items() if score > 0) print(f"analyze_spins: splits_output='{splits_output}'") sides_output = "Sides of Zero:\n" + "\n".join(f"{name}: {score}" for name, score in state.side_scores.items()) print(f"analyze_spins: sides_output='{sides_output}'") print("analyze_spins: Creating straight_up_df") straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) straight_up_df["Left Neighbor"] = straight_up_df["Number"].apply(lambda x: current_neighbors[x][0] if x in current_neighbors else "") straight_up_df["Right Neighbor"] = straight_up_df["Number"].apply(lambda x: current_neighbors[x][1] if x in current_neighbors else "") straight_up_html = create_html_table(straight_up_df[["Number", "Left Neighbor", "Right Neighbor", "Score"]], "Strongest Numbers") print(f"analyze_spins: straight_up_html generated") print("analyze_spins: Creating top_18_df") top_18_df = straight_up_df.head(18).sort_values(by="Number", ascending=True) numbers = top_18_df["Number"].tolist() if len(numbers) < 18: numbers.extend([""] * (18 - len(numbers))) grid_data = [numbers[i::3] for i in range(3)] top_18_html = "

Top 18 Strongest Numbers (Sorted Lowest to Highest)

" top_18_html += '' for row in grid_data: top_18_html += "" for num in row: top_18_html += f'' top_18_html += "" top_18_html += "
{num}
" print(f"analyze_spins: top_18_html generated") print("analyze_spins: Getting strongest numbers") strongest_numbers_output = get_strongest_numbers_with_neighbors(3) print(f"analyze_spins: strongest_numbers_output='{strongest_numbers_output}'") print("analyze_spins: Generating dynamic_table_html") dynamic_table_html = create_dynamic_table(strategy_name, neighbours_count) print(f"analyze_spins: dynamic_table_html generated") print("analyze_spins: Generating strategy_output") strategy_output = show_strategy_recommendations(strategy_name, neighbours_count, *checkbox_args) print(f"analyze_spins: Strategy output = {strategy_output}") print("analyze_spins: Returning results") return (spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_html, strategy_output, render_sides_of_zero_display()) except Exception as e: print(f"analyze_spins: Unexpected error: {str(e)}") raise # Re-raise for debugging # Function to reset scores (no longer needed, but kept for compatibility) def reset_scores(): state.reset() return "Scores reset!" def undo_last_spin(current_spins_display, undo_count, strategy_name, neighbours_count, strong_numbers_count, *checkbox_args): if not state.spin_history: return ("No spins to undo.", "", "", "", "", "", "", "", "", "", "", current_spins_display, current_spins_display, "", create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count), "", create_color_code_table(), update_spin_counter(), render_sides_of_zero_display()) try: undo_count = int(undo_count) if undo_count <= 0: return ("Please select a positive number of spins to undo.", "", "", "", "", "", "", "", "", "", "", current_spins_display, current_spins_display, "", create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count), "", create_color_code_table(), update_spin_counter(), render_sides_of_zero_display()) undo_count = min(undo_count, len(state.spin_history)) # Don't exceed history length # Undo the specified number of spins undone_spins = [] for _ in range(undo_count): if not state.spin_history: break action = state.spin_history.pop() spin_value = action["spin"] undone_spins.append(str(spin_value)) # Decrement scores based on recorded increments for category, increments in action["increments"].items(): score_dict = getattr(state, category) for key, value in increments.items(): score_dict[key] -= value if score_dict[key] < 0: # Prevent negative scores score_dict[key] = 0 state.last_spins.pop() # Remove from last_spins too spins_input = ", ".join(state.last_spins) if state.last_spins else "" spin_analysis_output = f"Undo successful: Removed {undo_count} spin(s) - {', '.join(undone_spins)}" even_money_output = "Even Money Bets:\n" + "\n".join(f"{name}: {score}" for name, score in state.even_money_scores.items()) dozens_output = "Dozens:\n" + "\n".join(f"{name}: {score}" for name, score in state.dozen_scores.items()) columns_output = "Columns:\n" + "\n".join(f"{name}: {score}" for name, score in state.column_scores.items()) streets_output = "Streets:\n" + "\n".join(f"{name}: {score}" for name, score in state.street_scores.items() if score > 0) corners_output = "Corners:\n" + "\n".join(f"{name}: {score}" for name, score in state.corner_scores.items() if score > 0) six_lines_output = "Double Streets:\n" + "\n".join(f"{name}: {score}" for name, score in state.six_line_scores.items() if score > 0) splits_output = "Splits:\n" + "\n".join(f"{name}: {score}" for name, score in state.split_scores.items() if score > 0) sides_output = "Sides of Zero:\n" + "\n".join(f"{name}: {score}" for name, score in state.side_scores.items()) straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) straight_up_df["Left Neighbor"] = straight_up_df["Number"].apply(lambda x: current_neighbors[x][0] if x in current_neighbors else "") straight_up_df["Right Neighbor"] = straight_up_df["Number"].apply(lambda x: current_neighbors[x][1] if x in current_neighbors else "") straight_up_html = create_html_table(straight_up_df[["Number", "Left Neighbor", "Right Neighbor", "Score"]], "Strongest Numbers") top_18_df = straight_up_df.head(18).sort_values(by="Number", ascending=True) numbers = top_18_df["Number"].tolist() if len(numbers) < 18: numbers.extend([""] * (18 - len(numbers))) grid_data = [numbers[i::3] for i in range(3)] top_18_html = "

Top 18 Strongest Numbers (Sorted Lowest to Highest)

" top_18_html += '' for row in grid_data: top_18_html += "" for num in row: top_18_html += f'' top_18_html += "" top_18_html += "
{num}
" strongest_numbers_output = get_strongest_numbers_with_neighbors(3) dynamic_table_html = create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count) print(f"undo_last_spin: Generating strategy recommendations for {strategy_name}") strategy_output = show_strategy_recommendations(strategy_name, neighbours_count, strong_numbers_count, *checkbox_args) return (spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, spins_input, spins_input, dynamic_table_html, strategy_output, create_color_code_table(), update_spin_counter(), render_sides_of_zero_display()) except ValueError: return ("Error: Invalid undo count. Please use a positive number.", "", "", "", "", "", "", "", "", "", "", current_spins_display, current_spins_display, "", create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count), "", create_color_code_table(), update_spin_counter(), render_sides_of_zero_display()) except Exception as e: print(f"undo_last_spin: Unexpected error: {str(e)}") return (f"Unexpected error during undo: {str(e)}", "", "", "", "", "", "", "", "", "", "", current_spins_display, current_spins_display, "", create_dynamic_table(strategy_name, neighbours_count, strong_numbers_count), "", create_color_code_table(), update_spin_counter(), render_sides_of_zero_display()) def clear_all(): state.selected_numbers.clear() state.last_spins = [] state.reset() return "", "", "All spins and scores cleared successfully!", "

Last Spins

No spins yet.

", "", "", "", "", "", "", "", "", "", "", "", update_spin_counter(), render_sides_of_zero_display() def reset_strategy_dropdowns(): default_category = "Even Money Strategies" default_strategy = "Best Even Money Bets" strategy_choices = strategy_categories[default_category] return default_category, default_strategy, strategy_choices def generate_random_spins(num_spins, current_spins_display, last_spin_count): try: num_spins = int(num_spins) if num_spins <= 0: return current_spins_display, current_spins_display, "Please select a number of spins greater than 0.", update_spin_counter(), render_sides_of_zero_display() new_spins = [str(random.randint(0, 36)) for _ in range(num_spins)] # Update scores for the new spins update_scores_batch(new_spins) if current_spins_display and current_spins_display.strip(): current_spins = current_spins_display.split(", ") updated_spins = current_spins + new_spins else: updated_spins = new_spins # Update state.last_spins state.last_spins = updated_spins # Replace the list entirely spins_text = ", ".join(updated_spins) print(f"generate_random_spins: Setting spins_textbox to '{spins_text}'") return spins_text, spins_text, f"Generated {num_spins} random spins: {', '.join(new_spins)}", update_spin_counter(), render_sides_of_zero_display() except ValueError: print("generate_random_spins: Invalid number of spins entered.") return current_spins_display, current_spins_display, "Please enter a valid number of spins.", update_spin_counter(), render_sides_of_zero_display() except Exception as e: print(f"generate_random_spins: Unexpected error: {str(e)}") return current_spins_display, current_spins_display, f"Error generating spins: {str(e)}", update_spin_counter(), render_sides_of_zero_display() # Strategy functions def best_even_money_bets(): recommendations = [] sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True) even_money_hits = [item for item in sorted_even_money if item[1] > 0] if not even_money_hits: recommendations.append("Best Even Money Bets: No hits yet.") return "\n".join(recommendations) # Collect the top 3 bets, including ties top_bets = [] scores_seen = set() for name, score in sorted_even_money: if len(top_bets) < 3 or score in scores_seen: top_bets.append((name, score)) scores_seen.add(score) else: break # Display the top 3 bets recommendations.append("Best Even Money Bets (Top 3):") for i, (name, score) in enumerate(top_bets[:3], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 3 positions if len(top_bets) > 1: # Check for ties at the 1st position first_score = top_bets[0][1] tied_first = [name for name, score in top_bets if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") # Check for ties at the 2nd position if len(top_bets) > 1: second_score = top_bets[1][1] tied_second = [name for name, score in top_bets if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") # Check for ties at the 3rd position if len(top_bets) > 2: third_score = top_bets[2][1] tied_third = [name for name, score in top_bets if score == third_score] if len(tied_third) > 1: recommendations.append(f"Note: Tie for 3rd place among {', '.join(tied_third)} with score {third_score}") return "\n".join(recommendations) def hot_bet_strategy(): recommendations = [] sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True) even_money_hits = [item for item in sorted_even_money if item[1] > 0] if even_money_hits: recommendations.append("Even Money (Top 2):") for i, (name, score) in enumerate(even_money_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("Even Money: No hits yet.") sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] if dozens_hits: recommendations.append("\nDozens (Top 2):") for i, (name, score) in enumerate(dozens_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nDozens: No hits yet.") sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] if columns_hits: recommendations.append("\nColumns (Top 2):") for i, (name, score) in enumerate(columns_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nColumns: No hits yet.") sorted_streets = sorted(state.street_scores.items(), key=lambda x: x[1], reverse=True) streets_hits = [item for item in sorted_streets if item[1] > 0] if streets_hits: recommendations.append("\nStreets (Ranked):") for i, (name, score) in enumerate(streets_hits, 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nStreets: No hits yet.") sorted_corners = sorted(state.corner_scores.items(), key=lambda x: x[1], reverse=True) corners_hits = [item for item in sorted_corners if item[1] > 0] if corners_hits: recommendations.append("\nCorners (Ranked):") for i, (name, score) in enumerate(corners_hits, 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nCorners: No hits yet.") sorted_six_lines = sorted(state.six_line_scores.items(), key=lambda x: x[1], reverse=True) six_lines_hits = [item for item in sorted_six_lines if item[1] > 0] if six_lines_hits: recommendations.append("\nDouble Streets (Ranked):") for i, (name, score) in enumerate(six_lines_hits, 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nDouble Streets: No hits yet.") sorted_splits = sorted(state.split_scores.items(), key=lambda x: x[1], reverse=True) splits_hits = [item for item in sorted_splits if item[1] > 0] if splits_hits: recommendations.append("\nSplits (Ranked):") for i, (name, score) in enumerate(splits_hits, 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nSplits: No hits yet.") sorted_sides = sorted(state.side_scores.items(), key=lambda x: x[1], reverse=True) sides_hits = [item for item in sorted_sides if item[1] > 0] if sides_hits: recommendations.append("\nSides of Zero:") recommendations.append(f"1. {sides_hits[0][0]}: {sides_hits[0][1]}") else: recommendations.append("\nSides of Zero: No hits yet.") sorted_numbers = sorted(state.scores.items(), key=lambda x: x[1], reverse=True) numbers_hits = [item for item in sorted_numbers if item[1] > 0] if numbers_hits: number_best = numbers_hits[0] left_neighbor, right_neighbor = current_neighbors[number_best[0]] recommendations.append(f"\nStrongest Number: {number_best[0]} (Score: {number_best[1]}) with neighbors {left_neighbor} and {right_neighbor}") else: recommendations.append("\nStrongest Number: No hits yet.") return "\n".join(recommendations) # Function for Cold Bet Strategy def cold_bet_strategy(): recommendations = [] sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1]) even_money_non_hits = [item for item in sorted_even_money if item[1] == 0] even_money_hits = [item for item in sorted_even_money if item[1] > 0] if even_money_non_hits: recommendations.append("Even Money (Not Hit):") recommendationsече.append(", ".join(item[0] for item in even_money_non_hits)) if even_money_hits: recommendations.append("\nEven Money (Lowest Scores):") for i, (name, score) in enumerate(even_money_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1]) dozens_non_hits = [item for item in sorted_dozens if item[1] == 0] dozens_hits = [item for item in sorted_dozens if item[1] > 0] if dozens_non_hits: recommendations.append("\nDozens (Not Hit):") recommendations.append(", ".join(item[0] for item in dozens_non_hits)) if dozens_hits: recommendations.append("\nDozens (Lowest Scores):") for i, (name, score) in enumerate(dozens_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1]) columns_non_hits = [item for item in sorted_columns if item[1] == 0] columns_hits = [item for item in sorted_columns if item[1] > 0] if columns_non_hits: recommendations.append("\nColumns (Not Hit):") recommendations.append(", ".join(item[0] for item in columns_non_hits)) if columns_hits: recommendations.append("\nColumns (Lowest Scores):") for i, (name, score) in enumerate(columns_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") sorted_streets = sorted(state.street_scores.items(), key=lambda x: x[1]) streets_non_hits = [item for item in sorted_streets if item[1] == 0] streets_hits = [item for item in sorted_streets if item[1] > 0] if streets_non_hits: recommendations.append("\nStreets (Not Hit):") recommendations.append(", ".join(item[0] for item in streets_non_hits)) if streets_hits: recommendations.append("\nStreets (Lowest Scores):") for i, (name, score) in enumerate(streets_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") sorted_corners = sorted(state.corner_scores.items(), key=lambda x: x[1]) corners_non_hits = [item for item in sorted_corners if item[1] == 0] corners_hits = [item for item in sorted_corners if item[1] > 0] if corners_non_hits: recommendations.append("\nCorners (Not Hit):") recommendations.append(", ".join(item[0] for item in corners_non_hits)) if corners_hits: recommendations.append("\nCorners (Lowest Scores):") for i, (name, score) in enumerate(corners_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") sorted_six_lines = sorted(state.six_line_scores.items(), key=lambda x: x[1]) six_lines_non_hits = [item for item in sorted_six_lines if item[1] == 0] six_lines_hits = [item for item in sorted_six_lines if item[1] > 0] if six_lines_non_hits: recommendations.append("\nDouble Streets (Not Hit):") recommendations.append(", ".join(item[0] for item in six_lines_non_hits)) if six_lines_hits: recommendations.append("\nDouble Streets (Lowest Scores):") for i, (name, score) in enumerate(six_lines_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") sorted_splits = sorted(state.split_scores.items(), key=lambda x: x[1]) splits_non_hits = [item for item in sorted_splits if item[1] == 0] splits_hits = [item for item in sorted_splits if item[1] > 0] if splits_non_hits: recommendations.append("\nSplits (Not Hit):") recommendations.append(", ".join(item[0] for item in splits_non_hits)) if splits_hits: recommendations.append("\nSplits (Lowest Scores):") for i, (name, score) in enumerate(splits_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") sorted_sides = sorted(state.side_scores.items(), key=lambda x: x[1]) sides_non_hits = [item for item in sorted_sides if item[1] == 0] sides_hits = [item for item in sorted_sides if item[1] > 0] if sides_non_hits: recommendations.append("\nSides of Zero (Not Hit):") recommendations.append(", ".join(item[0] for item in sides_non_hits)) if sides_hits: recommendations.append("\nSides of Zero (Lowest Score):") recommendations.append(f"1. {sides_hits[0][0]}: {sides_hits[0][1]}") sorted_numbers = sorted(state.scores.items(), key=lambda x: x[1]) numbers_non_hits = [item for item in sorted_numbers if item[1] == 0] numbers_hits = [item for item in sorted_numbers if item[1] > 0] if numbers_non_hits: recommendations.append("\nNumbers (Not Hit):") recommendations.append(", ".join(str(item[0]) for item in numbers_non_hits)) if numbers_hits: number_worst = numbers_hits[0] left_neighbor, right_neighbor = current_neighbors[number_worst[0]] recommendations.append(f"\nColdest Number: {number_worst[0]} (Score: {number_worst[1]}) with neighbors {left_neighbor} and {right_neighbor}") return "\n".join(recommendations) def best_dozens(): recommendations = [] sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] if dozens_hits: recommendations.append("Best Dozens (Top 2):") for i, (name, score) in enumerate(dozens_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("Best Dozens: No hits yet.") return "\n".join(recommendations) def best_columns(): recommendations = [] sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] if columns_hits: recommendations.append("Best Columns (Top 2):") for i, (name, score) in enumerate(columns_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("Best Columns: No hits yet.") return "\n".join(recommendations) def fibonacci_strategy(): recommendations = [] sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] if not dozens_hits and not columns_hits: recommendations.append("Fibonacci Strategy: No hits in Dozens or Columns yet.") return "\n".join(recommendations) best_dozen_score = dozens_hits[0][1] if dozens_hits else 0 best_column_score = columns_hits[0][1] if columns_hits else 0 if best_dozen_score > best_column_score: # Dozens wins: show top two dozens recommendations.append("Best Category: Dozens") top_dozens = [] scores_seen = set() for name, score in sorted_dozens: if len(top_dozens) < 2 or score in scores_seen: top_dozens.append((name, score)) scores_seen.add(score) else: break for i, (name, score) in enumerate(top_dozens[:2], 1): recommendations.append(f"Best Dozen {i}: {name} (Score: {score})") # Check for ties among the top two if len(top_dozens) > 1 and top_dozens[0][1] == top_dozens[1][1]: tied_dozens = [name for name, score in top_dozens if score == top_dozens[0][1]] recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_dozens)} with score {top_dozens[0][1]}") elif best_column_score > best_dozen_score: # Columns wins: show top two columns recommendations.append("Best Category: Columns") top_columns = [] scores_seen = set() for name, score in sorted_columns: if len(top_columns) < 2 or score in scores_seen: top_columns.append((name, score)) scores_seen.add(score) else: break for i, (name, score) in enumerate(top_columns[:2], 1): recommendations.append(f"Best Column {i}: {name} (Score: {score})") # Check for ties among the top two if len(top_columns) > 1 and top_columns[0][1] == top_columns[1][1]: tied_columns = [name for name, score in top_columns if score == top_columns[0][1]] recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_columns)} with score {top_columns[0][1]}") else: # Tie between Dozens and Columns: show both top options recommendations.append(f"Best Category (Tied): Dozens and Columns (Score: {best_dozen_score})") if dozens_hits: top_dozens = [] scores_seen = set() for name, score in sorted_dozens: if len(top_dozens) < 2 or score in scores_seen: top_dozens.append((name, score)) scores_seen.add(score) else: break for i, (name, score) in enumerate(top_dozens[:2], 1): recommendations.append(f"Best Dozen {i}: {name} (Score: {score})") if len(top_dozens) > 1 and top_dozens[0][1] == top_dozens[1][1]: tied_dozens = [name for name, score in top_dozens if score == top_dozens[0][1]] recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_dozens)} with score {top_dozens[0][1]}") if columns_hits: top_columns = [] scores_seen = set() for name, score in sorted_columns: if len(top_columns) < 2 or score in scores_seen: top_columns.append((name, score)) scores_seen.add(score) else: break for i, (name, score) in enumerate(top_columns[:2], 1): recommendations.append(f"Best Column {i}: {name} (Score: {score})") if len(top_columns) > 1 and top_columns[0][1] == top_columns[1][1]: tied_columns = [name for name, score in top_columns if score == top_columns[0][1]] recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_columns)} with score {top_columns[0][1]}") return "\n".join(recommendations) def best_streets(): recommendations = [] sorted_streets = sorted(state.street_scores.items(), key=lambda x: x[1], reverse=True) streets_hits = [item for item in sorted_streets if item[1] > 0] if not streets_hits: recommendations.append("Best Streets: No hits yet.") return "\n".join(recommendations) recommendations.append("Top 3 Streets:") for i, (name, score) in enumerate(streets_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") recommendations.append("\nTop 6 Streets:") for i, (name, score) in enumerate(streets_hits[:6], 1): recommendations.append(f"{i}. {name}: {score}") return "\n".join(recommendations) def best_double_streets(): recommendations = [] sorted_six_lines = sorted(state.six_line_scores.items(), key=lambda x: x[1], reverse=True) six_lines_hits = [item for item in sorted_six_lines if item[1] > 0] if not six_lines_hits: recommendations.append("Best Double Streets: No hits yet.") return "\n".join(recommendations) recommendations.append("Double Streets (Ranked):") for i, (name, score) in enumerate(six_lines_hits, 1): recommendations.append(f"{i}. {name}: {score}") return "\n".join(recommendations) def best_corners(): recommendations = [] sorted_corners = sorted(state.corner_scores.items(), key=lambda x: x[1], reverse=True) corners_hits = [item for item in sorted_corners if item[1] > 0] if not corners_hits: recommendations.append("Best Corners: No hits yet.") return "\n".join(recommendations) recommendations.append("Corners (Ranked):") for i, (name, score) in enumerate(corners_hits, 1): recommendations.append(f"{i}. {name}: {score}") return "\n".join(recommendations) def best_splits(): recommendations = [] sorted_splits = sorted(state.split_scores.items(), key=lambda x: x[1], reverse=True) splits_hits = [item for item in sorted_splits if item[1] > 0] if not splits_hits: recommendations.append("Best Splits: No hits yet.") return "\n".join(recommendations) recommendations.append("Splits (Ranked):") for i, (name, score) in enumerate(splits_hits, 1): recommendations.append(f"{i}. {name}: {score}") return "\n".join(recommendations) def best_dozens_and_streets(): recommendations = [] sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] if dozens_hits: recommendations.append("Best Dozens (Top 2):") for i, (name, score) in enumerate(dozens_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("Best Dozens: No hits yet.") sorted_streets = sorted(state.street_scores.items(), key=lambda x: x[1], reverse=True) streets_hits = [item for item in sorted_streets if item[1] > 0] if streets_hits: recommendations.append("\nTop 3 Streets (Yellow):") for i, (name, score) in enumerate(streets_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") recommendations.append("\nMiddle 3 Streets (Cyan):") for i, (name, score) in enumerate(streets_hits[3:6], 1): recommendations.append(f"{i}. {name}: {score}") recommendations.append("\nBottom 3 Streets (Green):") for i, (name, score) in enumerate(streets_hits[6:9], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nBest Streets: No hits yet.") return "\n".join(recommendations) def best_columns_and_streets(): recommendations = [] sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] if columns_hits: recommendations.append("Best Columns (Top 2):") for i, (name, score) in enumerate(columns_hits[:2], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("Best Columns: No hits yet.") sorted_streets = sorted(state.street_scores.items(), key=lambda x: x[1], reverse=True) streets_hits = [item for item in sorted_streets if item[1] > 0] if streets_hits: recommendations.append("\nTop 3 Streets (Yellow):") for i, (name, score) in enumerate(streets_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") recommendations.append("\nMiddle 3 Streets (Cyan):") for i, (name, score) in enumerate(streets_hits[3:6], 1): recommendations.append(f"{i}. {name}: {score}") recommendations.append("\nBottom 3 Streets (Green):") for i, (name, score) in enumerate(streets_hits[6:9], 1): recommendations.append(f"{i}. {name}: {score}") else: recommendations.append("\nBest Streets: No hits yet.") return "\n".join(recommendations) def non_overlapping_double_street_strategy(): non_overlapping_sets = [ ["1ST D.STREET – 1, 4", "3RD D.STREET – 7, 10", "5TH D.STREET – 13, 16", "7TH D.STREET – 19, 22", "9TH D.STREET – 25, 28"], ["2ND D.STREET – 4, 7", "4TH D.STREET – 10, 13", "6TH D.STREET – 16, 19", "8TH D.STREET – 22, 25", "10TH D.STREET – 28, 31"] ] set_scores = [] for idx, non_overlapping_set in enumerate(non_overlapping_sets): total_score = sum(state.six_line_scores[name] for name in non_overlapping_set) set_scores.append((idx, total_score, non_overlapping_set)) best_set = max(set_scores, key=lambda x: x[1]) best_set_idx, best_set_score, best_set_streets = best_set sorted_streets = sorted(best_set_streets, key=lambda name: state.six_line_scores[name], reverse=True) recommendations = [] recommendations.append(f"Non-Overlapping Double Streets Strategy (Set {best_set_idx + 1} with Total Score: {best_set_score})") recommendations.append("Hottest Non-Overlapping Double Streets (Sorted by Hotness):") for i, name in enumerate(sorted_streets, 1): score = state.six_line_scores[name] recommendations.append(f"{i}. {name}: {score}") return "\n".join(recommendations) def non_overlapping_corner_strategy(): non_overlapping_sets = [ ["1ST CORNER – 1, 2, 4, 5", "5TH CORNER – 7, 8, 10, 11", "9TH CORNER – 13, 14, 16, 17", "13TH CORNER – 19, 20, 22, 23", "17TH CORNER – 25, 26, 28, 29", "21ST CORNER – 31, 32, 34, 35"], ["2ND CORNER – 2, 3, 5, 6", "6TH CORNER – 8, 9, 11, 12", "10TH CORNER – 14, 15, 17, 18", "14TH CORNER – 20, 21, 23, 24", "18TH CORNER – 26, 27, 29, 30", "22ND CORNER – 32, 33, 35, 36"] ] set_scores = [] for idx, non_overlapping_set in enumerate(non_overlapping_sets): total_score = sum(state.corner_scores[name] for name in non_overlapping_set) set_scores.append((idx, total_score, non_overlapping_set)) best_set = max(set_scores, key=lambda x: x[1]) best_set_idx, best_set_score, best_set_corners = best_set sorted_corners = sorted(best_set_corners, key=lambda name: state.corner_scores[name], reverse=True) recommendations = [] recommendations.append(f"Non-Overlapping Corner Strategy (Set {best_set_idx + 1} with Total Score: {best_set_score})") recommendations.append("Hottest Non-Overlapping Corners (Sorted by Hotness):") for i, name in enumerate(sorted_corners, 1): score = state.corner_scores[name] recommendations.append(f"{i}. {name}: {score}") return "\n".join(recommendations) def romanowksy_missing_dozen_strategy(): recommendations = [] sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] dozens_no_hits = [item for item in sorted_dozens if item[1] == 0] if not dozens_hits and not dozens_no_hits: recommendations.append("Romanowksy Missing Dozen Strategy: No spins recorded yet.") return "\n".join(recommendations) if len(dozens_hits) < 2: recommendations.append("Romanowksy Missing Dozen Strategy: Not enough dozens have hit yet.") if dozens_hits: recommendations.append(f"Hottest Dozen: {dozens_hits[0][0]} (Score: {dozens_hits[0][1]})") return "\n".join(recommendations) top_dozens = [] scores_seen = set() for name, score in sorted_dozens: if len(top_dozens) < 2 or score in scores_seen: top_dozens.append((name, score)) scores_seen.add(score) else: break recommendations.append("Hottest Dozens (Top 2):") for i, (name, score) in enumerate(top_dozens[:2], 1): recommendations.append(f"{i}. {name}: {score}") if len(top_dozens) > 2 and top_dozens[1][1] == top_dozens[2][1]: tied_dozens = [name for name, score in top_dozens if score == top_dozens[1][1]] recommendations.append(f"Note: Tie detected among {', '.join(tied_dozens)} with score {top_dozens[1][1]}") weakest_dozen = sorted_dozens[-1] weakest_dozen_name, weakest_dozen_score = weakest_dozen recommendations.append(f"\nWeakest Dozen: {weakest_dozen_name} (Score: {weakest_dozen_score})") weakest_dozen_numbers = set(DOZENS[weakest_dozen_name]) straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty: recommendations.append("No strong numbers have hit yet in any dozen.") return "\n".join(recommendations) strong_numbers_in_weakest = [] neighbors_in_weakest = [] for _, row in straight_up_df.iterrows(): number = row["Number"] score = row["Score"] if number in weakest_dozen_numbers: strong_numbers_in_weakest.append((number, score)) else: if number in current_neighbors: left, right = current_neighbors[number] if left in weakest_dozen_numbers: neighbors_in_weakest.append((left, number, score)) if right in weakest_dozen_numbers: neighbors_in_weakest.append((right, number, score)) if strong_numbers_in_weakest: recommendations.append("\nStrongest Numbers in Weakest Dozen:") for number, score in strong_numbers_in_weakest: recommendations.append(f"Number {number} (Score: {score})") else: recommendations.append("\nNo strong numbers directly in the Weakest Dozen.") if neighbors_in_weakest: recommendations.append("\nNeighbors of Strong Numbers in Weakest Dozen:") for neighbor, strong_number, score in neighbors_in_weakest: recommendations.append(f"Number {neighbor} (Neighbor of {strong_number}, Score: {score})") else: if not strong_numbers_in_weakest: recommendations.append("No neighbors of strong numbers in the Weakest Dozen.") return "\n".join(recommendations) def fibonacci_to_fortune_strategy(): recommendations = [] # Debug: Print scores to verify state print(f"fibonacci_to_fortune_strategy: Dozen scores = {dict(state.dozen_scores)}") print(f"fibonacci_to_fortune_strategy: Column scores = {dict(state.column_scores)}") print(f"fibonacci_to_fortune_strategy: Even money scores = {dict(state.even_money_scores)}") # Part 1: Fibonacci Strategy (Best Category: Dozens or Columns) sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] best_dozen_score = dozens_hits[0][1] if dozens_hits else 0 best_column_score = columns_hits[0][1] if columns_hits else 0 recommendations.append("Fibonacci Strategy:") if not dozens_hits and not columns_hits: recommendations.append("No hits in Dozens or Columns yet.") elif best_dozen_score > best_column_score: recommendations.append(f"Best Category: Dozens (Score: {best_dozen_score})") recommendations.append(f"Best Dozen: {dozens_hits[0][0]}") elif best_column_score > best_dozen_score: recommendations.append(f"Best Category: Columns (Score: {best_column_score})") recommendations.append(f"Best Column: {columns_hits[0][0]}") else: recommendations.append(f"Best Category (Tied): Dozens and Columns (Score: {best_dozen_score})") if dozens_hits: recommendations.append(f"Best Dozen: {dozens_hits[0][0]}") if columns_hits: recommendations.append(f"Best Column: {columns_hits[0][0]}") # Part 2: Dozens (Top 2) recommendations.append("\nDozens (Top 2):") print(f"fibonacci_to_fortune_strategy: Sorted dozens = {sorted_dozens}") if len(sorted_dozens) >= 2: for i, (name, score) in enumerate(sorted_dozens[:2], 1): recommendations.append(f"{i}. {name}: {score}") elif sorted_dozens: name, score = sorted_dozens[0] recommendations.append(f"1. {name}: {score}") recommendations.append("2. No other dozens available.") else: recommendations.append("No hits yet.") # Part 3: Columns (Top 2) recommendations.append("\nColumns (Top 2):") print(f"fibonacci_to_fortune_strategy: Sorted columns = {sorted_columns}") if len(sorted_columns) >= 2: for i, (name, score) in enumerate(sorted_columns[:2], 1): recommendations.append(f"{i}. {name}: {score}") elif sorted_columns: name, score = sorted_columns[0] recommendations.append(f"1. {name}: {score}") recommendations.append("2. No other columns available.") else: recommendations.append("No hits yet.") # Part 4: Best Even Money Bet sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True) print(f"fibonacci_to_fortune_strategy: Sorted even money = {sorted_even_money}") even_money_hits = [item for item in sorted_even_money if item[1] > 0] recommendations.append("\nEven Money (Top 1):") if even_money_hits: best_even_money = even_money_hits[0] name, score = best_even_money recommendations.append(f"1. {name}: {score}") else: recommendations.append("No hits yet.") # Part 5: Best Double Street in Weakest Dozen (Excluding Top Two Dozens) weakest_dozen = min(state.dozen_scores.items(), key=lambda x: x[1], default=("1st Dozen", 0)) weakest_dozen_name, weakest_dozen_score = weakest_dozen weakest_dozen_numbers = set(DOZENS[weakest_dozen_name]) top_two_dozens = [item[0] for item in sorted_dozens[:2]] top_two_dozen_numbers = set() for dozen_name in top_two_dozens: top_two_dozen_numbers.update(DOZENS[dozen_name]) double_streets_in_weakest = [] for name, numbers in SIX_LINES.items(): numbers_set = set(numbers) if numbers_set.issubset(weakest_dozen_numbers) and not numbers_set.intersection(top_two_dozen_numbers): score = state.six_line_scores.get(name, 0) double_streets_in_weakest.append((name, score)) print(f"fibonacci_to_fortune_strategy: Double streets in weakest dozen ({weakest_dozen_name}) = {double_streets_in_weakest}") recommendations.append(f"\nDouble Streets (Top 1 in Weakest Dozen: {weakest_dozen_name}, Score: {weakest_dozen_score}):") if double_streets_in_weakest: double_streets_sorted = sorted(double_streets_in_weakest, key=lambda x: x[1], reverse=True) best_double_street = double_streets_sorted[0] name, score = best_double_street numbers = ', '.join(map(str, sorted(SIX_LINES[name]))) recommendations.append(f"1. {name} (Numbers: {numbers}, Score: {score})") else: recommendations.append("No suitable double street available (all overlap with top two dozens or no hits).") return "\n".join(recommendations) def three_eight_six_rising_martingale(): recommendations = [] sorted_streets = sorted(state.street_scores.items(), key=lambda x: x[1], reverse=True) streets_hits = [item for item in sorted_streets if item[1] > 0] if not streets_hits: recommendations.append("3-8-6 Rising Martingale: No streets have hit yet.") return "\n".join(recommendations) recommendations.append("Top 3 Streets (Yellow):") for i, (name, score) in enumerate(streets_hits[:3], 1): recommendations.append(f"{i}. {name}: {score}") recommendations.append("\nMiddle 3 Streets (Cyan):") for i, (name, score) in enumerate(streets_hits[3:6], 1): recommendations.append(f"{i}. {name}: {score}") recommendations.append("\nBottom 2 Streets (Green):") for i, (name, score) in enumerate(streets_hits[6:8], 1): recommendations.append(f"{i}. {name}: {score}") return "\n".join(recommendations) def one_dozen_one_column_strategy(): recommendations = [] sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] if not dozens_hits: recommendations.append("Best Dozen: No dozens have hit yet.") else: top_score = dozens_hits[0][1] top_dozens = [item for item in sorted_dozens if item[1] == top_score] if len(top_dozens) == 1: recommendations.append(f"Best Dozen: {top_dozens[0][0]}") else: recommendations.append("Best Dozens (Tied):") for name, _ in top_dozens: recommendations.append(f"- {name}") sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] if not columns_hits: recommendations.append("Best Column: No columns have hit yet.") else: top_score = columns_hits[0][1] top_columns = [item for item in sorted_columns if item[1] == top_score] if len(top_columns) == 1: recommendations.append(f"Best Column: {top_columns[0][0]}") else: recommendations.append("Best Columns (Tied):") for name, _ in top_columns: recommendations.append(f"- {name}") return "\n".join(recommendations) def top_pick_18_numbers_without_neighbours(): recommendations = [] straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty or len(straight_up_df) < 18: recommendations.append("Top Pick 18 Numbers without Neighbours: Not enough numbers have hit yet (need at least 18).") return "\n".join(recommendations) top_18_df = straight_up_df.head(18) top_18_numbers = top_18_df["Number"].tolist() top_6 = top_18_numbers[:6] next_6 = top_18_numbers[6:12] last_6 = top_18_numbers[12:18] recommendations.append("Top Pick 18 Numbers without Neighbours:") recommendations.append("\nTop 6 Numbers (Yellow):") for i, num in enumerate(top_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nNext 6 Numbers (Blue):") for i, num in enumerate(next_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nLast 6 Numbers (Green):") for i, num in enumerate(last_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") return "\n".join(recommendations) def best_even_money_and_top_18(): recommendations = [] # Best Even Money Bets (Top 3 with tie handling, same as best_even_money_bets) sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True) even_money_hits = [item for item in sorted_even_money if item[1] > 0] if even_money_hits: # Collect the top 3 bets, including ties top_bets = [] scores_seen = set() for name, score in sorted_even_money: if len(top_bets) < 3 or score in scores_seen: top_bets.append((name, score)) scores_seen.add(score) else: break # Display the top 3 bets recommendations.append("Best Even Money Bets (Top 3):") for i, (name, score) in enumerate(top_bets[:3], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 3 positions if len(top_bets) > 1: first_score = top_bets[0][1] tied_first = [name for name, score in top_bets if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") if len(top_bets) > 1: second_score = top_bets[1][1] tied_second = [name for name, score in top_bets if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") if len(top_bets) > 2: third_score = top_bets[2][1] tied_third = [name for name, score in top_bets if score == third_score] if len(tied_third) > 1: recommendations.append(f"Note: Tie for 3rd place among {', '.join(tied_third)} with score {third_score}") else: recommendations.append("Best Even Money Bets: No hits yet.") # Top Pick 18 Numbers without Neighbours (same as top_pick_18_numbers_without_neighbours) recommendations.append("") # Add a blank line for separation straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty or len(straight_up_df) < 18: recommendations.append("Top Pick 18 Numbers without Neighbours: Not enough numbers have hit yet (need at least 18).") return "\n".join(recommendations) top_18_df = straight_up_df.head(18) top_18_numbers = top_18_df["Number"].tolist() top_6 = top_18_numbers[:6] next_6 = top_18_numbers[6:12] last_6 = top_18_numbers[12:18] recommendations.append("Top Pick 18 Numbers without Neighbours:") recommendations.append("\nTop 6 Numbers (Yellow):") for i, num in enumerate(top_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nNext 6 Numbers (Blue):") for i, num in enumerate(next_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nLast 6 Numbers (Green):") for i, num in enumerate(last_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") return "\n".join(recommendations) def best_dozens_and_top_18(): recommendations = [] # Best Dozens (Top 2 with tie handling, same as best_dozens) sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] if dozens_hits: # Collect the top 2 dozens, including ties top_dozens = [] scores_seen = set() for name, score in sorted_dozens: if len(top_dozens) < 2 or score in scores_seen: top_dozens.append((name, score)) scores_seen.add(score) else: break # Display the top 2 dozens recommendations.append("Best Dozens (Top 2):") for i, (name, score) in enumerate(top_dozens[:2], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 2 positions if len(top_dozens) > 1: first_score = top_dozens[0][1] tied_first = [name for name, score in top_dozens if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") if len(top_dozens) > 1: second_score = top_dozens[1][1] tied_second = [name for name, score in top_dozens if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") else: recommendations.append("Best Dozens: No hits yet.") # Top Pick 18 Numbers without Neighbours (same as top_pick_18_numbers_without_neighbours) recommendations.append("") # Add a blank line for separation straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty or len(straight_up_df) < 18: recommendations.append("Top Pick 18 Numbers without Neighbours: Not enough numbers have hit yet (need at least 18).") return "\n".join(recommendations) top_18_df = straight_up_df.head(18) top_18_numbers = top_18_df["Number"].tolist() top_6 = top_18_numbers[:6] next_6 = top_18_numbers[6:12] last_6 = top_18_numbers[12:18] recommendations.append("Top Pick 18 Numbers without Neighbours:") recommendations.append("\nTop 6 Numbers (Yellow):") for i, num in enumerate(top_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nNext 6 Numbers (Blue):") for i, num in enumerate(next_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nLast 6 Numbers (Green):") for i, num in enumerate(last_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") return "\n".join(recommendations) def best_columns_and_top_18(): recommendations = [] # Best Columns (Top 2 with tie handling, same as best_columns) sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] if columns_hits: # Collect the top 2 columns, including ties top_columns = [] scores_seen = set() for name, score in sorted_columns: if len(top_columns) < 2 or score in scores_seen: top_columns.append((name, score)) scores_seen.add(score) else: break # Display the top 2 columns recommendations.append("Best Columns (Top 2):") for i, (name, score) in enumerate(top_columns[:2], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 2 positions if len(top_columns) > 1: first_score = top_columns[0][1] tied_first = [name for name, score in top_columns if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") if len(top_columns) > 1: second_score = top_columns[1][1] tied_second = [name for name, score in top_columns if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") else: recommendations.append("Best Columns: No hits yet.") # Top Pick 18 Numbers without Neighbours (same as top_pick_18_numbers_without_neighbours) recommendations.append("") # Add a blank line for separation straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty or len(straight_up_df) < 18: recommendations.append("Top Pick 18 Numbers without Neighbours: Not enough numbers have hit yet (need at least 18).") return "\n".join(recommendations) top_18_df = straight_up_df.head(18) top_18_numbers = top_18_df["Number"].tolist() top_6 = top_18_numbers[:6] next_6 = top_18_numbers[6:12] last_6 = top_18_numbers[12:18] recommendations.append("Top Pick 18 Numbers without Neighbours:") recommendations.append("\nTop 6 Numbers (Yellow):") for i, num in enumerate(top_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nNext 6 Numbers (Blue):") for i, num in enumerate(next_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nLast 6 Numbers (Green):") for i, num in enumerate(last_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") return "\n".join(recommendations) def best_dozens_even_money_and_top_18(): recommendations = [] # Best Dozens (Top 2 with tie handling, same as best_dozens) sorted_dozens = sorted(state.dozen_scores.items(), key=lambda x: x[1], reverse=True) dozens_hits = [item for item in sorted_dozens if item[1] > 0] if dozens_hits: # Collect the top 2 dozens, including ties top_dozens = [] scores_seen = set() for name, score in sorted_dozens: if len(top_dozens) < 2 or score in scores_seen: top_dozens.append((name, score)) scores_seen.add(score) else: break # Display the top 2 dozens recommendations.append("Best Dozens (Top 2):") for i, (name, score) in enumerate(top_dozens[:2], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 2 positions if len(top_dozens) > 1: first_score = top_dozens[0][1] tied_first = [name for name, score in top_dozens if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") if len(top_dozens) > 1: second_score = top_dozens[1][1] tied_second = [name for name, score in top_dozens if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") else: recommendations.append("Best Dozens: No hits yet.") # Best Even Money Bets (Top 3 with tie handling, same as best_even_money_bets) recommendations.append("") # Add a blank line for separation sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True) even_money_hits = [item for item in sorted_even_money if item[1] > 0] if even_money_hits: # Collect the top 3 bets, including ties top_bets = [] scores_seen = set() for name, score in sorted_even_money: if len(top_bets) < 3 or score in scores_seen: top_bets.append((name, score)) scores_seen.add(score) else: break # Display the top 3 bets recommendations.append("Best Even Money Bets (Top 3):") for i, (name, score) in enumerate(top_bets[:3], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 3 positions if len(top_bets) > 1: first_score = top_bets[0][1] tied_first = [name for name, score in top_bets if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") if len(top_bets) > 1: second_score = top_bets[1][1] tied_second = [name for name, score in top_bets if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") if len(top_bets) > 2: third_score = top_bets[2][1] tied_third = [name for name, score in top_bets if score == third_score] if len(tied_third) > 1: recommendations.append(f"Note: Tie for 3rd place among {', '.join(tied_third)} with score {third_score}") else: recommendations.append("Best Even Money Bets: No hits yet.") # Top Pick 18 Numbers without Neighbours (same as top_pick_18_numbers_without_neighbours) recommendations.append("") # Add a blank line for separation straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty or len(straight_up_df) < 18: recommendations.append("Top Pick 18 Numbers without Neighbours: Not enough numbers have hit yet (need at least 18).") return "\n".join(recommendations) top_18_df = straight_up_df.head(18) top_18_numbers = top_18_df["Number"].tolist() top_6 = top_18_numbers[:6] next_6 = top_18_numbers[6:12] last_6 = top_18_numbers[12:18] recommendations.append("Top Pick 18 Numbers without Neighbours:") recommendations.append("\nTop 6 Numbers (Yellow):") for i, num in enumerate(top_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nNext 6 Numbers (Blue):") for i, num in enumerate(next_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nLast 6 Numbers (Green):") for i, num in enumerate(last_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") return "\n".join(recommendations) def best_columns_even_money_and_top_18(): recommendations = [] # Best Columns (Top 2 with tie handling, same as best_columns) sorted_columns = sorted(state.column_scores.items(), key=lambda x: x[1], reverse=True) columns_hits = [item for item in sorted_columns if item[1] > 0] if columns_hits: # Collect the top 2 columns, including ties top_columns = [] scores_seen = set() for name, score in sorted_columns: if len(top_columns) < 2 or score in scores_seen: top_columns.append((name, score)) scores_seen.add(score) else: break # Display the top 2 columns recommendations.append("Best Columns (Top 2):") for i, (name, score) in enumerate(top_columns[:2], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 2 positions if len(top_columns) > 1: first_score = top_columns[0][1] tied_first = [name for name, score in top_columns if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") if len(top_columns) > 1: second_score = top_columns[1][1] tied_second = [name for name, score in top_columns if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") else: recommendations.append("Best Columns: No hits yet.") # Best Even Money Bets (Top 3 with tie handling, same as best_even_money_bets) recommendations.append("") # Add a blank line for separation sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True) even_money_hits = [item for item in sorted_even_money if item[1] > 0] if even_money_hits: # Collect the top 3 bets, including ties top_bets = [] scores_seen = set() for name, score in sorted_even_money: if len(top_bets) < 3 or score in scores_seen: top_bets.append((name, score)) scores_seen.add(score) else: break # Display the top 3 bets recommendations.append("Best Even Money Bets (Top 3):") for i, (name, score) in enumerate(top_bets[:3], 1): recommendations.append(f"{i}. {name}: {score}") # Check for ties among the top 3 positions if len(top_bets) > 1: first_score = top_bets[0][1] tied_first = [name for name, score in top_bets if score == first_score] if len(tied_first) > 1: recommendations.append(f"Note: Tie for 1st place among {', '.join(tied_first)} with score {first_score}") if len(top_bets) > 1: second_score = top_bets[1][1] tied_second = [name for name, score in top_bets if score == second_score] if len(tied_second) > 1: recommendations.append(f"Note: Tie for 2nd place among {', '.join(tied_second)} with score {second_score}") if len(top_bets) > 2: third_score = top_bets[2][1] tied_third = [name for name, score in top_bets if score == third_score] if len(tied_third) > 1: recommendations.append(f"Note: Tie for 3rd place among {', '.join(tied_third)} with score {third_score}") else: recommendations.append("Best Even Money Bets: No hits yet.") # Top Pick 18 Numbers without Neighbours (same as top_pick_18_numbers_without_neighbours) recommendations.append("") # Add a blank line for separation straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty or len(straight_up_df) < 18: recommendations.append("Top Pick 18 Numbers without Neighbours: Not enough numbers have hit yet (need at least 18).") return "\n".join(recommendations) top_18_df = straight_up_df.head(18) top_18_numbers = top_18_df["Number"].tolist() top_6 = top_18_numbers[:6] next_6 = top_18_numbers[6:12] last_6 = top_18_numbers[12:18] recommendations.append("Top Pick 18 Numbers without Neighbours:") recommendations.append("\nTop 6 Numbers (Yellow):") for i, num in enumerate(top_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nNext 6 Numbers (Blue):") for i, num in enumerate(next_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") recommendations.append("\nLast 6 Numbers (Green):") for i, num in enumerate(last_6, 1): score = top_18_df[top_18_df["Number"] == num]["Score"].iloc[0] recommendations.append(f"{i}. Number {num} (Score: {score})") return "\n".join(recommendations) def create_color_code_table(): html = '''

Color Code Key

Color Meaning
Yellow (Top Tier) Indicates the hottest or top-ranked numbers/sections (e.g., top 3 or top 6 in most strategies). For Dozen Tracker, this highlights the most frequent Dozen when no strategy is selected. Can be changed via color pickers.
Cyan (Middle Tier) Represents the second tier of trending numbers/sections (e.g., ranks 4-6 or secondary picks). For Dozen Tracker, this highlights the second most frequent Dozen when no strategy is selected. Can be changed via color pickers.
Green (Lower Tier) Marks the third tier of strong numbers/sections (e.g., ranks 7-9 or lower priority). Can be changed via color pickers.
Light Gray (Cold Top) Used in Cold Bet Strategy for the coldest top-tier sections (least hits). Fixed for this strategy.
Plum (Cold Middle) Used in Cold Bet Strategy for middle-tier cold sections. Fixed for this strategy.
Light Cyan (Cold Lower) Used in Cold Bet Strategy for lower-tier cold sections. Fixed for this strategy.
Red Default color for red numbers on the roulette table.
Black Default color for black numbers on the roulette table.
Green Default color for zero (0) on the roulette table.
Tomato Red Used in Dozen Tracker to represent the 1st Dozen.
Steel Blue Used in Dozen Tracker to represent the 2nd Dozen.
Lime Green Used in Dozen Tracker to represent the 3rd Dozen.
Gray Used in Dozen Tracker to represent spins not in any Dozen (i.e., 0).
''' return html def update_spin_counter(): """Update the spin counter HTML with the total number of spins.""" total_spins = len(state.last_spins) return f'Total Spins: {total_spins}' # Lines before (context, unchanged) def top_numbers_with_neighbours_tiered(): recommendations = [] straight_up_df = pd.DataFrame(list(state.scores.items()), columns=["Number", "Score"]) straight_up_df = straight_up_df[straight_up_df["Score"] > 0].sort_values(by="Score", ascending=False) if straight_up_df.empty: return "

Top Numbers with Neighbours (Tiered): No numbers have hit yet.

" # Start with the HTML table for Strongest Numbers table_html = '' table_html += "" # Table header for _, row in straight_up_df.iterrows(): num = str(row["Number"]) left, right = current_neighbors.get(row["Number"], ("", "")) left = str(left) if left is not None else "" right = str(right) if right is not None else "" table_html += f"" table_html += "
HitLeft N.Right N.
{num}{left}{right}
" # Wrap the table in a div with a heading recommendations.append("

Strongest Numbers:

") recommendations.append(table_html) num_to_take = min(8, len(straight_up_df)) top_numbers = straight_up_df["Number"].head(num_to_take).tolist() all_numbers = set() number_scores = {} for num in top_numbers: neighbors = current_neighbors.get(num, (None, None)) left, right = neighbors all_numbers.add(num) number_scores[num] = state.scores[num] if left is not None: all_numbers.add(left) if right is not None: all_numbers.add(right) number_groups = [] for num in top_numbers: left, right = current_neighbors.get(num, (None, None)) group = [num] if left is not None: group.append(left) if right is not None: group.append(right) number_groups.append((state.scores[num], group)) number_groups.sort(key=lambda x: x[0], reverse=True) ordered_numbers = [] for _, group in number_groups: ordered_numbers.extend(group) ordered_numbers = ordered_numbers[:24] top_8 = ordered_numbers[:8] next_8 = ordered_numbers[8:16] last_8 = ordered_numbers[16:24] recommendations.append("

Top Numbers with Neighbours (Tiered):

") recommendations.append("

Top Tier (Yellow):

") for i, num in enumerate(top_8, 1): score = number_scores.get(num, "Neighbor") recommendations.append(f"

{i}. Number {num} (Score: {score})

") recommendations.append("

Second Tier (Blue):

") for i, num in enumerate(next_8, 1): score = number_scores.get(num, "Neighbor") recommendations.append(f"

{i}. Number {num} (Score: {score})

") recommendations.append("

Third Tier (Green):

") for i, num in enumerate(last_8, 1): score = number_scores.get(num, "Neighbor") recommendations.append(f"

{i}. Number {num} (Score: {score})

") return "\n".join(recommendations) # Line 1: Start of neighbours_of_strong_number function (updated) def neighbours_of_strong_number(neighbours_count, strong_numbers_count): """Recommend numbers and their neighbors based on hit frequency, including strategy recommendations with tie information.""" recommendations = [] # Validate inputs try: neighbours_count = int(neighbours_count) strong_numbers_count = int(strong_numbers_count) if neighbours_count < 0 or strong_numbers_count < 0: raise ValueError("Neighbours count and strong numbers count must be non-negative.") if strong_numbers_count == 0: raise ValueError("Strong numbers count must be at least 1.") except (ValueError, TypeError) as e: return f"Error: Invalid input - {str(e)}. Please use positive integers for neighbours and strong numbers.", {} # Check if current_neighbors is valid if not isinstance(current_neighbors, dict): return "Error: Neighbor data is not properly configured. Contact support.", {} for key, value in current_neighbors.items(): if not isinstance(key, int) or not isinstance(value, tuple) or len(value) != 2: return "Error: Neighbor data is malformed. Contact support.", {} try: print(f"neighbours_of_strong_number: Starting with neighbours_count = {neighbours_count}, strong_numbers_count = {strong_numbers_count}") sorted_numbers = sorted(state.scores.items(), key=lambda x: (-x[1], x[0])) numbers_hits = [item for item in sorted_numbers if item[1] > 0] if not numbers_hits: recommendations.append("Neighbours of Strong Number: No numbers have hit yet.") return "\n".join(recommendations), {} # Limit strong_numbers_count to available hits strong_numbers_count = min(strong_numbers_count, len(numbers_hits)) top_numbers = [item[0] for item in numbers_hits[:strong_numbers_count]] top_scores = {item[0]: item[1] for item in numbers_hits[:strong_numbers_count]} selected_numbers = set(top_numbers) neighbors_set = set() # Calculate neighbors for each strong number for strong_number in top_numbers: if strong_number not in current_neighbors: recommendations.append(f"Warning: No neighbor data for number {strong_number}. Skipping its neighbors.") continue current_number = strong_number # Left neighbors for i in range(neighbours_count): left, _ = current_neighbors.get(current_number, (None, None)) if left is not None: neighbors_set.add(left) current_number = left else: break # Right neighbors current_number = strong_number for i in range(neighbours_count): _, right = current_neighbors.get(current_number, (None, None)) if right is not None: neighbors_set.add(right) current_number = right else: break # Remove overlap (strong numbers take precedence) neighbors_set = neighbors_set - selected_numbers print(f"neighbours_of_strong_number: Strong numbers = {sorted(list(selected_numbers))}") print(f"neighbours_of_strong_number: Neighbors = {sorted(list(neighbors_set))}") # Combine all bet numbers (strong numbers + neighbors) for aggregated scoring bet_numbers = list(selected_numbers) + list(neighbors_set) # Calculate Aggregated Scores for the bet numbers (needed for Suggestions) even_money_scores, dozen_scores, column_scores = state.calculate_aggregated_scores_for_spins(bet_numbers) # Determine the best even money bet and check for ties sorted_even_money = sorted(even_money_scores.items(), key=lambda x: (-x[1], x[0])) best_even_money = sorted_even_money[0] if sorted_even_money else ("None", 0) best_even_money_name, best_even_money_hits = best_even_money # Check for ties in even money bets even_money_ties = [] if sorted_even_money and best_even_money_hits > 0: even_money_ties = [f"{name}: {score}" for name, score in sorted_even_money if score == best_even_money_hits and name != best_even_money_name] even_money_tie_text = f" (Tied with {', '.join(even_money_ties)})" if even_money_ties else "" # Determine the best dozen and best column best_dozen = max(dozen_scores.items(), key=lambda x: x[1], default=("None", 0)) best_dozen_name, best_dozen_hits = best_dozen best_column = max(column_scores.items(), key=lambda x: x[1], default=("None", 0)) best_column_name, best_column_hits = best_column # Compare dozens vs. columns for the stronger section and check for ties suggestion = "" winner_category = "" best_bet_tie_text = "" if best_dozen_hits > best_column_hits: suggestion = f"{best_dozen_name}: {best_dozen_hits}" winner_category = "dozen" # Check if the best dozen ties with others sorted_dozens = sorted(dozen_scores.items(), key=lambda x: (-x[1], x[0])) dozen_ties = [f"{name}: {score}" for name, score in sorted_dozens if score == best_dozen_hits and name != best_dozen_name] if dozen_ties: best_bet_tie_text = f" (Tied with {', '.join(dozen_ties)})" elif best_column_hits > best_dozen_hits: suggestion = f"{best_column_name}: {best_column_hits}" winner_category = "column" # Check if the best column ties with others sorted_columns = sorted(column_scores.items(), key=lambda x: (-x[1], x[0])) column_ties = [f"{name}: {score}" for name, score in sorted_columns if score == best_column_hits and name != best_column_name] if column_ties: best_bet_tie_text = f" (Tied with {', '.join(column_ties)})" else: # Check for ties between dozens and columns at the top level sorted_dozens = sorted(dozen_scores.items(), key=lambda x: (-x[1], x[0])) sorted_columns = sorted(column_scores.items(), key=lambda x: (-x[1], x[0])) if len(sorted_dozens) >= 2 and sorted_dozens[0][1] == sorted_dozens[1][1] and sorted_dozens[0][1] > 0: # Two dozens tie at the highest hit count suggestion = f"{sorted_dozens[0][0]} and {sorted_dozens[1][0]}: {sorted_dozens[0][1]}" winner_category = "dozen" # Check for additional dozen ties dozen_ties = [f"{name}: {score}" for name, score in sorted_dozens[2:] if score == sorted_dozens[0][1]] if dozen_ties: best_bet_tie_text = f" (Tied with {', '.join(dozen_ties)})" elif len(sorted_columns) >= 2 and sorted_columns[0][1] == sorted_columns[1][1] and sorted_columns[0][1] > 0: # Two columns tie at the highest hit count suggestion = f"{sorted_columns[0][0]} and {sorted_columns[1][0]}: {sorted_columns[0][1]}" winner_category = "column" # Check for additional column ties column_ties = [f"{name}: {score}" for name, score in sorted_columns[2:] if score == sorted_columns[0][1]] if column_ties: best_bet_tie_text = f" (Tied with {', '.join(column_ties)})" else: # Default to the best dozen (alphabetically if tied), check for ties with columns suggestion = f"{best_dozen_name}: {best_dozen_hits}" winner_category = "dozen" if best_dozen_hits == best_column_hits and best_column_hits > 0: best_bet_tie_text = f" (Tied with {best_column_name}: {best_column_hits})" # Determine the top two winners in the winning category (dozens or columns) and check for ties two_winners_suggestion = "" two_winners_tie_text = "" if winner_category == "dozen": sorted_dozens = sorted(dozen_scores.items(), key=lambda x: (-x[1], x[0])) top_two_dozens = sorted_dozens[:2] # Take top two dozens if top_two_dozens[0][1] > 0: # Only suggest if there are hits two_winners_suggestion = f"Play Two Dozens: {top_two_dozens[0][0]} ({top_two_dozens[0][1]}) and {top_two_dozens[1][0]} ({top_two_dozens[1][1]})" # Check if the second dozen ties with others if len(sorted_dozens) > 2: second_score = top_two_dozens[1][1] ties = [f"{name}: {score}" for name, score in sorted_dozens[2:] if score == second_score] if ties: two_winners_tie_text = f" (Tied with {', '.join(ties)})" else: two_winners_suggestion = "Play Two Dozens: Not enough hits to suggest two dozens." elif winner_category == "column": sorted_columns = sorted(column_scores.items(), key=lambda x: (-x[1], x[0])) top_two_columns = sorted_columns[:2] # Take top two columns if top_two_columns[0][1] > 0: # Only suggest if there are hits two_winners_suggestion = f"Play Two Columns: {top_two_columns[0][0]} ({top_two_columns[0][1]}) and {top_two_columns[1][0]} ({top_two_columns[1][1]})" # Check if the second column ties with others if len(sorted_columns) > 2: second_score = top_two_columns[1][1] ties = [f"{name}: {score}" for name, score in sorted_columns[2:] if score == second_score] if ties: two_winners_tie_text = f" (Tied with {', '.join(ties)})" else: two_winners_suggestion = "Play Two Columns: Not enough hits to suggest two columns." # Create the suggestions dictionary suggestions = { "best_even_money": f"{best_even_money_name}: {best_even_money_hits}{even_money_tie_text}", "best_bet": f"{suggestion}{best_bet_tie_text}", "play_two": f"{two_winners_suggestion}{two_winners_tie_text}" } # Append the Suggestions section first recommendations.append("Suggestions:") recommendations.append(f"Best Even Money Bet: {best_even_money_name}: {best_even_money_hits}{even_money_tie_text}") recommendations.append(f"Best Bet: {suggestion}{best_bet_tie_text}") recommendations.append(f"{two_winners_suggestion}{two_winners_tie_text}") # Now append the Strongest Numbers and Neighbours section recommendations.append(f"\nTop {strong_numbers_count} Strongest Numbers and Their Neighbours:") recommendations.append("\nStrongest Numbers (Yellow):") for i, num in enumerate(sorted(top_numbers), 1): score = top_scores[num] recommendations.append(f"{i}. Number {num} (Score: {score})") if neighbors_set: recommendations.append(f"\nNeighbours ({neighbours_count} Left + {neighbours_count} Right, Cyan):") for i, num in enumerate(sorted(list(neighbors_set)), 1): recommendations.append(f"{i}. Number {num}") else: recommendations.append(f"\nNeighbours ({neighbours_count} Left + {neighbours_count} Right, Cyan): None") return "\n".join(recommendations), suggestions except Exception as e: print(f"neighbours_of_strong_number: Unexpected error: {str(e)}") return f"Error in Neighbours of Strong Number: Unexpected issue - {str(e)}. Please try again or contact support.", {} # Line 3: Start of dozen_tracker function (unchanged) def dozen_tracker(num_spins_to_check, consecutive_hits_threshold, alert_enabled, sequence_length, follow_up_spins, sequence_alert_enabled): """Track and display the history of Dozen hits for the last N spins, with optional alerts for consecutive hits and sequence matching.""" recommendations = [] sequence_recommendations = [] # Validate inputs try: num_spins_to_check = int(num_spins_to_check) consecutive_hits_threshold = int(consecutive_hits_threshold) sequence_length = int(sequence_length) follow_up_spins = int(follow_up_spins) if num_spins_to_check < 1: return "Error: Number of spins to check must be at least 1.", "

Error: Number of spins to check must be at least 1.

", "

Error: Number of spins to check must be at least 1.

" if consecutive_hits_threshold < 1: return "Error: Consecutive hits threshold must be at least 1.", "

Error: Consecutive hits threshold must be at least 1.

", "

Error: Consecutive hits threshold must be at least 1.

" if sequence_length < 1: return "Error: Sequence length must be at least 1.", "

Error: Sequence length must be at least 1.

", "

Error: Sequence length must be at least 1.

" if follow_up_spins < 1: return "Error: Follow-up spins must be at least 1.", "

Error: Follow-up spins must be at least 1.

", "

Error: Follow-up spins must be at least 1.

" except (ValueError, TypeError): return "Error: Invalid inputs. Please use positive integers.", "

Error: Invalid inputs. Please use positive integers.

", "

Error: Invalid inputs. Please use positive integers.

" # Get the last N spins for sequence matching recent_spins = state.last_spins[-num_spins_to_check:] if len(state.last_spins) >= num_spins_to_check else state.last_spins print(f"dozen_tracker: Tracking {num_spins_to_check} spins for sequence matching, recent_spins length = {len(recent_spins)}") if not recent_spins: return "Dozen Tracker: No spins recorded yet.", "

Dozen Tracker: No spins recorded yet.

", "

Dozen Tracker: No spins recorded yet.

" # Map each spin to its Dozen for sequence matching dozen_pattern = [] dozen_counts = {"1st Dozen": 0, "2nd Dozen": 0, "3rd Dozen": 0, "Not in Dozen": 0} for spin in recent_spins: spin_value = int(spin) if spin_value == 0: dozen_pattern.append("Not in Dozen") dozen_counts["Not in Dozen"] += 1 else: found = False for name, numbers in DOZENS.items(): if spin_value in numbers: dozen_pattern.append(name) dozen_counts[name] += 1 found = True break if not found: dozen_pattern.append("Not in Dozen") dozen_counts["Not in Dozen"] += 1 # Map the entire spin history to Dozens for sequence matching full_dozen_pattern = [] for spin in state.last_spins: spin_value = int(spin) if spin_value == 0: full_dozen_pattern.append("Not in Dozen") else: found = False for name, numbers in DOZENS.items(): if spin_value in numbers: full_dozen_pattern.append(name) found = True break if not found: full_dozen_pattern.append("Not in Dozen") # Detect consecutive Dozen hits in the LAST 3 spins only (if alert is enabled) if alert_enabled: # Take only the last 3 spins (or fewer if not enough spins) last_three_spins = state.last_spins[-3:] if len(state.last_spins) >= 3 else state.last_spins print(f"dozen_tracker: Checking last 3 spins for consecutive hits, last_three_spins = {last_three_spins}") if len(last_three_spins) < 3: print("dozen_tracker: Not enough spins to check for consecutive hits (need at least 3).") state.last_dozen_alert_index = -1 state.last_alerted_spins = None else: # Map the last 3 spins to their Dozens last_three_dozens = [] for spin in last_three_spins: spin_value = int(spin) if spin_value == 0: last_three_dozens.append("Not in Dozen") else: found = False for name, numbers in DOZENS.items(): if spin_value in numbers: last_three_dozens.append(name) found = True break if not found: last_three_dozens.append("Not in Dozen") print(f"dozen_tracker: Last 3 spins dozens = {last_three_dozens}") # Check if all 3 spins are in the same Dozen and not "Not in Dozen" if (last_three_dozens[0] == last_three_dozens[1] == last_three_dozens[2] and last_three_dozens[0] != "Not in Dozen"): current_dozen = last_three_dozens[0] # Convert last_three_spins to a tuple for comparison (immutable and hashable) current_spins_tuple = tuple(last_three_spins) # Check if this set of spins is different from the last alerted set if state.last_alerted_spins != current_spins_tuple: # Include the spins in the alert spins_str = ", ".join(map(str, last_three_spins)) alert_message = f"Alert: {current_dozen} has hit 3 times consecutively! (Spins: {spins_str})" gr.Warning(alert_message) recommendations.append(alert_message) state.last_dozen_alert_index = len(state.last_spins) - 1 # Update the last alerted index state.last_alerted_spins = current_spins_tuple # Store the spins that triggered this alert else: # If the last 3 spins don't form a streak, reset the alert index and spins state.last_dozen_alert_index = -1 state.last_alerted_spins = None # Detect sequence matches (only if sequence alert is enabled) sequence_matches = [] sequence_follow_ups = [] if sequence_alert_enabled and len(full_dozen_pattern) >= sequence_length: # Take the last X spins to check for a match last_x_spins = full_dozen_pattern[-sequence_length:] if len(full_dozen_pattern) >= sequence_length else full_dozen_pattern print(f"dozen_tracker: Checking last {sequence_length} spins for sequence matching, last_x_spins = {last_x_spins}") if len(last_x_spins) < sequence_length: print(f"dozen_tracker: Not enough spins to check for sequence of length {sequence_length}.") else: # Convert the last X spins to a tuple for comparison last_x_pattern = tuple(last_x_spins) # Collect all sequences of length X within the tracking window (recent_spins) sequences = [] for i in range(len(dozen_pattern) - sequence_length + 1): seq = tuple(dozen_pattern[i:i + sequence_length]) # Only consider sequences that end before the last X spins if i + sequence_length <= len(dozen_pattern) - sequence_length: sequences.append((i, seq)) print(f"dozen_tracker: Found {len(sequences)} sequences of length {sequence_length} in the tracking window") # Check if the last X spins match any previous sequence for start_idx, seq in sequences: if seq == last_x_pattern: # Check if we've already alerted for this exact pattern if seq not in state.alerted_patterns: sequence_matches.append((start_idx, seq)) # Get the next Y spins after the first occurrence follow_up_start = start_idx + sequence_length follow_up_end = follow_up_start + follow_up_spins if follow_up_end <= len(dozen_pattern): follow_up = dozen_pattern[follow_up_start:follow_up_end] sequence_follow_ups.append((start_idx, seq, follow_up)) # Mark this pattern as alerted state.alerted_patterns.add(seq) # If a match is found, provide betting recommendations with spin context if sequence_matches: latest_match = max(sequence_matches, key=lambda x: x[0]) # Latest match by start index latest_start_idx, matched_sequence = latest_match # Find the follow-up spins for the first occurrence of this sequence first_occurrence = min((seq for seq in sequences if seq[1] == matched_sequence), key=lambda x: x[0])[0] follow_up_start = first_occurrence + sequence_length follow_up_end = follow_up_start + follow_up_spins # Adjust indices for the full spin history latest_start_idx_full = len(full_dozen_pattern) - sequence_length # Get the actual spins that triggered the sequence sequence_spins = recent_spins[-sequence_length:] # Last X spins sequence_spins_str = ", ".join(map(str, sequence_spins)) if follow_up_end <= len(dozen_pattern): follow_up = dozen_pattern[follow_up_start:follow_up_end] alert_message = f"Alert: Sequence {', '.join(matched_sequence)} has repeated at spins {sequence_spins_str}!" gr.Warning(alert_message) sequence_recommendations.append(alert_message) sequence_recommendations.append(f"Previous follow-up spins (next {follow_up_spins}): {', '.join(follow_up)}") sequence_recommendations.append("Betting Recommendations (Bet Against Historical Follow-Ups):") all_dozens = ["1st Dozen", "2nd Dozen", "3rd Dozen"] for idx, dozen in enumerate(follow_up): if dozen == "Not in Dozen": sequence_recommendations.append(f"Spin {idx + 1}: 0 (Not in Dozen) - No bet recommendation.") else: dozens_to_bet = [d for d in all_dozens if d != dozen] sequence_recommendations.append(f"Spin {idx + 1}: Bet against {dozen} - Bet on {', '.join(dozens_to_bet)}") else: # If no match is found, reset the alerted patterns to allow future matches state.alerted_patterns.clear() # Text summary for Dozen Tracker recommendations.append(f"Dozen Tracker (Last {len(recent_spins)} Spins):") recommendations.append("Dozen History: " + ", ".join(dozen_pattern)) recommendations.append("\nSummary of Dozen Hits:") for name, count in dozen_counts.items(): recommendations.append(f"{name}: {count} hits") # HTML representation for Dozen Tracker html_output = f'

Dozen Tracker (Last {len(recent_spins)} Spins):

' html_output += '
' for dozen in dozen_pattern: color = { "1st Dozen": "#FF6347", # Tomato red "2nd Dozen": "#4682B4", # Steel blue "3rd Dozen": "#32CD32", # Lime green "Not in Dozen": "#808080" # Gray for 0 }.get(dozen, "#808080") html_output += f'{dozen}' html_output += '
' if alert_enabled and "Alert:" in "\n".join(recommendations): # Extract the alert message from recommendations alert_message = next((line for line in recommendations if line.startswith("Alert:")), "") html_output += f'

{alert_message}

' html_output += '

Summary of Dozen Hits:

' html_output += '' # HTML representation for Sequence Matching sequence_html_output = "

Sequence Matching Results:

" if not sequence_alert_enabled: sequence_html_output += "

Sequence matching is disabled. Enable it to see results.

" elif len(dozen_pattern) < sequence_length: sequence_html_output += f"

Not enough spins to match a sequence of length {sequence_length}.

" elif not sequence_matches: sequence_html_output += "

No sequence matches found yet.

" else: sequence_html_output += "" if sequence_recommendations: sequence_html_output += "

Latest Match Details:

" sequence_html_output += "" return "\n".join(recommendations), html_output, sequence_html_output # New: Even Money Bet Tracker Function def even_money_tracker(spins_to_check, consecutive_hits_threshold, alert_enabled, combination_mode, track_red, track_black, track_even, track_odd, track_low, track_high, identical_traits_enabled, consecutive_identical_count): """Track even money bets and their combinations for consecutive hits, with optional tracking of consecutive identical trait combinations.""" # Sanitize inputs with defaults to prevent None or invalid values spins_to_check = int(spins_to_check) if spins_to_check and str(spins_to_check).strip().isdigit() else 5 consecutive_hits_threshold = int(consecutive_hits_threshold) if consecutive_hits_threshold and str(consecutive_hits_threshold).strip().isdigit() else 3 consecutive_identical_count = int(consecutive_identical_count) if consecutive_identical_count and str(consecutive_identical_count).strip().isdigit() else 2 # Validate inputs if spins_to_check < 1 or consecutive_hits_threshold < 1 or consecutive_identical_count < 1: return "Error: Inputs must be at least 1.", "

Error: Inputs must be at least 1.

" # Get recent spins recent_spins = state.last_spins[-spins_to_check:] if len(state.last_spins) >= spins_to_check else state.last_spins if not recent_spins: return "Even Money Tracker: No spins recorded yet.", "

Even Money Tracker: No spins recorded yet.

" # Determine which categories to track categories_to_track = [] if track_red: categories_to_track.append("Red") if track_black: categories_to_track.append("Black") if track_even: categories_to_track.append("Even") if track_odd: categories_to_track.append("Odd") if track_low: categories_to_track.append("Low") if track_high: categories_to_track.append("High") # If no categories are explicitly selected, track all categories by default if not categories_to_track: categories_to_track = ["Red", "Black", "Even", "Odd", "Low", "High"] # Map spins to even money categories and track full trait combinations pattern = [] category_counts = {name: 0 for name in EVEN_MONEY.keys()} trait_combinations = [] # Store the full trait combination for each spin (e.g., "Red, Odd, Low") hit_spins = [] # Track spins for each pattern element (Hit/Miss) for spin in recent_spins: spin_value = int(spin) spin_categories = [] for name, numbers in EVEN_MONEY.items(): if spin_value in numbers: spin_categories.append(name) category_counts[name] += 1 # Determine if the spin matches the tracked combination if combination_mode == "And": if all(cat in spin_categories for cat in categories_to_track): pattern.append("Hit") hit_spins.append(str(spin_value)) else: pattern.append("Miss") hit_spins.append(str(spin_value)) else: # Or mode if any(cat in spin_categories for cat in categories_to_track): pattern.append("Hit") hit_spins.append(str(spin_value)) else: pattern.append("Miss") hit_spins.append(str(spin_value)) # Build the full trait combination for this spin (Color, Parity, Range) color = "Red" if "Red" in spin_categories else ("Black" if "Black" in spin_categories else "None") parity = "Even" if "Even" in spin_categories else ("Odd" if "Odd" in spin_categories else "None") range_ = "Low" if "Low" in spin_categories else ("High" if "High" in spin_categories else "None") trait_combination = f"{color}, {parity}, {range_}" trait_combinations.append(trait_combination) # Track consecutive hits of the selected combination with spin context current_streak = 1 if pattern[0] == "Hit" else 0 max_streak = current_streak max_streak_start = 0 current_streak_spins = [hit_spins[0]] if pattern[0] == "Hit" else [] max_streak_spins = current_streak_spins[:] for i in range(1, len(pattern)): if pattern[i] == "Hit" and pattern[i-1] == "Hit": current_streak += 1 current_streak_spins.append(hit_spins[i]) else: current_streak = 1 if pattern[i] == "Hit" else 0 current_streak_spins = [hit_spins[i]] if pattern[i] == "Hit" else [] if current_streak > max_streak: max_streak = current_streak max_streak_start = i - current_streak + 1 max_streak_spins = current_streak_spins[:] # Track consecutive identical trait combinations with spin context identical_recommendations = [] identical_html_output = "" betting_recommendation = None if identical_traits_enabled: # Detect consecutive identical trait combinations identical_streak = 1 identical_streak_start = 0 identical_matches = [] identical_streak_spins = [recent_spins[0]] # Track spins for identical streaks for i in range(1, len(trait_combinations)): if trait_combinations[i] == trait_combinations[i-1] and trait_combinations[i] != "None, None, None": identical_streak += 1 identical_streak_spins.append(recent_spins[i]) if identical_streak == consecutive_identical_count: identical_matches.append((i - consecutive_identical_count + 1, trait_combinations[i], identical_streak_spins[-consecutive_identical_count:])) identical_streak_start = i - consecutive_identical_count + 1 else: identical_streak = 1 identical_streak_spins = [recent_spins[i]] if identical_matches: # Process the most recent match latest_match_start, matched_traits, matched_spins = identical_matches[-1] spins_str = ", ".join(map(str, matched_spins)) if alert_enabled: gr.Warning(f"Alert: Traits '{matched_traits}' appeared {consecutive_identical_count} times consecutively! (Spins: {spins_str})") identical_recommendations.append(f"Alert: Traits '{matched_traits}' appeared {consecutive_identical_count} times consecutively! (Spins: {spins_str})") # Calculate opposite traits traits = [t.strip() for t in matched_traits.split(",")] opposite_traits = [] for trait in traits: if trait == "Red": opposite_traits.append("Black") elif trait == "Black": opposite_traits.append("Red") elif trait == "Even": opposite_traits.append("Odd") elif trait == "Odd": opposite_traits.append("Even") elif trait == "Low": opposite_traits.append("High") elif trait == "High": opposite_traits.append("Low") else: opposite_traits.append("None") opposite_combination = ", ".join(opposite_traits) identical_recommendations.append(f"Opposite Traits: {opposite_combination}") # Get the top-tier even money bet (highest score in even_money_scores) sorted_even_money = sorted(state.even_money_scores.items(), key=lambda x: x[1], reverse=True) even_money_hits = [item for item in sorted_even_money if item[1] > 0] if even_money_hits: top_tier_bet = even_money_hits[0][0] # e.g., "Even" top_tier_score = even_money_hits[0][1] identical_recommendations.append(f"Current Top-Tier Even Money Bet (Yellow): {top_tier_bet} (Score: {top_tier_score})") # Correctly compare top-tier bet to the corresponding opposite trait opposites_map = { "Red": "Black", "Black": "Red", "Even": "Odd", "Odd": "Even", "Low": "High", "High": "Low" } # Determine which trait category the top-tier bet belongs to trait_index = None if top_tier_bet in ["Red", "Black"]: trait_index = 0 # Color elif top_tier_bet in ["Even", "Odd"]: trait_index = 1 # Parity elif top_tier_bet in ["Low", "High"]: trait_index = 2 # Range match_found = False if trait_index is not None: corresponding_opposite = opposite_traits[trait_index] # Check if the top-tier bet matches its opposite in the correct category if top_tier_bet == corresponding_opposite: match_found = True if match_found: betting_recommendation = f"Match found! Bet on '{top_tier_bet}' for the next 3 spins." if alert_enabled: gr.Warning(f"Match found! Bet on '{top_tier_bet}' for the next 3 spins.") identical_recommendations.append(betting_recommendation) else: identical_recommendations.append("No match with opposite traits. No betting recommendation.") else: identical_recommendations.append("No top-tier even money bet available (no hits yet).") # Build HTML output for identical traits tracking identical_html_output = "
" identical_html_output += "

Consecutive Identical Traits Tracking:

" identical_html_output += "" identical_html_output += "
" # Generate text and HTML for the original even money tracking with spin context tracked_str = " and ".join(categories_to_track) if combination_mode == "And" else " or ".join(categories_to_track) recommendations = [] html_output = "
" recommendations.append(f"Even Money Tracker (Last {len(recent_spins)} Spins):") recommendations.append(f"Tracking: {tracked_str} ({combination_mode})") recommendations.append("History: " + ", ".join(pattern)) if alert_enabled and max_streak >= consecutive_hits_threshold: # Include the spins that triggered the streak streak_spins = ", ".join(max_streak_spins[-consecutive_hits_threshold:]) gr.Warning(f"Alert: {tracked_str} hit {max_streak} times consecutively! (Spins: {streak_spins})") recommendations.append(f"\nAlert: {tracked_str} hit {max_streak} times consecutively! (Spins: {streak_spins})") recommendations.append("\nSummary of Hits:") for name, count in category_counts.items(): if name in categories_to_track: recommendations.append(f"{name}: {count} hits") html_output += f'

Even Money Tracker (Last {len(recent_spins)} Spins):

' html_output += f'

Tracking: {tracked_str} ({combination_mode})

' html_output += '
' for status, spin in zip(pattern, hit_spins): color = "#32CD32" if status == "Hit" else "#FF6347" # Green for Hit, Red for Miss html_output += f'{status}' html_output += '
' if alert_enabled and max_streak >= consecutive_hits_threshold: html_output += f'

Alert: {tracked_str} hit {max_streak} times consecutively! (Spins: {streak_spins})

' html_output += '

Summary of Hits:

' html_output += '' # Append the identical traits tracking output (if enabled) if identical_traits_enabled and identical_html_output: html_output += identical_html_output html_output += "
" return "\n".join(recommendations), html_output def validate_hot_cold_numbers(numbers_input, type_label): """Validate hot or cold numbers input (1 to 10 numbers, 0-36).""" import gradio as gr if not numbers_input or not numbers_input.strip(): return None, f"Please enter 1 to 10 {type_label} numbers." try: numbers = [int(n.strip()) for n in numbers_input.split(",") if n.strip()] if len(numbers) < 1 or len(numbers) > 10: return None, f"Enter 1 to 10 {type_label} numbers (entered {len(numbers)})." if not all(0 <= n <= 36 for n in numbers): return None, f"All {type_label} numbers must be between 0 and 36." return numbers, None except ValueError: return None, f"Invalid {type_label} numbers. Use comma-separated integers (e.g., 1, 3, 5, 7, 9)." # Note: play_specific_numbers and clear_hot_cold_picks remain unchanged, e.g.: def play_specific_numbers(numbers_input, number_type, spins_display, last_spin_count): """ Add hot or cold numbers to the spins list and update the UI. Args: numbers_input (str): Comma-separated string of numbers (e.g., "1, 3, 5"). number_type (str): "Hot" or "Cold" to indicate the type of numbers. spins_display (str): Current spins display string. last_spin_count (int): Number of spins to consider for display. Returns: tuple: Updated spins_display, spins_textbox, casino_data_output, spin_counter, sides_of_zero_display. """ try: # Debug: Track how many times this function is called if not hasattr(state, 'play_specific_numbers_counter'): state.play_specific_numbers_counter = 0 state.play_specific_numbers_counter += 1 print(f"play_specific_numbers called (count: {state.play_specific_numbers_counter}) for {number_type} numbers") # Parse the input numbers if not numbers_input or not numbers_input.strip(): return ( spins_display, spins_display, f"

No {number_type.lower()} numbers provided to play.

", update_spin_counter(), render_sides_of_zero_display() ) # Split and clean the input numbers = [num.strip() for num in numbers_input.split(",") if num.strip()] if not numbers: return ( spins_display, spins_display, f"

No valid {number_type.lower()} numbers provided.

", update_spin_counter(), render_sides_of_zero_display() ) # Validate numbers (must be integers between 0 and 36) valid_numbers = [] for num in numbers: try: n = int(num) if 0 <= n <= 36: valid_numbers.append(str(n)) else: print(f"Invalid {number_type.lower()} number: {num}. Must be between 0 and 36.") except ValueError: print(f"Invalid {number_type.lower()} number: {num}. Must be an integer.") continue if not valid_numbers: return ( spins_display, spins_display, f"

No valid {number_type.lower()} numbers to play. Numbers must be between 0 and 36.

", update_spin_counter(), render_sides_of_zero_display() ) # Update state.last_spins if not hasattr(state, 'last_spins'): state.last_spins = [] # Append the valid numbers to state.last_spins state.last_spins.extend(valid_numbers) # Update spins_display to match state.last_spins new_spins_display = ", ".join(state.last_spins) if state.last_spins else "" # Update casino_data_output with a confirmation message casino_message = f"

Played {number_type.lower()} numbers: {', '.join(valid_numbers)}

" # Update spin_counter and sides_of_zero_display new_spin_counter = update_spin_counter() new_sides_of_zero = render_sides_of_zero_display() print(f"Played {number_type.lower()} numbers: {valid_numbers}") print(f"Updated state.last_spins: {state.last_spins}") return ( new_spins_display, # spins_display new_spins_display, # spins_textbox casino_message, # casino_data_output new_spin_counter, # spin_counter new_sides_of_zero # sides_of_zero_display ) except Exception as e: print(f"Error in play_specific_numbers: {str(e)}") return ( spins_display, spins_display, f"

Error playing {number_type.lower()} numbers: {str(e)}

", update_spin_counter(), render_sides_of_zero_display() ) def clear_hot_cold_picks(type_label, current_spins_display): """Clear hot or cold numbers input.""" state.casino_data[f"{type_label.lower()}_numbers"] = [] success_msg = f"Cleared {type_label} Picks successfully" print(f"clear_hot_cold_picks: {success_msg}") return "", success_msg, update_spin_counter(), render_sides_of_zero_display(), current_spins_display def calculate_hit_percentages(last_spin_count): """Calculate hit percentages and generate BOTH Old Badges and New Charts.""" try: # --- 1. Data Collection --- last_spin_count = int(last_spin_count) if last_spin_count is not None else 36 last_spin_count = max(1, min(last_spin_count, 36)) last_spins = state.last_spins[-last_spin_count:] if state.last_spins else [] if not last_spins: return "

No spins available for analysis.

" total_spins = len(last_spins) even_money_counts = {"Red": 0, "Black": 0, "Even": 0, "Odd": 0, "Low": 0, "High": 0} column_counts = {"1st Column": 0, "2nd Column": 0, "3rd Column": 0} dozen_counts = {"1st Dozen": 0, "2nd Dozen": 0, "3rd Dozen": 0} for spin in last_spins: try: num = int(spin) for name, numbers in EVEN_MONEY.items(): if num in numbers: even_money_counts[name] += 1 for name, numbers in COLUMNS.items(): if num in numbers: column_counts[name] += 1 for name, numbers in DOZENS.items(): if num in numbers: dozen_counts[name] += 1 except ValueError: continue max_even_money = max(even_money_counts.values()) if even_money_counts else 0 max_columns = max(column_counts.values()) if column_counts else 0 max_dozens = max(dozen_counts.values()) if dozen_counts else 0 # --- 2. OLD DISPLAY: Horizontal Badges --- old_html = '
' old_html += f'

Hit Percentage Overview (Last {total_spins} Spins):

' old_html += '
' # Even Money old_html += '
' old_html += '

Even Money Bets

' old_html += '
' for name, count in even_money_counts.items(): percentage = (count / total_spins * 100) if total_spins > 0 else 0 badge_class = "percentage-item even-money winner" if count == max_even_money and max_even_money > 0 else "percentage-item even-money" bar_color = "#b71c1c" if name == "Red" else "#000000" if name == "Black" else "#666" old_html += f'
{name}: {percentage:.1f}%
' old_html += '
' # Columns old_html += '
' old_html += '

Columns

' old_html += '
' for name, count in column_counts.items(): percentage = (count / total_spins * 100) if total_spins > 0 else 0 badge_class = "percentage-item column winner" if count == max_columns and max_columns > 0 else "percentage-item column" bar_color = "#1565c0" old_html += f'
{name.split()[0]}: {percentage:.1f}%
' old_html += '
' # Dozens old_html += '
' old_html += '

Dozens

' old_html += '
' for name, count in dozen_counts.items(): percentage = (count / total_spins * 100) if total_spins > 0 else 0 badge_class = "percentage-item dozen winner" if count == max_dozens and max_dozens > 0 else "percentage-item dozen" bar_color = "#388e3c" old_html += f'
{name.split()[0]}: {percentage:.1f}%
' old_html += '
' old_html += '
' # End percentage-wrapper and overview # --- 3. NEW DISPLAY: Vertical Charts (Appended) --- # Helper: Get Rank Color (Yellow > Blue > Green) def get_rank_color(items_dict, key): sorted_items = sorted(items_dict.items(), key=lambda x: x[1], reverse=True) try: rank_idx = [k for k, v in sorted_items].index(key) if rank_idx == 0: return "#FFD700" # Yellow (Hottest) if rank_idx == 1: return "#00BFFF" # Deep Sky Blue (2nd) return "#32CD32" # Lime Green (Coldest/3rd) except ValueError: return "#ccc" # Helper: Create Vertical Bar HTML def create_bar(label, count, total, color): percent = (count / total * 100) if total > 0 else 0 return f"""
{percent:.0f}%
{label} ({count})
""" new_html = '
' new_html += '

Visual Trend Comparison

' new_html += '
' # Chart 1: Even Money Pairs new_html += '
' new_html += '

Even Money

' new_html += '
' # Red vs Black rb_group = {"Red": even_money_counts["Red"], "Black": even_money_counts["Black"]} new_html += '
' new_html += create_bar("Red", even_money_counts["Red"], total_spins, get_rank_color(rb_group, "Red")) new_html += create_bar("Black", even_money_counts["Black"], total_spins, get_rank_color(rb_group, "Black")) new_html += '
' # Odd vs Even oe_group = {"Odd": even_money_counts["Odd"], "Even": even_money_counts["Even"]} new_html += '
' new_html += create_bar("Odd", even_money_counts["Odd"], total_spins, get_rank_color(oe_group, "Odd")) new_html += create_bar("Even", even_money_counts["Even"], total_spins, get_rank_color(oe_group, "Even")) new_html += '
' # Low vs High lh_group = {"Low": even_money_counts["Low"], "High": even_money_counts["High"]} new_html += '
' new_html += create_bar("Low", even_money_counts["Low"], total_spins, get_rank_color(lh_group, "Low")) new_html += create_bar("High", even_money_counts["High"], total_spins, get_rank_color(lh_group, "High")) new_html += '
' new_html += '
' # Chart 2: Columns new_html += '
' new_html += '

Columns

' new_html += '
' for name in ["1st Column", "2nd Column", "3rd Column"]: new_html += create_bar(name.split()[0], column_counts[name], total_spins, get_rank_color(column_counts, name)) new_html += '
' # Chart 3: Dozens new_html += '
' new_html += '

Dozens

' new_html += '
' for name in ["1st Dozen", "2nd Dozen", "3rd Dozen"]: new_html += create_bar(name.split()[0], dozen_counts[name], total_spins, get_rank_color(dozen_counts, name)) new_html += '
' new_html += '
' # End charts-container # Legend new_html += """
Hottest
2nd Hot
Coldest
""" new_html += '
' # End charts-section # Return concatenated HTML (Old + Separator + New) return old_html + new_html except Exception as e: print(f"calculate_hit_percentages: Error: {str(e)}") return "

Error calculating hit percentages.

" # Updated function with debug log DEBUG = True # Keep debugging enabled def summarize_spin_traits(last_spin_count): """Summarize traits for the last X spins as HTML badges, highlighting winners, hot streaks, and chopping patterns.""" try: if DEBUG: print(f"summarize_spin_traits: last_spin_count = {last_spin_count}") # Validate and clamp last_spin_count last_spin_count = int(last_spin_count) if last_spin_count is not None else 36 last_spin_count = max(1, min(last_spin_count, 36)) if DEBUG: print(f"summarize_spin_traits: After clamping, last_spin_count = {last_spin_count}") # Validate state if not hasattr(state, 'last_spins') or not isinstance(state.last_spins, list): if DEBUG: print(f"summarize_spin_traits: Invalid state.last_spins") return "

Error: Spin data not initialized.

" last_spins = state.last_spins[-last_spin_count:] if state.last_spins else [] if DEBUG: print(f"summarize_spin_traits: last_spins = {last_spins}") if not last_spins: return "

No spins available for analysis.

" # Validate bet mappings if not all(x in globals() for x in ['EVEN_MONEY', 'COLUMNS', 'DOZENS']): missing = [x for x in ['EVEN_MONEY', 'COLUMNS', 'DOZENS'] if x not in globals()] if DEBUG: print(f"summarize_spin_traits: Missing bet mappings: {missing}") return "

Error: Bet mappings not defined.

" # Validate EVEN_MONEY mappings for Red and Black if "Red" not in EVEN_MONEY or "Black" not in EVEN_MONEY: if DEBUG: print(f"summarize_spin_traits: EVEN_MONEY missing Red or Black mappings") return "

Error: EVEN_MONEY mappings incomplete.

" # Initialize counters and streaks even_money_counts = {"Red": 0, "Black": 0, "Even": 0, "Odd": 0, "Low": 0, "High": 0} column_counts = {"1st Column": 0, "2nd Column": 0, "3rd Column": 0} dozen_counts = {"1st Dozen": 0, "2nd Dozen": 0, "3rd Dozen": 0} number_counts = {} even_money_streaks = {key: {"current": 0, "max": 0, "last_hit": False, "spins": []} for key in even_money_counts} column_streaks = {key: {"current": 0, "max": 0, "last_hit": False, "spins": []} for key in column_counts} dozen_streaks = {key: {"current": 0, "max": 0, "last_hit": False, "spins": []} for key in dozen_counts} if DEBUG: print(f"summarize_spin_traits: Initialized counters and streaks") # Analyze spins for idx, spin in enumerate(last_spins): if DEBUG: print(f"summarize_spin_traits: Processing spin {idx}: {spin}") try: num = int(spin) if DEBUG: print(f"summarize_spin_traits: Converted spin to integer: {num}") # Reset last_hit flags for key in even_money_streaks: even_money_streaks[key]["last_hit"] = False for key in column_streaks: column_streaks[key]["last_hit"] = False for key in dozen_streaks: dozen_streaks[key]["last_hit"] = False if DEBUG: print(f"summarize_spin_traits: Reset last_hit flags for spin {num}") # Even Money Bets for name, numbers in EVEN_MONEY.items(): if num in numbers: even_money_counts[name] += 1 even_money_streaks[name]["last_hit"] = True even_money_streaks[name]["current"] += 1 even_money_streaks[name]["spins"].append(str(num)) if len(even_money_streaks[name]["spins"]) > even_money_streaks[name]["current"]: even_money_streaks[name]["spins"] = even_money_streaks[name]["spins"][-even_money_streaks[name]["current"]:] even_money_streaks[name]["max"] = max(even_money_streaks[name]["max"], even_money_streaks[name]["current"]) else: if not even_money_streaks[name]["last_hit"]: even_money_streaks[name]["current"] = 0 even_money_streaks[name]["spins"] = [] if DEBUG: print(f"summarize_spin_traits: Processed Even Money Bets for spin {num}") # Columns for name, numbers in COLUMNS.items(): if num in numbers: column_counts[name] += 1 column_streaks[name]["last_hit"] = True column_streaks[name]["current"] += 1 column_streaks[name]["spins"].append(str(num)) if len(column_streaks[name]["spins"]) > column_streaks[name]["current"]: column_streaks[name]["spins"] = column_streaks[name]["spins"][-column_streaks[name]["current"]:] column_streaks[name]["max"] = max(column_streaks[name]["max"], column_streaks[name]["current"]) else: if not column_streaks[name]["last_hit"]: column_streaks[name]["current"] = 0 column_streaks[name]["spins"] = [] if DEBUG: print(f"summarize_spin_traits: Processed Columns for spin {num}") # Dozens for name, numbers in DOZENS.items(): if num in numbers: dozen_counts[name] += 1 dozen_streaks[name]["last_hit"] = True dozen_streaks[name]["current"] += 1 dozen_streaks[name]["spins"].append(str(num)) if len(dozen_streaks[name]["spins"]) > dozen_streaks[name]["current"]: dozen_streaks[name]["spins"] = dozen_streaks[name]["spins"][-dozen_streaks[name]["current"]:] dozen_streaks[name]["max"] = max(dozen_streaks[name]["max"], dozen_streaks[name]["current"]) else: if not dozen_streaks[name]["last_hit"]: dozen_streaks[name]["current"] = 0 dozen_streaks[name]["spins"] = [] if DEBUG: print(f"summarize_spin_traits: Processed Dozens for spin {num}") number_counts[num] = number_counts.get(num, 0) + 1 if DEBUG: print(f"summarize_spin_traits: Processed Repeat Numbers for spin {num}") except ValueError as ve: if DEBUG: print(f"summarize_spin_traits: ValueError converting spin {spin} to integer: {str(ve)}") continue # Calculate max counts if DEBUG: print(f"summarize_spin_traits: Calculating max counts") max_even_money = max(even_money_counts.values()) if even_money_counts else 0 max_columns = max(column_counts.values()) if column_counts else 0 max_dozens = max(dozen_counts.values()) if dozen_counts else 0 if DEBUG: print(f"summarize_spin_traits: Max counts - Even Money: {max_even_money}, Columns: {max_columns}, Dozens: {max_dozens}") # Quick Trends and Betting Suggestions if DEBUG: print(f"summarize_spin_traits: Calculating Quick Trends") total_spins = len(last_spins) trends = [] suggestions = [] if total_spins > 0: all_counts = {**even_money_counts, **column_counts, **dozen_counts} dominant = max(all_counts.items(), key=lambda x: x[1], default=("None", 0)) if dominant[1] > 0: percentage = (dominant[1] / total_spins * 100) trends.append(("hot", f"{dominant[0]} dominates with {percentage:.1f}% hits")) # Add suggestion for dominant trait if percentage >= 40: # Suggest only if hit rate is significant suggestions.append(f"Bet on {dominant[0]} - {percentage:.1f}% hit rate in last {total_spins} spins!") all_streaks = {**even_money_streaks, **column_streaks, **dozen_streaks} longest_streak = max((v["current"] for v in all_streaks.values() if v["current"] > 1), default=0) if longest_streak > 1: streak_name = next(k for k, v in all_streaks.items() if v["current"] == longest_streak) streak_spins = ", ".join(all_streaks[streak_name]["spins"][-longest_streak:]) trends.append(("streak", f"{streak_name} on a {longest_streak}-spin streak (Spins: {streak_spins})")) # Add suggestion for streak if longest_streak >= 3: # Suggest for significant streaks suggestions.append(f"{streak_name} is hot - {longest_streak}/{total_spins} hits!") # Add cold trend for least hit trait least_hit = min(all_counts.items(), key=lambda x: x[1], default=("None", 0)) if least_hit[1] == 0 and least_hit[0] != "None": trends.append(("cold", f"{least_hit[0]} has no hits")) if DEBUG: print(f"summarize_spin_traits: Quick Trends calculated: {trends}, Suggestions: {suggestions}") # Calculate Red/Black Switches (Suggestion 9) switch_count = 0 switch_dots = [] recent_spins = last_spins[-6:] if len(last_spins) >= 6 else last_spins if DEBUG: print(f"summarize_spin_traits: Recent spins for switch alert: {recent_spins}") for i, spin in enumerate(recent_spins): try: num = int(spin) color = "green" if num == 0 else \ "red" if num in EVEN_MONEY["Red"] else \ "black" if num in EVEN_MONEY["Black"] else "unknown" switch_dots.append(color) if i > 0 and color != "green" and switch_dots[i-1] != "green" and color != switch_dots[i-1]: switch_count += 1 except ValueError: switch_dots.append("unknown") switch_class = " high-switches" if switch_count >= 4 else "" if DEBUG: print(f"summarize_spin_traits: Red/Black Switches: {switch_count}, Dots: {switch_dots}") # Calculate Dozen Shifts (Suggestion 10) dozen_counts_prev = {"1st Dozen": 0, "2nd Dozen": 0, "3rd Dozen": 0} dozen_counts_current = {"1st Dozen": 0, "2nd Dozen": 0, "3rd Dozen": 0} prev_spins = last_spins[-10:-5] if len(last_spins) >= 10 else last_spins[:5] if len(last_spins) >= 5 else [] current_spins = last_spins[-5:] if len(last_spins) >= 5 else last_spins for spin in prev_spins: try: num = int(spin) for name, numbers in DOZENS.items(): if num in numbers: dozen_counts_prev[name] += 1 except ValueError: continue for spin in current_spins: try: num = int(spin) for name, numbers in DOZENS.items(): if num in numbers: dozen_counts_current[name] += 1 except ValueError: continue dozen_shifts = {name: dozen_counts_current[name] - dozen_counts_prev[name] for name in dozen_counts} max_shift = max(dozen_shifts.values(), default=0) dominant_dozen = None dozen_class = "" if max_shift > 0: dominant_dozen = next(name for name, shift in dozen_shifts.items() if shift == max_shift) dozen_class = "d1" if dominant_dozen == "1st Dozen" else "d2" if dominant_dozen == "2nd Dozen" else "d3" if DEBUG: print(f"summarize_spin_traits: Dozen Shifts - Previous: {dozen_counts_prev}, Current: {dozen_counts_current}, Shifts: {dozen_shifts}, Dominant: {dominant_dozen}") # Build HTML if DEBUG: print(f"summarize_spin_traits: Building HTML") html = '
' html += f'

SpinTrend Radar (Last {len(last_spins)} Spins):

' html += '
' html += '' if DEBUG: print(f"summarize_spin_traits: Quick Trends, Switch Alert, and Dozen Shift Indicator HTML generated") # Even Money Bets html += '
' html += '

Even Money Bets

' html += '
' for name, count in even_money_counts.items(): badge_class = "trait-badge even-money winner" if count == max_even_money and max_even_money > 0 else "trait-badge even-money" streak = even_money_streaks[name]["max"] streak_title = f"{name} Hot Streak: {streak} consecutive hits" if streak >= 3 else "" percentage = (count / total_spins * 100) if total_spins > 0 else 0 bar_color = "#b71c1c" if name in ["Red", "Even", "Low"] else "#000000" if name in ["Black", "Odd", "High"] else "#666" html += f'
{name}: {count}
' html += '
' if DEBUG: print(f"summarize_spin_traits: Even Money Bets HTML generated") # Columns html += '
' html += '

Columns

' html += '
' for name, count in column_counts.items(): badge_class = "trait-badge column winner" if count == max_columns and max_columns > 0 else "trait-badge column" streak = column_streaks[name]["max"] streak_title = f"{name} Hot Streak: {streak} consecutive hits" if streak >= 3 else "" percentage = (count / total_spins * 100) if total_spins > 0 else 0 bar_color = "#1565c0" html += f'
{name}: {count}
' html += '
' if DEBUG: print(f"summarize_spin_traits: Columns HTML generated") # Dozens html += '
' html += '

Dozens

' html += '
' for name, count in dozen_counts.items(): badge_class = "trait-badge dozen winner" if count == max_dozens and max_dozens > 0 else "trait-badge dozen" streak = dozen_streaks[name]["max"] streak_title = f"{name} Hot Streak: {streak} consecutive hits" if streak >= 3 else "" percentage = (count / total_spins * 100) if total_spins > 0 else 0 bar_color = "#388e3c" html += f'
{name}: {count}
' html += '
' if DEBUG: print(f"summarize_spin_traits: Dozens HTML generated") # Repeat Numbers html += '
' html += '

Repeat Numbers

' html += '
' repeats = {num: count for num, count in number_counts.items() if count > 1} if repeats: for num, count in sorted(repeats.items()): html += f'{num}: {count} hits' else: html += 'No repeats' html += '
' html += '
' # Close traits-wrapper and traits-overview if DEBUG: print(f"summarize_spin_traits: Repeat Numbers HTML generated") if DEBUG: print(f"summarize_spin_traits: Returning HTML successfully") return html except Exception as e: if DEBUG: print(f"summarize_spin_traits: Caught exception: {str(e)}") raise # Re-raise to see the full stack trace in logs return "

Error analyzing spin traits.

" def cache_analysis(spins, last_spin_count): """Cache the results of summarize_spin_traits to avoid redundant calculations.""" spins_list = state.last_spins if hasattr(state, 'last_spins') else [] if not spins_list and isinstance(spins, str) and spins.strip(): spins_list = [s.strip() for s in spins.split(",") if s.strip()] cache_key = f"{last_spin_count}_{hash(tuple(spins_list))}" if cache_key in state.analysis_cache: if DEBUG: print(f"cache_analysis: Cache hit for key {cache_key}") return state.analysis_cache[cache_key] # Limit cache size MAX_CACHE_SIZE = 100 if len(state.analysis_cache) >= MAX_CACHE_SIZE: oldest_key = next(iter(state.analysis_cache)) del state.analysis_cache[oldest_key] if DEBUG: print(f"cache_analysis: Removed oldest cache entry {oldest_key}") # Perform analysis result = summarize_spin_traits(last_spin_count) state.analysis_cache[cache_key] = result if DEBUG: print(f"cache_analysis: Cached result for key {cache_key}") return result def select_next_spin_top_pick(last_spin_count, trait_filter=None, trait_match_weight=100, secondary_match_weight=10, wheel_side_weight=5, section_weight=10, recency_weight=1, hit_bonus_weight=5, neighbor_weight=2): try: last_spin_count = int(last_spin_count) if last_spin_count is not None else 18 last_spin_count = max(1, min(last_spin_count, 36)) last_spins = state.last_spins[-last_spin_count:] if state.last_spins else [] if not last_spins: return "

No spins available for analysis.

" # Log the spins being analyzed print(f"Analyzing last {last_spin_count} spins: {last_spins}") numbers = set(range(37)) hit_counts = {n: 0 for n in range(37)} last_positions = {n: -1 for n in range(37)} for i, spin in enumerate(last_spins): try: num = int(spin) hit_counts[num] += 1 last_positions[num] = i except ValueError: continue # Default to all traits if none specified or empty if trait_filter is None or not trait_filter: trait_filter = ["Red/Black", "Even/Odd", "Low/High", "Dozens", "Columns", "Wheel Sections", "Neighbors"] even_money_counts = {"Red": 0, "Black": 0, "Even": 0, "Odd": 0, "Low": 0, "High": 0} column_counts = {"1st Column": 0, "2nd Column": 0, "3rd Column": 0} dozen_counts = {"1st Dozen": 0, "2nd Dozen": 0, "3rd Dozen": 0} for spin in last_spins: try: num = int(spin) if "Red/Black" in trait_filter: if num in EVEN_MONEY["Red"]: even_money_counts["Red"] += 1 elif num in EVEN_MONEY["Black"]: even_money_counts["Black"] += 1 if "Even/Odd" in trait_filter: if num in EVEN_MONEY["Even"]: even_money_counts["Even"] += 1 elif num in EVEN_MONEY["Odd"]: even_money_counts["Odd"] += 1 if "Low/High" in trait_filter: if num in EVEN_MONEY["Low"]: even_money_counts["Low"] += 1 elif num in EVEN_MONEY["High"]: even_money_counts["High"] += 1 if "Dozens" in trait_filter: for name, nums in DOZENS.items(): if num in nums: dozen_counts[name] += 1 if "Columns" in trait_filter: for name, nums in COLUMNS.items(): if num in nums: column_counts[name] += 1 except ValueError: continue # Calculate percentages for included traits total_spins = len(last_spins) trait_percentages = {} if "Red/Black" in trait_filter: for trait in ["Red", "Black"]: trait_percentages[trait] = (even_money_counts[trait] / total_spins) * 100 if total_spins > 0 else 0 if "Even/Odd" in trait_filter: for trait in ["Even", "Odd"]: trait_percentages[trait] = (even_money_counts[trait] / total_spins) * 100 if total_spins > 0 else 0 if "Low/High" in trait_filter: for trait in ["Low", "High"]: trait_percentages[trait] = (even_money_counts[trait] / total_spins) * 100 if total_spins > 0 else 0 if "Dozens" in trait_filter: for trait in dozen_counts: trait_percentages[trait] = (dozen_counts[trait] / total_spins) * 100 if total_spins > 0 else 0 if "Columns" in trait_filter: for trait in column_counts: trait_percentages[trait] = (column_counts[trait] / total_spins) * 100 if total_spins > 0 else 0 # Sort traits by percentage (highest to lowest) sorted_traits = sorted(trait_percentages.items(), key=lambda x: (-x[1], x[0])) # Determine hottest traits (top non-conflicting traits) hottest_traits = [] seen_categories = set() for trait, percentage in sorted_traits: if trait in ["Red", "Black"]: if "Red-Black" in seen_categories: continue hottest_traits.append(trait) seen_categories.add("Red-Black") elif trait in ["Even", "Odd"]: if "Even-Odd" in seen_categories: continue hottest_traits.append(trait) seen_categories.add("Even-Odd") elif trait in ["Low", "High"]: if "Low-High" in seen_categories: continue hottest_traits.append(trait) seen_categories.add("Low-High") elif trait in ["1st Dozen", "2nd Dozen", "3rd Dozen"]: if "Dozens" in seen_categories: continue hottest_traits.append(trait) seen_categories.add("Dozens") elif trait in ["1st Column", "2nd Column", "3rd Column"]: if "Columns" in seen_categories: continue hottest_traits.append(trait) seen_categories.add("Columns") # Second best traits for tiebreakers second_best_traits = [] seen_categories = set() for trait, percentage in sorted_traits: if trait in hottest_traits: continue if trait in ["Red", "Black"]: if "Red-Black" in seen_categories: continue second_best_traits.append(trait) seen_categories.add("Red-Black") elif trait in ["Even", "Odd"]: if "Even-Odd" in seen_categories: continue second_best_traits.append(trait) seen_categories.add("Even-Odd") elif trait in ["Low", "High"]: if "Low-High" in seen_categories: continue second_best_traits.append(trait) seen_categories.add("Low-High") elif trait in ["1st Dozen", "2nd Dozen", "3rd Dozen"]: if "Dozens" in seen_categories: continue second_best_traits.append(trait) seen_categories.add("Dozens") elif trait in ["1st Column", "2nd Column", "3rd Column"]: if "Columns" in seen_categories: continue second_best_traits.append(trait) seen_categories.add("Columns") # Wheel side analysis (only if included) left_side = set(LEFT_OF_ZERO_EUROPEAN) right_side = set(RIGHT_OF_ZERO_EUROPEAN) left_hits = 0 right_hits = 0 if "Wheel Sections" in trait_filter: left_hits = sum(hit_counts[num] for num in left_side) right_hits = sum(hit_counts[num] for num in right_side) most_hit_side = "Left" if left_hits > right_hits else "Right" if right_hits > left_hits else "Both" betting_sections = { "Voisins du Zero": [22, 18, 29, 7, 28, 12, 35, 3, 26, 0, 32, 15, 19, 4, 21, 2, 25], "Orphelins": [17, 34, 6, 1, 20, 14, 31, 9], "Tiers du Cylindre": [27, 13, 36, 11, 30, 8, 23, 10, 5, 24, 16, 33] } section_hits = {name: 0 for name in betting_sections} section_last_pos = {name: -1 for name in betting_sections} if "Wheel Sections" in trait_filter: section_hits = {name: sum(hit_counts[num] for num in nums) for name, nums in betting_sections.items()} for name, nums in betting_sections.items(): for num in nums: if last_positions[num] > section_last_pos[name]: section_last_pos[name] = last_positions[num] sorted_sections = sorted(section_hits.items(), key=lambda x: (-x[1], -section_last_pos[x[0]])) top_section = sorted_sections[0][0] if sorted_sections and "Wheel Sections" in trait_filter else None neighbor_boost = {num: 0 for num in range(37)} if "Neighbors" in trait_filter: last_five = last_spins[-5:] if len(last_spins) >= 5 else last_spins last_five_set = set(last_five) for num in range(37): if num in NEIGHBORS_EUROPEAN: left, right = NEIGHBORS_EUROPEAN[num] if left is not None and str(left) in last_five_set: neighbor_boost[num] += 2 if right is not None and str(right) in last_five_set: neighbor_boost[num] += 2 # Score numbers based on the number of matching traits in order scores = [] for num in range(37): if num not in hit_counts or hit_counts[num] == 0: continue # Only consider numbers that appear in the spins # Count matching traits in order matching_traits = 0 for trait in hottest_traits: if trait in EVEN_MONEY and num in EVEN_MONEY[trait]: matching_traits += 1 elif trait in DOZENS and num in DOZENS[trait]: matching_traits += 1 elif trait in COLUMNS and num in COLUMNS[trait]: matching_traits += 1 # Secondary score for second best traits secondary_matches = 0 for trait in second_best_traits: if trait in EVEN_MONEY and num in EVEN_MONEY[trait]: secondary_matches += 1 elif trait in DOZENS and num in DOZENS[trait]: secondary_matches += 1 elif trait in COLUMNS and num in COLUMNS[trait]: secondary_matches += 1 # Additional scoring factors wheel_side_score = 0 if "Wheel Sections" in trait_filter: if most_hit_side == "Both" or (most_hit_side == "Left" and num in left_side) or (most_hit_side == "Right" and num in right_side): wheel_side_score = 1 # Will be scaled by weight section_score = 1 if top_section and num in betting_sections.get(top_section, []) else 0 recency_score = (last_spin_count - (last_positions[num] + 1)) if last_positions[num] >= 0 else 0 if last_positions[num] == last_spin_count - 1: recency_score = max(recency_score, 10) hit_bonus = 1 if hit_counts[num] > 0 else 0 neighbor_score = neighbor_boost[num] if "Neighbors" in trait_filter else 0 tiebreaker_score = 0 if num == 0: pass else: if num in EVEN_MONEY["Red"] and "Red/Black" in trait_filter: tiebreaker_score += even_money_counts["Red"] elif num in EVEN_MONEY["Black"] and "Red/Black" in trait_filter: tiebreaker_score += even_money_counts["Black"] if num in EVEN_MONEY["Even"] and "Even/Odd" in trait_filter: tiebreaker_score += even_money_counts["Even"] elif num in EVEN_MONEY["Odd"] and "Even/Odd" in trait_filter: tiebreaker_score += even_money_counts["Odd"] if num in EVEN_MONEY["Low"] and "Low/High" in trait_filter: tiebreaker_score += even_money_counts["Low"] elif num in EVEN_MONEY["High"] and "Low/High" in trait_filter: tiebreaker_score += even_money_counts["High"] if "Dozens" in trait_filter: for name, nums in DOZENS.items(): if num in nums: tiebreaker_score += dozen_counts[name] break if "Columns" in trait_filter: for name, nums in COLUMNS.items(): if num in nums: tiebreaker_score += column_counts[name] break # Apply configurable weights total_score = ( matching_traits * trait_match_weight + secondary_matches * secondary_match_weight + wheel_side_score * wheel_side_weight + section_score * section_weight + recency_score * recency_weight + hit_bonus * hit_bonus_weight + neighbor_score * neighbor_weight ) scores.append((num, total_score, matching_traits, secondary_matches, wheel_side_score, section_score, recency_score, hit_bonus, neighbor_score, tiebreaker_score)) # Sort by number of matching traits, then secondary matches, then tiebreaker, then recency scores.sort(key=lambda x: (-x[2], -x[3], -x[9], -x[6], -x[0])) # Ensure top 10 picks have at least as many matches as the 10th pick if len(scores) > 10: min_traits = sorted([x[2] for x in scores[:10]], reverse=True)[9] top_picks = [x for x in scores if x[2] >= min_traits][:10] else: top_picks = scores[:10] state.current_top_pick = top_picks[0][0] top_pick = top_picks[0][0] # Calculate confidence based on matching traits max_possible_traits = len(hottest_traits) top_traits_matched = top_picks[0][2] confidence = max(0, min(100, int((top_traits_matched / max_possible_traits) * 100))) if max_possible_traits > 0 else 0 characteristics = [] top_pick_int = int(top_pick) if top_pick_int == 0: characteristics.append("Green") elif "Red" in EVEN_MONEY and top_pick_int in EVEN_MONEY["Red"] and "Red/Black" in trait_filter: characteristics.append("Red") elif "Black" in EVEN_MONEY and top_pick_int in EVEN_MONEY["Black"] and "Red/Black" in trait_filter: characteristics.append("Black") if top_pick_int != 0: if "Even" in EVEN_MONEY and top_pick_int in EVEN_MONEY["Even"] and "Even/Odd" in trait_filter: characteristics.append("Even") elif "Odd" in EVEN_MONEY and top_pick_int in EVEN_MONEY["Odd"] and "Even/Odd" in trait_filter: characteristics.append("Odd") if "Low" in EVEN_MONEY and top_pick_int in EVEN_MONEY["Low"] and "Low/High" in trait_filter: characteristics.append("Low") elif "High" in EVEN_MONEY and top_pick_int in EVEN_MONEY["High"] and "Low/High" in trait_filter: characteristics.append("High") if "Dozens" in trait_filter: for name, nums in DOZENS.items(): if top_pick_int in nums: characteristics.append(name) break if "Columns" in trait_filter: for name, nums in COLUMNS.items(): if top_pick_int in nums: characteristics.append(name) break characteristics_str = ", ".join(characteristics) if characteristics else "No notable characteristics" color = colors.get(str(top_pick), "black") _, total_score, matching_traits, secondary_matches, wheel_side_score, section_score, recency_score, hit_bonus, neighbor_score, tiebreaker_score = top_picks[0] reasons = [] matched_traits = [] for trait in hottest_traits: if trait in EVEN_MONEY and top_pick in EVEN_MONEY[trait]: matched_traits.append(trait) elif trait in DOZENS and top_pick in DOZENS[trait]: matched_traits.append(trait) elif trait in COLUMNS and top_pick in COLUMNS[trait]: matched_traits.append(trait) if matched_traits: reasons.append(f"Matches the hottest traits: {', '.join(matched_traits)} (weight: {trait_match_weight})") if section_score > 0 and "Wheel Sections" in trait_filter: reasons.append(f"Located in the hottest wheel section: {top_section} (weight: {section_weight})") if recency_score > 0: last_pos = last_positions[top_pick] reasons.append(f"Recently appeared in the spin history (position {last_pos}) (weight: {recency_weight})") if hit_bonus > 0: reasons.append(f"Has appeared in the spin history (weight: {hit_bonus_weight})") if wheel_side_score > 0 and "Wheel Sections" in trait_filter: reasons.append(f"On the most hit side of the wheel: {most_hit_side} (weight: {wheel_side_weight})") if neighbor_score > 0 and "Neighbors" in trait_filter: neighbors_hit = [str(n) for n in NEIGHBORS_EUROPEAN.get(top_pick, (None, None)) if str(n) in last_five_set] reasons.append(f"Has recent neighbors in the last 5 spins: {', '.join(neighbors_hit)} (weight: {neighbor_weight})") if tiebreaker_score > 0: reasons.append(f"Boosted by aggregated trait scores (tiebreaker: {tiebreaker_score})") reasons_html = "" if reasons else "

No specific reasons available.

" last_five_spins = last_spins[-5:] if len(last_spins) >= 5 else last_spins last_five_spins_html = "" for spin in last_five_spins: spin_color = colors.get(str(spin), "black") last_five_spins_html += f'{spin}' top_5_html = "" for i, (num, total_score, matching_traits, secondary_matches, wheel_side_score, section_score, recency_score, hit_bonus, neighbor_score, tiebreaker_score) in enumerate(top_picks[1:10], 1): num_color = colors.get(str(num), "black") num_characteristics = [] if num == 0: num_characteristics.append("Green") elif "Red" in EVEN_MONEY and num in EVEN_MONEY["Red"] and "Red/Black" in trait_filter: num_characteristics.append("Red") elif "Black" in EVEN_MONEY and num in EVEN_MONEY["Black"] and "Red/Black" in trait_filter: num_characteristics.append("Black") if num != 0: if "Even" in EVEN_MONEY and num in EVEN_MONEY["Even"] and "Even/Odd" in trait_filter: num_characteristics.append("Even") elif "Odd" in EVEN_MONEY and num in EVEN_MONEY["Odd"] and "Even/Odd" in trait_filter: num_characteristics.append("Odd") if "Low" in EVEN_MONEY and num in EVEN_MONEY["Low"] and "Low/High" in trait_filter: num_characteristics.append("Low") elif "High" in EVEN_MONEY and num in EVEN_MONEY["High"] and "Low/High" in trait_filter: num_characteristics.append("High") if "Dozens" in trait_filter: for name, nums in DOZENS.items(): if num in nums: num_characteristics.append(name) break if "Columns" in trait_filter: for name, nums in COLUMNS.items(): if num in nums: num_characteristics.append(name) break num_characteristics_str = ", ".join(num_characteristics) if num_characteristics else "No notable characteristics" num_reasons = [] num_matched_traits = [] for trait in hottest_traits: if trait in EVEN_MONEY and num in EVEN_MONEY[trait]: num_matched_traits.append(trait) elif trait in DOZENS and num in DOZENS[trait]: num_matched_traits.append(trait) elif trait in COLUMNS and num in COLUMNS[trait]: num_matched_traits.append(trait) if num_matched_traits: num_reasons.append(f"Matches: {', '.join(num_matched_traits)}") if "Wheel Sections" in trait_filter: for section_name, nums in betting_sections.items(): if num in nums: num_reasons.append(f"In {section_name}") break if tiebreaker_score > 0: num_reasons.append(f"Tiebreaker: {tiebreaker_score}") num_reasons_str = ", ".join(num_reasons) if num_reasons else "No notable reasons" top_5_html += f'''
{num}
{''.join(f'{char}' for char in num_characteristics_str.split(", "))}
{num_reasons_str}
''' html = f'''
Last 5 Spins
{last_five_spins_html}

Top Pick for Next Spin

{top_pick}
{''.join(f'{char}' for char in characteristics_str.split(", "))}
Confidence: {confidence}%

Based on analysis of the last {last_spin_count} spins.

{reasons_html}
Other Top Picks
{top_5_html}
''' return html except Exception as e: print(f"select_next_spin_top_pick: Error: {str(e)}") return "

Error selecting top pick.

" # ------------------------------------------------------------------- # DE2D TRACKER LOGIC (TITANIUM v10.11: FIX VARIABLE SCOPE ERROR) # ------------------------------------------------------------------- def de2d_tracker_logic(miss_threshold=11, even_threshold=10, streak_threshold=9, pattern_x=8, voisins_threshold=8, tiers_threshold=9, left_threshold=7, right_threshold=7, ds_threshold=4, d17_threshold=6, corner_threshold=6, grind_active=False, grind_target="3rd Dozen"): """ Logic for the DE2D Tracker section. Version: TITANIUM v10.11. Updates: 1. CRITICAL FIX: Moved 'max_corner_miss' calculation up to prevent 'referenced before assignment' error. 2. Maintains all previous features (Spotlight, Flames, On Deck Radar). """ try: # ========================================================= # 1. SELF-HEALING STATE INITIALIZATION # ========================================================= if not hasattr(state, 'grind_step_index'): state.grind_step_index = 0 if not hasattr(state, 'grind_last_spin_count'): state.grind_last_spin_count = 0 if not hasattr(state, 'd17_list'): state.d17_list = [] if not hasattr(state, 'd17_locked'): state.d17_locked = False if not hasattr(state, 'last_spins') or state.last_spins is None: state.last_spins = [] # ========================================================= # 2. VARIABLE INITIALIZATION # ========================================================= config_html = "" spins_html = "" actions_section = "" visual_table = "" sequences_info_html = "" active_actions = [] highlight_targets = set() grind_targets = set() active_target_groups = [] on_deck_triggers = [] # List for triggers 1 spin away # ========================================================= # 3. CSS STYLES # ========================================================= inactive_style = "background-color: #34495e; color: #bdc3c7; font-size: 10px; border: 1px solid #2c3e50;" active_style = "background-color: #FFD700; color: #000000; font-weight: 900; font-size: 11px; border: 2px solid #FFC107; box-shadow: 0 0 5px #FFD700;" grind_style = "background-color: #2ecc71; color: white; font-weight: bold; font-size: 10px; border: 1px solid #27ae60;" rank1_style = "border: 3px solid #FFFF00 !important; box-shadow: 0 0 15px #FFFF00 !important; z-index: 10;" rank2_style = "border: 2px solid #00FFFF !important; box-shadow: 0 0 10px #00FFFF !important; z-index: 5;" rank3_style = "border: 2px solid #32CD32 !important; box-shadow: 0 0 8px #32CD32 !important;" # ========================================================= # 4. HELPER FUNCTIONS # ========================================================= def parse_arg(val, default): try: return int(val) if val is not None else default except: return default def get_status_style(is_active): if is_active: return "background: linear-gradient(135deg, #d32f2f, #ff5252); color: white; font-weight: bold; border: 2px solid #ff8a80; box-shadow: 0 0 10px rgba(255, 82, 82, 0.7); animation: pulse-red 1.5s infinite;" return "background: #f5f5f5; color: #777; border: 1px solid #ddd;" def get_counter_color(curr, thresh): if curr >= thresh: return "color: #fff; font-weight: bold; text-shadow: 0 0 2px black;" if curr >= thresh - 1: return "color: #d32f2f; font-weight: bold;" if curr >= thresh - 2: return "color: #f57c00; font-weight: bold;" return "color: #333;" def get_progress_bar_html(current, threshold): if threshold <= 0: threshold = 1 pct = min(100, (current / threshold) * 100) bar_color = "#4CAF50" if pct >= 50: bar_color = "#FFC107" if pct >= 80: bar_color = "#FF5722" if pct >= 100: bar_color = "#D32F2F" if pct >= 100: bar_color = "#fff" return f"""
""" def format_seq(data_list): return " ".join([f"{item[0]}" for item in data_list]) def format_sequence_html(sequence, current_val, threshold): idx = current_val - threshold if idx < 0: idx = -1 if idx >= len(sequence): idx = len(sequence) - 1 html_parts = [] for i, val in enumerate(sequence): if i == idx: html_parts.append(f'{val}') else: html_parts.append(str(val)) return f"[{', '.join(html_parts)}]" def count_misses(target_set, spin_list, zero_is_miss=True): count = 0 for s in reversed(spin_list): if s in target_set: return count if s == 0 and not zero_is_miss: return count count += 1 return count def count_hits(target_set, spin_list): count = 0 for s in reversed(spin_list): if s in target_set: count += 1 elif s == 0: return count else: return count return count def count_hits_with_zero(target_set, spin_list): count = 0 for s in reversed(spin_list): if s in target_set or s == 0: count += 1 else: return count return count def count_frequency(target_set, spin_list): return sum(1 for s in spin_list if s in target_set) def get_header_color(title): if "MISSING" in title: return "#1976D2" if "STREAK" in title: return "#E64A19" if "EVEN" in title: return "#388E3C" if "PATTERN" in title: return "#7B1FA2" if "VOISINS" in title: return "#0097A7" if "TIERS" in title: return "#0288D1" if "SIDE" in title: return "#FBC02D"; if "5DS" in title: return "#C2185B" if "17" in title: return "#D32F2F" if "CORNER" in title: return "#FF9800" if "GRIND" in title: return "#2E7D32" return "#455A64" def get_text_color_for_header(title): if "SIDE" in title: return "#333" return "#FFF" def get_hottest_sector(spins): if not spins: return "3rd Dozen" d_counts = {d: count_frequency(set(nums), spins) for d, nums in DOZENS.items()} c_counts = {c: count_frequency(set(nums), spins) for c, nums in COLUMNS.items()} best_d = max(d_counts.items(), key=lambda x: x[1]) best_c = max(c_counts.items(), key=lambda x: x[1]) if best_d[1] == best_c[1]: return [best_d[0], best_c[0]] elif best_d[1] > best_c[1]: return best_d[0] else: return best_c[0] def get_grind_numbers(target): if isinstance(target, list): nums = set() for t in target: if t in DOZENS: nums.update(DOZENS[t]) elif t in COLUMNS: nums.update(COLUMNS[t]) return list(nums) if target in DOZENS: return DOZENS[target] elif target in COLUMNS: return COLUMNS[target] return [] def get_pattern_alert(spin_list, type="dozen"): mapped_tuples = [] for s in spin_list: cat = "0" if s == 0: cat = "0" elif type == "dozen": if s in DOZENS["1st Dozen"]: cat = "D1" elif s in DOZENS["2nd Dozen"]: cat = "D2" elif s in DOZENS["3rd Dozen"]: cat = "D3" else: if s in COLUMNS["1st Column"]: cat = "C1" elif s in COLUMNS["2nd Column"]: cat = "C2" elif s in COLUMNS["3rd Column"]: cat = "C3" mapped_tuples.append((cat, s)) categories = [t[0] for t in mapped_tuples] if len(categories) < pat_x + 10: return None target_pattern = categories[-pat_x:] search_limit = len(categories) - pat_x - pat_y match_idx = -1 for i in range(search_limit, -1, -1): if categories[i : i+pat_x] == target_pattern: match_idx = i break if match_idx != -1: return target_pattern, mapped_tuples[match_idx : match_idx+pat_x], mapped_tuples[match_idx+pat_x : match_idx+pat_x+pat_y] return None def generate_action_card(title, target, current_miss, wait_threshold, sequence, cost_per_unit, tooltip_nums=None, is_5ds=False, spots_override=None): step_index = current_miss - wait_threshold if step_index < 0: step_index = 0 if step_index >= len(sequence): step_index = len(sequence) - 1 unit_mult = sequence[step_index] # --- MATH CORRECTION FOR D17 --- if "17-NUMBER" in title: spots = 17 spot_name = "Num" p_unit = 0.01; d_unit = 0.10; D_unit = 1.00 p_total = unit_mult * p_unit; d_total = unit_mult * d_unit; D_total = unit_mult * D_unit p_per_spot = p_total / 17; d_per_spot = d_total / 17; D_per_spot = D_total / 17 else: if spots_override: spots = spots_override spot_name = "Line" if is_5ds else "Spot" else: spots = 1 spot_name = "Spot" if is_5ds: spots = 5; spot_name = "Line" elif "CORNER" in title: spots = 5; spot_name = "Crnr" elif "STREAK" in title: spots = 2; spot_name = "Doz" elif "SIDE" in title: spots = 25; spot_name = "Num" elif "VOISINS" in title: spots = 17; spot_name = "Num" elif "TIERS" in title: spots = 20; spot_name = "Num" p_unit = 0.01; p_per_spot = unit_mult * p_unit; p_total = p_per_spot * spots d_unit = 0.10; d_per_spot = unit_mult * d_unit; d_total = d_per_spot * spots D_unit = 1.00; D_per_spot = unit_mult * D_unit; D_total = D_per_spot * spots if "VOISINS" in title or "TIERS" in title or "SIDE" in title: p_total = unit_mult * p_unit * spots p_per_spot = unit_mult * p_unit d_total = unit_mult * d_unit * spots d_per_spot = unit_mult * d_unit D_total = unit_mult * D_unit * spots D_per_spot = unit_mult * D_unit mantras = ["Protect the Bankroll.", "Don't Chase Ghosts.", "Patience Pays.", "Sniper Mode On.", "Respect the Stop Loss.", "Stay Cool 🧊", "Trust the Math.", "Lock Profit Early."] import random daily_mantra = random.choice(mantras) header_bg = get_header_color(title) header_text = get_text_color_for_header(title) tooltip_html = "" if tooltip_nums: sorted_nums = sorted(list(tooltip_nums)) nums_str = ", ".join(map(str, sorted_nums)) tooltip_html = f'title="Cover: {nums_str}"' step_badge = f"STEP {step_index + 1}/{len(sequence)}" if "GRIND" in title: step_badge = f"STEP {current_miss + 1}/{len(sequence)}" seq_html = format_sequence_html(sequence, current_miss if "GRIND" in title else current_miss, wait_threshold if "GRIND" not in title else 0) extra_html = f"""
Progression Path:
{seq_html}
""" html = f"""
{title} {step_badge}
Target:
{target}
Unit Per {spot_name} Total Bet
${p_per_spot:.2f} ${p_total:.2f}
10¢ ${d_per_spot:.2f} ${d_total:.2f}
$1 ${D_per_spot:.2f} ${D_total:.2f}
{extra_html}
""" return html def get_label_style(target_name, highlight_targets_set, grind_targets_set, sector_ranks_dict): base = f"{inactive_style} opacity: 0.3;" is_active = target_name in highlight_targets_set is_grind = target_name in grind_targets_set rank = sector_ranks_dict.get(target_name, 0) rank_css = "" if rank == 1: rank_css = rank1_style elif rank == 2: rank_css = rank2_style elif rank == 3: rank_css = rank3_style if is_active: if rank == 1: base = "background-color: #ffd700; color: black !important; font-weight: 900 !important; font-size: 11px !important; border: 3px solid #FFD700 !important; box-shadow: 0 0 10px #FFD700 !important; opacity: 1;" else: base = active_style + " " + rank_css elif is_grind: base = grind_style + " " + rank_css else: base = f"{inactive_style} {rank_css} opacity: 0.5;" return base def render_row_cells(num_list, highlight_targets_set, grind_targets_set, hot_subset_set, colors_dict): cells = "" for n in num_list: c = colors_dict.get(str(n), "black") is_active = False; is_grind = False; is_hot = n in hot_subset_set if n != 0: if "1st Dozen" in highlight_targets_set and n in DOZENS["1st Dozen"]: is_active = True elif "2nd Dozen" in highlight_targets_set and n in DOZENS["2nd Dozen"]: is_active = True elif "3rd Dozen" in highlight_targets_set and n in DOZENS["3rd Dozen"]: is_active = True if "1st Column" in highlight_targets_set and n in COLUMNS["1st Column"]: is_active = True elif "2nd Column" in highlight_targets_set and n in COLUMNS["2nd Column"]: is_active = True elif "3rd Column" in highlight_targets_set and n in COLUMNS["3rd Column"]: is_active = True if "Red" in highlight_targets_set and n in EVEN_MONEY["Red"]: is_active = True elif "Black" in highlight_targets_set and n in EVEN_MONEY["Black"]: is_active = True elif "Even" in highlight_targets_set and n in EVEN_MONEY["Even"]: is_active = True elif "Odd" in highlight_targets_set and n in EVEN_MONEY["Odd"]: is_active = True elif "Low" in highlight_targets_set and n in EVEN_MONEY["Low"]: is_active = True elif "High" in highlight_targets_set and n in EVEN_MONEY["High"]: is_active = True if "Voisins" in highlight_targets_set and n in voisins_numbers: is_active = True if "TiersOrph" in highlight_targets_set and n in tiers_orph_numbers: is_active = True if "LeftSide" in highlight_targets_set and n in left_side_covered: is_active = True if "RightSide" in highlight_targets_set and n in right_side_covered: is_active = True if n in highlight_targets_set: is_active = True if not is_active: if "1st Dozen" in grind_targets_set and n in DOZENS["1st Dozen"]: is_grind = True elif "2nd Dozen" in grind_targets_set and n in DOZENS["2nd Dozen"]: is_grind = True elif "3rd Dozen" in grind_targets_set and n in DOZENS["3rd Dozen"]: is_grind = True if "1st Column" in grind_targets_set and n in COLUMNS["1st Column"]: is_grind = True elif "2nd Column" in grind_targets_set and n in COLUMNS["2nd Column"]: is_grind = True elif "3rd Column" in grind_targets_set and n in COLUMNS["3rd Column"]: is_grind = True flame_html = "" if is_hot: flame_html = '🔥' in_spotlight = is_active or is_grind or is_hot opacity = "1.0" if in_spotlight else "0.3" if is_active: base_style = active_style + (" border: 3px solid #ff3333; box-shadow: 0 0 10px #ff0000;" if is_hot else "") elif is_grind: base_style = grind_style + (" border: 3px solid #ff3333;" if is_hot else "") else: base_style = f"background-color:{c}; color:white;" + (f" border: 3px solid #ff3333; box-shadow: inset 0 0 5px #ff0000;" if is_hot else "") base_style += f" opacity: {opacity}; position: relative; overflow: visible;" cells += f'
{n}{flame_html}
' return cells # ========================================================= # 5. STATIC DATA DEFINITIONS # ========================================================= colors = { "0": "green", "1": "red", "3": "red", "5": "red", "7": "red", "9": "red", "12": "red", "14": "red", "16": "red", "18": "red", "19": "red", "21": "red", "23": "red", "25": "red", "27": "red", "30": "red", "32": "red", "34": "red", "36": "red", "2": "black", "4": "black", "6": "black", "8": "black", "10": "black", "11": "black", "13": "black", "15": "black", "17": "black", "20": "black", "22": "black", "24": "black", "26": "black", "28": "black", "29": "black", "31": "black", "33": "black", "35": "black" } voisins_numbers = [0, 2, 3, 4, 7, 12, 15, 18, 19, 21, 22, 25, 26, 28, 29, 32, 35] tiers_orph_numbers = [1, 5, 6, 8, 9, 10, 11, 13, 14, 16, 17, 20, 23, 24, 27, 30, 31, 33, 34, 36] left_side_covered = [1, 3, 5, 7, 9, 12, 14, 16, 18, 20, 22, 24, 27, 26, 25, 28, 29, 30, 33, 32, 31, 34, 35, 36, 0] left_uncovered = [2, 4, 6, 8, 10, 11, 13, 15, 17, 19, 21, 23] right_side_covered = [2, 4, 6, 8, 10, 11, 13, 15, 17, 19, 21, 23, 27, 26, 25, 28, 29, 30, 33, 32, 31, 34, 35, 36, 0] right_uncovered = [1, 3, 5, 7, 9, 12, 14, 16, 18, 20, 22, 24] double_streets = { "DS 1-6": [1, 2, 3, 4, 5, 6], "DS 4-9": [4, 5, 6, 7, 8, 9], "DS 7-12": [7, 8, 9, 10, 11, 12], "DS 10-15": [10, 11, 12, 13, 14, 15], "DS 13-18": [13, 14, 15, 16, 17, 18], "DS 16-21": [16, 17, 18, 19, 20, 21], "DS 19-24": [19, 20, 21, 22, 23, 24], "DS 22-27": [22, 23, 24, 25, 26, 27], "DS 25-30": [25, 26, 27, 28, 29, 30], "DS 28-33": [28, 29, 30, 31, 32, 33], "DS 31-36": [31, 32, 33, 34, 35, 36] } ghost_parents = { "DS 4-9": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], "DS 10-15": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], "DS 16-21": [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], "DS 22-27": [19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], "DS 28-33": [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36] } ds_ranges = { "DS 1-6": "1-6", "DS 4-9": "4-9", "DS 7-12": "7-12", "DS 10-15": "10-15", "DS 13-18": "13-18", "DS 16-21": "16-21", "DS 19-24": "19-24", "DS 22-27": "22-27", "DS 25-30": "25-30", "DS 28-33": "28-33", "DS 31-36": "31-36" } corner_templates = { "Standard": [[1, 2, 4, 5], [8, 9, 11, 12], [13, 14, 16, 17], [20, 21, 23, 24], [25, 26, 28, 29]], "Shifted": [[2, 3, 5, 6], [7, 8, 10, 11], [14, 15, 17, 18], [19, 20, 22, 23], [26, 27, 29, 30]], "High-Low": [[4, 5, 7, 8], [10, 11, 13, 14], [16, 17, 19, 20], [22, 23, 25, 26], [32, 33, 35, 36]] } seq_fib = [1, 1, 2, 3, 4, 6, 9, 14, 21, 31, 47, 70, 105, 158, 237] seq_mart = [1, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] seq_manual_grind = [1, 1, 1, 1, 2, 3, 5, 7, 11, 16, 24, 36, 54, 81, 122, 183, 274, 411, 617, 925, 1388, 2082, 3123, 4684, 7026, 10539, 15809, 23713, 35569, 53354] seq_trip = [1, 2, 6, 18, 54, 162, 487, 1461, 4382, 13146, 39438, 118314, 354942] seq_voisins = [1, 1, 2, 4, 8, 15, 28, 53, 101, 191, 362, 686, 1300, 2463, 4667, 8842] seq_tiers = [1, 2, 4, 9, 21, 47, 105, 237, 533, 1199] seq_sides = [1, 3, 10, 32, 105] seq_5ds = [1, 5, 30, 180, 1080, 6480, 38880, 233281, 1399685, 8398110] seq_d17 = [17, 34, 68, 136, 272, 544] seq_corners = [1, 2, 4, 9, 20, 45, 102, 229, 515, 1159, 2608, 5868, 13203, 29707, 66840, 150390] cost_dozen = 1; cost_even = 1; cost_streak = 2; cost_voisins = 17 cost_tiers = 20; cost_sides = 18; cost_5ds = 5; cost_d17 = 1; cost_corner = 5 cost_grind = 0.01 # ========================================================= # 6. PARSE ARGUMENTS & CALCULATE FLAGS # ========================================================= miss_wait = parse_arg(miss_threshold, 11) even_wait = parse_arg(even_threshold, 10) streak_wait = parse_arg(streak_threshold, 9) pat_x = parse_arg(pattern_x, 8) voisins_wait = parse_arg(voisins_threshold, 8) tiers_wait = parse_arg(tiers_threshold, 9) left_wait = parse_arg(left_threshold, 7) right_wait = parse_arg(right_threshold, 7) ds_wait = parse_arg(ds_threshold, 4) d17_wait = parse_arg(d17_threshold, 6) corner_wait = parse_arg(corner_threshold, 3) pat_y = 12 - pat_x status_flags = { "missing": False, "even": False, "streak": False, "pattern": False, "voisins": False, "tiers": False, "left": False, "right": False, "5ds": False, "d17": False, "corner": False } # Safe Spin Data raw_spins = state.last_spins if hasattr(state, 'last_spins') else [] if raw_spins is None: raw_spins = [] spins = [] for s in raw_spins: try: if str(s).strip().lstrip('-').isdigit(): spins.append(int(s)) except: continue # Hot Numbers hot_subset = set() hot_list = [] if hasattr(state, 'scores'): sorted_scores = sorted(state.scores.items(), key=lambda x: x[1], reverse=True) hot_subset = {num for num, score in sorted_scores[:5] if score > 0} hot_candidates = [(num, score) for num, score in sorted_scores if score > 0] hot_list = sorted(hot_candidates[:5], key=lambda x: x[0]) sector_ranks = {} if spins: d_counts = {d: count_frequency(set(nums), spins) for d, nums in DOZENS.items()} sorted_d = sorted(d_counts.items(), key=lambda x: x[1], reverse=True) for i, (name, _) in enumerate(sorted_d): sector_ranks[name] = i + 1 c_counts = {c: count_frequency(set(nums), spins) for c, nums in COLUMNS.items()} sorted_c = sorted(c_counts.items(), key=lambda x: x[1], reverse=True) for i, (name, _) in enumerate(sorted_c): sector_ranks[name] = i + 1 em_counts = {} for name, nums in EVEN_MONEY.items(): em_counts[name] = count_frequency(set(nums), spins) sorted_em = sorted(em_counts.items(), key=lambda x: x[1], reverse=True) for i, (name, _) in enumerate(sorted_em): if i < 3: sector_ranks[name] = i + 1 # Dynamic 17 Logic if not hasattr(state, 'd17_list'): state.d17_list = [] if not hasattr(state, 'd17_locked'): state.d17_locked = False temp_list = [] is_locked = False d17_miss_count = 0 for s in spins: if not is_locked: if s not in temp_list: temp_list.append(s) if len(temp_list) == 17: is_locked = True else: if s in temp_list: d17_miss_count = 0 else: d17_miss_count += 1 state.d17_list = temp_list state.d17_locked = is_locked # --- MANUAL GRIND LOGIC --- current_spin_count = len(spins) if grind_active: if current_spin_count > state.grind_last_spin_count: new_spins_list = spins[state.grind_last_spin_count:] active_target = grind_target if grind_target == "Auto (Hottest D/C)": active_target = get_hottest_sector(spins) for s in new_spins_list: target_nums = get_grind_numbers(active_target) if s in target_nums: state.grind_step_index = 0 else: state.grind_step_index += 1 if state.grind_step_index >= len(seq_manual_grind): state.grind_step_index = len(seq_manual_grind) - 1 state.grind_last_spin_count = current_spin_count elif current_spin_count < state.grind_last_spin_count: state.grind_last_spin_count = current_spin_count else: state.grind_last_spin_count = current_spin_count # Live Stats Calculation miss_counts = {} for dname, nums in DOZENS.items(): miss_counts[dname] = count_misses(set(nums), spins, zero_is_miss=True) col_miss_counts = {} for cname, nums in COLUMNS.items(): col_miss_counts[cname] = count_misses(set(nums), spins, zero_is_miss=True) all_section_misses = {**miss_counts, **col_miss_counts} worst_section_miss_val = max(all_section_misses.values()) if all_section_misses else 0 worst_section_name = max(all_section_misses, key=all_section_misses.get) if all_section_misses else "N/A" even_miss_counts = {} for ename, nums in EVEN_MONEY.items(): even_miss_counts[ename] = count_misses(set(nums), spins, zero_is_miss=True) worst_even_miss_val = max(even_miss_counts.values()) if even_miss_counts else 0 worst_even_name = max(even_miss_counts, key=even_miss_counts.get) if even_miss_counts else "N/A" streak_counts = {} for dname, nums in DOZENS.items(): streak_counts[dname] = count_hits(set(nums), spins) for cname, nums in COLUMNS.items(): streak_counts[cname] = count_hits(set(nums), spins) best_streak_val = max(streak_counts.values()) if streak_counts else 0 best_streak_name = max(streak_counts, key=streak_counts.get) if streak_counts else "N/A" streak_targets = { "1st Dozen": "2nd & 3rd Dozen", "2nd Dozen": "1st & 3rd Dozen", "3rd Dozen": "1st & 2nd Dozen", "1st Column": "2nd & 3rd Column", "2nd Column": "1st & 3rd Column", "3rd Column": "1st & 2nd Column" } display_streak_target = streak_targets.get(best_streak_name, best_streak_name) curr_voisins_miss = count_misses(set(voisins_numbers), spins, zero_is_miss=False) curr_tiers_miss = count_misses(set(tiers_orph_numbers), spins, zero_is_miss=True) curr_left_miss = count_hits(set(left_uncovered), spins) curr_right_miss = count_hits(set(right_uncovered), spins) ds_streaks = {} for ds_name, nums in double_streets.items(): ds_streaks[ds_name] = count_hits_with_zero(set(nums), spins) best_ds_streak = max(ds_streaks.values()) if ds_streaks else 0 best_ds_name = max(ds_streaks, key=ds_streaks.get) if ds_streaks else "N/A" # --- FIXED: 5-CORNER CALCULATION MOVED UP --- best_corner_template = None; max_corner_miss = 0 for template_name, corners_list in corner_templates.items(): template_numbers = set() for c in corners_list: template_numbers.update(c) misses = count_misses(template_numbers, spins, zero_is_miss=True) if misses > max_corner_miss: max_corner_miss = misses; best_corner_template = (template_name, template_numbers) # --- ON DECK RADAR CALCULATION --- # Add triggers that are exactly (threshold - 1) away from firing if worst_section_miss_val == miss_wait - 1: on_deck_triggers.append(f"Missing {worst_section_name}") if worst_even_miss_val == even_wait - 1: on_deck_triggers.append(f"Even Drought ({worst_even_name})") if best_streak_val == streak_wait - 1: on_deck_triggers.append(f"Streak ({best_streak_name})") if curr_voisins_miss == voisins_wait - 1: on_deck_triggers.append("Voisins") if curr_tiers_miss == tiers_wait - 1: on_deck_triggers.append("Tiers") if curr_left_miss == left_wait - 1: on_deck_triggers.append("Left Side") if curr_right_miss == right_wait - 1: on_deck_triggers.append("Right Side") if best_ds_streak == ds_wait - 1: on_deck_triggers.append(f"5DS ({best_ds_name})") if is_locked and d17_miss_count == d17_wait - 1: on_deck_triggers.append("D17") if max_corner_miss == corner_wait - 1: on_deck_triggers.append("Corner Shuffle") on_deck_html = "" if on_deck_triggers: on_deck_html = f'
📡 ON DECK (1 Spin Away): {", ".join(on_deck_triggers)}
' # ========================================================= # 7. GENERATE ALERTS & ACTION CARDS # ========================================================= # MANUAL GRIND grind_card_title = "MANUAL GRIND TRACKER" display_target = "PAUSED / INACTIVE" hottest_sector = get_hottest_sector(spins) if isinstance(hottest_sector, list): hottest_sector_str = " / ".join(hottest_sector) else: hottest_sector_str = hottest_sector grind_rec_html = f'🔥 Grind Rec: {hottest_sector_str}' if grind_active: if grind_target == "Auto (Hottest D/C)": current_hottest = hottest_sector if isinstance(current_hottest, list): display_target = f"Bet {' & '.join(current_hottest)} (Auto Tie)" for t in current_hottest: grind_targets.add(t) else: display_target = f"Bet {current_hottest} (Auto)" grind_targets.add(current_hottest) else: display_target = f"Bet {grind_target}" grind_targets.add(grind_target) active_actions.append(generate_action_card(grind_card_title, display_target, state.grind_step_index, 0, seq_manual_grind, cost_grind)) # 5-CORNER # Already calculated above for On Deck, re-using values here. if best_corner_template and max_corner_miss >= corner_wait: status_flags["corner"] = True t_name, t_nums = best_corner_template highlight_targets.update(t_nums) active_actions.append(generate_action_card("5-CORNER STRESS SHUFFLE", f"Bet {t_name} Corners", max_corner_miss, corner_wait, seq_corners, cost_corner)) active_target_groups.append((f"BET {t_name.upper()} CORNERS", sorted(list(t_nums)), get_header_color("CORNER"))) # D17 if is_locked and d17_miss_count >= d17_wait: status_flags["d17"] = True highlight_targets.update(state.d17_list) active_actions.append(generate_action_card("17-NUMBER ASSAULT", "Bet Captured 17", d17_miss_count, d17_wait, seq_d17, cost_d17, set(state.d17_list))) # 5DS (SAFETY MODE) for ds_name, streak_val in ds_streaks.items(): if streak_val >= ds_wait: status_flags["5ds"] = True is_ghost = ds_name in ghost_parents hot_numbers_set = set() range_to_skip = "N/A" if is_ghost: hot_numbers_set.update(ghost_parents[ds_name]) range_to_skip = f"{ds_name} & Parents" target_display = f"SAFETY: Skip {range_to_skip} & 0" active_actions.append(generate_action_card("5DS SAFETY MODE", target_display, streak_val, ds_wait, seq_5ds, cost_5ds, is_5ds=True, spots_override=4)) else: hot_numbers_set.update(double_streets[ds_name]) range_to_skip = ds_ranges.get(ds_name, ds_name) target_display = f"Skip: {range_to_skip} & 0" active_actions.append(generate_action_card("5DS STRATEGY ALERT", target_display, streak_val, ds_wait, seq_5ds, cost_5ds, is_5ds=True, spots_override=5)) all_numbers_set = set(range(1, 37)) safe_numbers = all_numbers_set - hot_numbers_set highlight_targets.update(safe_numbers) active_target_groups.append(("BET 5DS (Excl. " + range_to_skip + ")", sorted(list(safe_numbers)), get_header_color("5DS"))) # Standard Triggers if curr_left_miss >= left_wait: status_flags["left"] = True highlight_targets.add("LeftSide") active_actions.append(generate_action_card("LEFT SIDE ATTACK", "Left + Zero", curr_left_miss, left_wait, seq_sides, cost_sides, set(left_side_covered), spots_override=25)) active_target_groups.append(("LEFT SIDE", sorted(left_side_covered), get_header_color("SIDE"))) if curr_right_miss >= right_wait: status_flags["right"] = True highlight_targets.add("RightSide") active_actions.append(generate_action_card("RIGHT SIDE ATTACK", "Right + Zero", curr_right_miss, right_wait, seq_sides, cost_sides, set(right_side_covered), spots_override=25)) active_target_groups.append(("RIGHT SIDE", sorted(right_side_covered), get_header_color("SIDE"))) if curr_voisins_miss >= voisins_wait: status_flags["voisins"] = True highlight_targets.add("Voisins") active_actions.append(generate_action_card("VOISINS ATTACK", "Voisins (0/2/3)", curr_voisins_miss, voisins_wait, seq_voisins, cost_voisins, set(voisins_numbers))) active_target_groups.append(("VOISINS", sorted(voisins_numbers), get_header_color("VOISINS"))) if curr_tiers_miss >= tiers_wait: status_flags["tiers"] = True highlight_targets.add("TiersOrph") active_actions.append(generate_action_card("TIERS+ORPH ATTACK", "Tiers + Orph", curr_tiers_miss, tiers_wait, seq_tiers, cost_tiers, set(tiers_orph_numbers))) active_target_groups.append(("TIERS+ORPH", sorted(tiers_orph_numbers), get_header_color("TIERS"))) for dname, val in miss_counts.items(): if val >= miss_wait: status_flags["missing"] = True highlight_targets.add(dname) active_actions.append(generate_action_card("MISSING DOZEN", dname, val, miss_wait, seq_manual_grind, cost_dozen)) active_target_groups.append((dname.upper(), sorted(DOZENS[dname]), get_header_color("MISSING"))) for cname, val in col_miss_counts.items(): if val >= miss_wait: status_flags["missing"] = True highlight_targets.add(cname) active_actions.append(generate_action_card("MISSING COLUMN", cname, val, miss_wait, seq_manual_grind, cost_dozen)) active_target_groups.append((cname.upper(), sorted(COLUMNS[cname]), get_header_color("MISSING"))) for ename, val in even_miss_counts.items(): if val >= even_wait: status_flags["even"] = True highlight_targets.add(ename) active_actions.append(generate_action_card("EVEN MONEY DROUGHT", ename, val, even_wait, seq_mart, cost_even)) active_target_groups.append((ename.upper(), sorted(EVEN_MONEY[ename]), get_header_color("EVEN"))) for section, streak_val in streak_counts.items(): if streak_val >= streak_wait: status_flags["streak"] = True target_str = streak_targets.get(section, "Others") if "1st" in target_str: highlight_targets.add("1st " + section.split()[1]) if "2nd" in target_str: highlight_targets.add("2nd " + section.split()[1]) if "3rd" in target_str: highlight_targets.add("3rd " + section.split()[1]) active_actions.append(generate_action_card("TWO DOZENS STREAK ATTACK", f"Bet {target_str}", streak_val, streak_wait, seq_trip, cost_streak)) d_data = get_pattern_alert(spins, "dozen") if d_data: status_flags["pattern"] = True d_pat, d_match_data, d_follow_data = d_data bet_list_html = "" for i, (cat, val) in enumerate(d_follow_data): spin_num = i + 1 suggestion = "Skip (0)" if cat == "D1": suggestion = "Bet D2 + D3"; elif cat == "D2": suggestion = "Bet D1 + D3"; elif cat == "D3": suggestion = "Bet D1 + D2"; if i == 0: if cat == "D1": highlight_targets.update(["2nd Dozen", "3rd Dozen"]) elif cat == "D2": highlight_targets.update(["1st Dozen", "3rd Dozen"]) elif cat == "D3": highlight_targets.update(["1st Dozen", "2nd Dozen"]) bet_list_html += f"
Spin {spin_num}: {suggestion}
" active_actions.append(f"""
PATTERN MATCH (X={pat_x})
Found: {format_seq(d_match_data)}
Anti-Betting Plan:
{bet_list_html}
""") c_data = get_pattern_alert(spins, "column") if c_data: status_flags["pattern"] = True c_pat, c_match_data, c_follow_data = c_data bet_list_html = "" for i, (cat, val) in enumerate(c_follow_data): spin_num = i + 1 suggestion = "Skip (0)" if cat == "C1": suggestion = "Bet C2 + C3"; elif cat == "C2": suggestion = "Bet C1 + C3"; elif cat == "C3": suggestion = "Bet C1 + C2"; if i == 0: if cat == "C1": highlight_targets.update(["2nd Column", "3rd Column"]) elif cat == "C2": highlight_targets.update(["1st Column", "3rd Column"]) elif cat == "C3": highlight_targets.update(["1st Column", "2nd Column"]) bet_list_html += f"
Spin {spin_num}: {suggestion}
" active_actions.append(f"""
PATTERN MATCH (X={pat_x})
Found: {format_seq(c_match_data)}
Anti-Betting Plan:
{bet_list_html}
""") # --------------------------------------------------------- # 8. GENERATE BOTTOM ROW ACTION STREAM (SAFE ORDER) # --------------------------------------------------------- hot_numbers_html = '
' if hot_list: hot_numbers_html += '
' hot_numbers_html += '🔥 Top 5 Hot:' for num, score in hot_list: c = colors.get(str(num), "black") hot_numbers_html += f'{num}' hot_numbers_html += '
' else: hot_numbers_html += '
Waiting for spin data...
' master_coverage_set = set() if active_target_groups: for title, nums, color in active_target_groups: master_coverage_set.update(nums) hot_numbers_html += f'
' hot_numbers_html += f'{title}:' for num in nums: if num == 0: c = "#27ae60" else: c = colors.get(str(num), "black") hot_numbers_html += f'{num}' hot_numbers_html += '
' hot_numbers_html += '
' # --- UPDATED: MERGE HOT NUMBERS AND CATEGORIZE BY DOZENS --- if master_coverage_set or hot_subset: master_coverage_set.update(hot_subset) sorted_master = sorted(list(master_coverage_set)) # --- RISK METER CALCULATION --- risk_pct = int((len(sorted_master) / 37) * 100) risk_color = "#4CAF50" # Safe if risk_pct > 40: risk_color = "#FFC107" # Moderate if risk_pct > 65: risk_color = "#D32F2F" # High Risk risk_meter_html = f'🛡️ Coverage: {risk_pct}% ({len(sorted_master)} #s)' # Buckets zeros_list = [n for n in sorted_master if n == 0] d1_list = [n for n in sorted_master if 1 <= n <= 12] d2_list = [n for n in sorted_master if 13 <= n <= 24] d3_list = [n for n in sorted_master if 25 <= n <= 36] hot_numbers_html += f'
' hot_numbers_html += f'
🎯 TOTAL UNIQUE COVERAGE: {risk_meter_html}
' hot_numbers_html += '
' def render_group(nums): html_out = '
' for num in nums: if num == 0: c = "#27ae60" else: c = colors.get(str(num), "black") flame = "" if num in hot_subset: flame = '🔥' html_out += f'{num}{flame}' html_out += '
' return html_out if zeros_list: hot_numbers_html += render_group(zeros_list) if d1_list: hot_numbers_html += render_group(d1_list) if d2_list: hot_numbers_html += render_group(d2_list) if d3_list: hot_numbers_html += render_group(d3_list) hot_numbers_html += '
' # --------------------------------------------------------- # 9. VISUAL TABLE GENERATION (SAFE ORDER) # --------------------------------------------------------- zero_active = (0 in highlight_targets) or ("Voisins" in highlight_targets and 0 in voisins_numbers) or ("LeftSide" in highlight_targets and 0 in left_side_covered) zero_hot = 0 in hot_subset zero_flame = "" if zero_hot: zero_flame = '🔥' in_spotlight_zero = zero_active or zero_hot zero_opacity = "1.0" if in_spotlight_zero else "0.3" zero_style = "background-color: #27ae60; color: white;" if zero_active: zero_style = active_style if zero_hot: zero_style += " border: 3px solid #ff3333; box-shadow: 0 0 10px #ff0000;" elif zero_hot: zero_style += " opacity:0.9; border: 3px solid #ff3333; box-shadow: inset 0 0 5px #ff0000;" zero_style += f" position: relative; overflow: visible; opacity: {zero_opacity};" row3 = render_row_cells([3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36], highlight_targets, grind_targets, hot_subset, colors) row2 = render_row_cells([2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35], highlight_targets, grind_targets, hot_subset, colors) row1 = render_row_cells([1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34], highlight_targets, grind_targets, hot_subset, colors) visual_table = f"""
0{zero_flame}
{row3}
{row2}
{row1}
C3
C2
C1
1st 12
2nd 12
3rd 12
1-18
EVEN
RED
BLK
ODD
19-36
""" # --- RESTORED: STRATEGY SETTINGS REFERENCE (CHEAT SHEET) --- strategy_reference_html = """
Strategy Settings Reference 📝
""" config_html = f""" {strategy_reference_html}
Live Trigger Status Dashboard
{grind_rec_html}
{on_deck_html}
MISSING DOZEN/COL
Target: {worst_section_name}
Current Miss: {worst_section_miss_val}/{miss_wait} {get_progress_bar_html(worst_section_miss_val, miss_wait)} {format_sequence_html(seq_manual_grind, worst_section_miss_val, miss_wait)}
EVEN MONEY DROUGHT
Target: {worst_even_name}
Current Miss: {worst_even_miss_val}/{even_wait} {get_progress_bar_html(worst_even_miss_val, even_wait)} {format_sequence_html(seq_mart, worst_even_miss_val, even_wait)}
TWO DOZENS STREAK ATTACK
Target: {display_streak_target}
Current Streak: {best_streak_val}/{streak_wait} {get_progress_bar_html(best_streak_val, streak_wait)} {format_sequence_html(seq_trip, best_streak_val, streak_wait)}
2 DOZENS PATTERN
Target: Auto-Detect
Match Length: X{pat_x} Dynamic Anti-Bet
VOISINS DU ZERO
Target: Voisins
Current Miss: {curr_voisins_miss}/{voisins_wait} {get_progress_bar_html(curr_voisins_miss, voisins_wait)} {format_sequence_html(seq_voisins, curr_voisins_miss, voisins_wait)}
TIERS + ORPH
Target: TiersOrph
Current Miss: {curr_tiers_miss}/{tiers_wait} {get_progress_bar_html(curr_tiers_miss, tiers_wait)} {format_sequence_html(seq_tiers, curr_tiers_miss, tiers_wait)}
LEFT SIDE ZERO
Target: LeftSide
Current Miss: {curr_left_miss}/{left_wait} {get_progress_bar_html(curr_left_miss, left_wait)} {format_sequence_html(seq_sides, curr_left_miss, left_wait)}
RIGHT SIDE ZERO
Target: RightSide
Current Miss: {curr_right_miss}/{right_wait} {get_progress_bar_html(curr_right_miss, right_wait)} {format_sequence_html(seq_sides, curr_right_miss, right_wait)}
5 DOUBLE STREETS
Target: {best_ds_name}
Streak: {best_ds_streak}/{ds_wait} {get_progress_bar_html(best_ds_streak, ds_wait)} {format_sequence_html(seq_5ds, best_ds_streak, ds_wait)}
DYNAMIC 17
Target: {len(state.d17_list)}/17 #s
{'Wait: ' + str(d17_miss_count) + '/' + str(d17_wait) if is_locked else 'Collecting...'} {get_progress_bar_html(d17_miss_count, d17_wait) if is_locked else ""} {format_sequence_html(seq_d17, d17_miss_count if is_locked else 0, d17_wait if is_locked else 100)}
5-CORNER SHUFFLE
Target: {best_corner_template[0] if best_corner_template else 'Scanning...'}
Current Miss: {max_corner_miss}/{corner_wait} {get_progress_bar_html(max_corner_miss, corner_wait)} {format_sequence_html(seq_corners, max_corner_miss, corner_wait)}
{hot_numbers_html}
""" if active_actions: actions_section = f"""
{"".join(active_actions)}
""" else: actions_section = "
No Active Triggers. Waiting for patterns...
" # --------------------------------------------------------- # 11. SEQUENCE SHOWCASE PANEL (COLLAPSIBLE) # --------------------------------------------------------- sequences_info_html = f"""
📜 Active Sequence Showcase (Click to Expand)
Manual Grind:
{str(seq_manual_grind)}
Martingale (Even):
{str(seq_mart)}
Voisins Stress:
{str(seq_voisins)}
Tiers Stress:
{str(seq_tiers)}
Sides Stress (5-Step):
{str(seq_sides)}
5DS:
{str(seq_5ds)}
D17:
{str(seq_d17)}
Corners:
{str(seq_corners)}
Streak:
{str(seq_trip)}
""" # Return concatenated HTML (Config + Spins + Actions + Table + Showcase) return config_html + spins_html + actions_section + visual_table + sequences_info_html except Exception as e: import traceback print(f"Error in DE2D Tracker: {str(e)}") print(traceback.format_exc()) return f"
Error loading DE2D tracker: {str(e)}
" # Lines after (context, unchanged from Part 2) with gr.Blocks(title="WheelPulse PRO by S.T.Y.W 📈") as demo: # Removed the Terms and Conditions Modal (gr.HTML block) # Static Centered Options Section (Above Header) gr.HTML("""
🌟 Discover WheelPulse: Your Guide to Mastering the App
""") # App Content (Header Section - Updated) with gr.Group(elem_id="appContent"): with gr.Row(elem_id="header-row"): gr.HTML("""

WheelPulse PRO by S.T.Y.W

""") # Ensure app content is shown after acceptance gr.HTML(""" """) # Updated Selected Spins Accordion Styling gr.HTML(""" """) # Start of the app layout (next section after the header) def suggest_hot_cold_numbers(): """Suggest top 5 hot and bottom 5 cold numbers based on state.scores.""" try: if not state.scores or not any(state.scores.values()): return "", "

No spin data available for suggestions.

" sorted_scores = sorted(state.scores.items(), key=lambda x: x[1], reverse=True) hot_numbers = [str(num) for num, score in sorted_scores[:5] if score > 0] cold_numbers = [str(num) for num, score in sorted_scores[-5:] if score >= 0] if not hot_numbers: hot_numbers = ["No hot numbers"] if not cold_numbers: cold_numbers = ["No cold numbers"] state.hot_suggestions = ", ".join(hot_numbers) state.cold_suggestions = ", ".join(cold_numbers) return state.hot_suggestions, state.cold_suggestions except Exception as e: print(f"suggest_hot_cold_numbers: Error: {str(e)}") return "", "

Error generating suggestions.

" STRATEGIES = { "Hot Bet Strategy": {"function": hot_bet_strategy, "categories": ["even_money", "dozens", "columns", "streets", "corners", "six_lines", "splits", "sides", "numbers"]}, "Cold Bet Strategy": {"function": cold_bet_strategy, "categories": ["even_money", "dozens", "columns", "streets", "corners", "six_lines", "splits", "sides", "numbers"]}, "Best Even Money Bets": {"function": best_even_money_bets, "categories": ["even_money"]}, "Best Even Money Bets + Top Pick 18 Numbers": {"function": best_even_money_and_top_18, "categories": ["even_money", "numbers"]}, "Best Dozens": {"function": best_dozens, "categories": ["dozens"]}, "Best Dozens + Top Pick 18 Numbers": {"function": best_dozens_and_top_18, "categories": ["dozens", "numbers"]}, "Best Columns": {"function": best_columns, "categories": ["columns"]}, "Best Columns + Top Pick 18 Numbers": {"function": best_columns_and_top_18, "categories": ["columns", "numbers"]}, "Best Dozens + Best Even Money Bets + Top Pick 18 Numbers": {"function": best_dozens_even_money_and_top_18, "categories": ["dozens", "even_money", "numbers", "trends"]}, "Best Columns + Best Even Money Bets + Top Pick 18 Numbers": {"function": best_columns_even_money_and_top_18, "categories": ["columns", "even_money", "numbers", "trends"]}, "Fibonacci Strategy": {"function": fibonacci_strategy, "categories": ["dozens", "columns"]}, "Best Streets": {"function": best_streets, "categories": ["streets"]}, "Best Double Streets": {"function": best_double_streets, "categories": ["six_lines"]}, "Best Corners": {"function": best_corners, "categories": ["corners"]}, "Best Splits": {"function": best_splits, "categories": ["splits"]}, "Best Dozens + Best Streets": {"function": best_dozens_and_streets, "categories": ["dozens", "streets"]}, "Best Columns + Best Streets": {"function": best_columns_and_streets, "categories": ["columns", "streets"]}, "Non-Overlapping Double Street Strategy": {"function": non_overlapping_double_street_strategy, "categories": ["six_lines"]}, "Non-Overlapping Corner Strategy": {"function": non_overlapping_corner_strategy, "categories": ["corners"]}, "Romanowksy Missing Dozen": {"function": romanowksy_missing_dozen_strategy, "categories": ["dozens", "numbers"]}, "Fibonacci To Fortune": {"function": fibonacci_to_fortune_strategy, "categories": ["even_money", "dozens", "columns", "six_lines"]}, "3-8-6 Rising Martingale": {"function": three_eight_six_rising_martingale, "categories": ["streets"]}, "1 Dozen +1 Column Strategy": {"function": one_dozen_one_column_strategy, "categories": ["dozens", "columns"]}, "Top Pick 18 Numbers without Neighbours": {"function": top_pick_18_numbers_without_neighbours, "categories": ["numbers"]}, "Top Numbers with Neighbours (Tiered)": {"function": top_numbers_with_neighbours_tiered, "categories": ["numbers"]}, "Neighbours of Strong Number": {"function": neighbours_of_strong_number, "categories": ["neighbours"]} } # Line 1: Start of show_strategy_recommendations function (updated) def show_strategy_recommendations(strategy_name, neighbours_count, *args): """Generate strategy recommendations based on the selected strategy.""" try: print(f"show_strategy_recommendations: scores = {dict(state.scores)}") print(f"show_strategy_recommendations: even_money_scores = {dict(state.even_money_scores)}") print(f"show_strategy_recommendations: any_scores = {any(state.scores.values())}, any_even_money = {any(state.even_money_scores.values())}") print(f"show_strategy_recommendations: strategy_name = {strategy_name}, neighbours_count = {neighbours_count}, args = {args}") if strategy_name == "None": return "

No strategy selected. Please choose a strategy to see recommendations.

" # If no spins yet, provide a default for "Best Even Money Bets" if not any(state.scores.values()) and not any(state.even_money_scores.values()): if strategy_name == "Best Even Money Bets": return "

No spins yet. Default Even Money Bets to consider:
1. Red
2. Black
3. Even

" return "

Please analyze some spins first to generate scores.

" strategy_info = STRATEGIES[strategy_name] strategy_func = strategy_info["function"] if strategy_name == "Neighbours of Strong Number": try: neighbours_count = int(neighbours_count) strong_numbers_count = int(args[0]) if args else 1 # Assuming strong_numbers_count is first in args print(f"show_strategy_recommendations: Using neighbours_count = {neighbours_count}, strong_numbers_count = {strong_numbers_count}") except (ValueError, TypeError) as e: print(f"show_strategy_recommendations: Error converting inputs: {str(e)}, defaulting to 2 and 1.") neighbours_count = 2 strong_numbers_count = 1 result = strategy_func(neighbours_count, strong_numbers_count) # Handle the tuple return value for Neighbours of Strong Number if isinstance(result, tuple) and len(result) == 2: recommendations, _ = result # We only need the recommendations string for display else: recommendations = result elif strategy_name == "Dozen Tracker": # Dozen Tracker expects multiple arguments and returns a tuple result = strategy_func(*args) if isinstance(result, tuple) and len(result) == 3: recommendations, _, _ = result # Unpack the tuple, we only need the first element else: recommendations = result elif strategy_name == "Top Numbers Strategy": # Handle Top Numbers Strategy try: strong_numbers_count = int(args[0]) if args else 5 # Number of top numbers to show print(f"show_strategy_recommendations: Using strong_numbers_count = {strong_numbers_count} for Top Numbers Strategy") except (ValueError, TypeError) as e: print(f"show_strategy_recommendations: Error converting inputs: {str(e)}, defaulting to 5.") strong_numbers_count = 5 # Call the strategy function to get the top numbers top_numbers = strategy_func() # Assuming this returns a list of (number, score) tuples if not top_numbers: return "

No top numbers available. Please analyze more spins.

" # Limit to strong_numbers_count and sort by score top_numbers = sorted(top_numbers, key=lambda x: x[1], reverse=True)[:strong_numbers_count] # Generate neighbors for each number html = "

Here are the top numbers to consider based on recent spins:

" html += '' html += "" # Pad the list with empty entries to make it divisible by 3 while len(top_numbers) % 3 != 0: top_numbers.append(("", "")) # Group numbers into sets of 3 for i in range(0, len(top_numbers), 3): group = top_numbers[i:i+3] html += "" for number, score in group: if number: neighbors = get_neighbors(number, neighbours_count) html += f"" else: html += "" html += "" html += "
NumberScoreNeighborsNumberScoreNeighborsNumberScoreNeighbors
{number}{score}{', '.join(map(str, neighbors))}
" return html else: # Other strategies return a single string recommendations = strategy_func() print(f"show_strategy_recommendations: Raw strategy output for {strategy_name} = '{recommendations}'") # If the output is already HTML (e.g., for "Top Numbers with Neighbours (Tiered)"), return it as is if strategy_name == "Top Numbers with Neighbours (Tiered)": return recommendations # Special handling for "Neighbours of Strong Number" to format Suggestions section elif strategy_name == "Neighbours of Strong Number": lines = recommendations.split("\n") html_lines = [] in_suggestions = False for line in lines: if line.strip() == "Suggestions:": in_suggestions = True html_lines.append('

Suggestions:

') elif line.strip() == "" and in_suggestions: in_suggestions = False html_lines.append('

') elif in_suggestions: html_lines.append(f'

{line}

') else: html_lines.append(f'

{line}

') return '
' + "".join(html_lines) + "
" # Otherwise, convert plain text to HTML with proper line breaks else: # Split the output into lines, removing any empty lines lines = [line for line in recommendations.split("\n") if line.strip()] # Wrap each line in

tags and join with
for proper spacing html_lines = [f"

{line}

" for line in lines] return "
" + "".join(html_lines) + "
" except Exception as e: print(f"show_strategy_recommendations: Error: {str(e)}") raise # Re-raise for debugging # Line 3: Start of clear_outputs function (unchanged) def clear_outputs(): return "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" # Lines after (context, unchanged) def toggle_checkboxes(strategy_name): return (gr.update(visible=strategy_name == "Kitchen Martingale"), gr.update(visible=strategy_name == "S.T.Y.W: Victory Vortex")) def reset_colors(): """Reset color pickers to default values and update the dynamic table.""" default_top = "rgba(255, 255, 0, 0.5)" # Yellow default_middle = "rgba(0, 255, 255, 0.5)" # Cyan default_lower = "rgba(0, 255, 0, 0.5)" # Green return default_top, default_middle, default_lower # Define state and components used across sections spins_display = gr.State(value="") show_trends_state = gr.State(value=False) # Default to hiding trends toggle_trends_label = gr.State(value="Show Trends") # Default label when trends are hidden analysis_cache = gr.State(value={}) # New: Cache for analysis results spins_textbox = gr.Textbox( label="🎰 Selected Spins (Enter numbers like 5, 12, 0)", value="", interactive=True, elem_id="selected-spins" ) gr.HTML("""
""") with gr.Accordion("Dealer’s Spin Tracker (Can you spot Bias???) 🕵️", open=False, elem_id="sides-of-zero-accordion"): sides_of_zero_display = gr.HTML( label="Sides of Zero", value=render_sides_of_zero_display(), elem_classes=["sides-of-zero-container"] ) # Start of updated section with gr.Accordion("Hit Percentage Overview 📊", open=False, elem_id="hit-percentage-overview"): gr.HTML(""" """) with gr.Row(elem_classes=["hit-percentage-row"]): with gr.Column(scale=1): hit_percentage_display = gr.HTML( label="Hit Percentages", value=calculate_hit_percentages(36), elem_classes=["hit-percentage-container"] ) with gr.Accordion("SpinTrend Radar 🌀", open=False, elem_id="spin-trend-radar"): gr.HTML(""" """) with gr.Row(elem_classes=["spin-trend-row"]): with gr.Column(scale=1): traits_display = gr.HTML( label="Spin Traits", value=summarize_spin_traits(36), elem_classes=["traits-container"] ) # Line 1: Updated Next Spin Top Pick accordion with gr.Accordion("Next Spin Top Pick 🎯", open=False, elem_id="next-spin-top-pick"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 🎯 Select Your Top Pick") gr.Markdown("Adjust the slider to analyze the last X spins and find the top pick for your next spin. Add spins using the roulette table below or enter them manually.") trait_filter = gr.CheckboxGroup( label="Include in Analysis", choices=["Red/Black", "Even/Odd", "Low/High", "Dozens", "Columns", "Wheel Sections", "Neighbors"], value=["Red/Black", "Even/Odd", "Low/High", "Dozens", "Columns", "Wheel Sections", "Neighbors"], interactive=True, elem_id="trait-filter" ) top_pick_spin_count = gr.Slider( label="Number of Spins to Analyze", minimum=1, maximum=36, step=1, value=18, interactive=True, elem_classes="long-slider" ) with gr.Accordion("Adjust Scoring Weights", open=False, elem_id="scoring-weights"): gr.Markdown("#### Customize Scoring Weights") gr.Markdown("Fine-tune how much each factor contributes to the top pick score.") trait_match_weight = gr.Number( label="Trait Match Weight", value=100, minimum=0, maximum=1000, step=1, interactive=True, elem_id="trait-match-weight" ) secondary_match_weight = gr.Number( label="Secondary Match Weight", value=10, minimum=0, maximum=1000, step=1, interactive=True, elem_id="secondary-match-weight" ) wheel_side_weight = gr.Number( label="Wheel Side Weight", value=5, minimum=0, maximum=1000, step=1, interactive=True, elem_id="wheel-side-weight" ) section_weight = gr.Number( label="Wheel Section Weight", value=10, minimum=0, maximum=1000, step=1, interactive=True, elem_id="section-weight" ) recency_weight = gr.Number( label="Recency Weight", value=1, minimum=0, maximum=1000, step=1, interactive=True, elem_id="recency-weight" ) hit_bonus_weight = gr.Number( label="Hit Bonus Weight", value=5, minimum=0, maximum=1000, step=1, interactive=True, elem_id="hit-bonus-weight" ) neighbor_weight = gr.Number( label="Neighbor Boost Weight", value=2, minimum=0, maximum=1000, step=1, interactive=True, elem_id="neighbor-weight" ) # NEW: Reset button reset_weights_button = gr.Button("Reset Weights to Default", elem_id="reset-weights") top_pick_display = gr.HTML( label="Top Pick", value=select_next_spin_top_pick(18, ["Red/Black", "Even/Odd", "Low/High", "Dozens", "Columns", "Wheel Sections", "Neighbors"]), elem_classes=["top-pick-container"] ) gr.HTML(""" """) # --------------------------------------------------------- # NEW SECTION: DE2D ZONE (Dynamic Master + 8 Triggers) # --------------------------------------------------------- with gr.Accordion("DE2D ZONE 💀 (Dynamic Master)", open=False, elem_id="de2d-tracker-accordion"): gr.HTML("""
Strategy Settings Reference 📝
""") # Controls for Dynamic Configuration with gr.Row(elem_classes=["de2d-controls"]): with gr.Column(): miss_slider = gr.Slider(label="Missing Dozen/Col (Wait)", minimum=6, maximum=12, value=11, step=1) even_slider = gr.Slider(label="Even Money (Wait)", minimum=4, maximum=12, value=10, step=1) with gr.Column(): streak_slider = gr.Slider(label="Streak (Wait Hits)", minimum=5, maximum=10, value=9, step=1) pattern_slider = gr.Slider(label="Pattern Match (X)", minimum=4, maximum=9, value=8, step=1) with gr.Column(): voisins_slider = gr.Slider(label="Voisins Missing (Wait)", minimum=4, maximum=12, value=8, step=1) tiers_slider = gr.Slider(label="Tiers+Orph Missing (Wait)", minimum=4, maximum=12, value=9, step=1) with gr.Column(): left_side_slider = gr.Slider(label="Left Side Missing (Wait)", minimum=4, maximum=12, value=7, step=1) right_side_slider = gr.Slider(label="Right Side Missing (Wait)", minimum=4, maximum=12, value=7, step=1) with gr.Column(): ds_strategy_slider = gr.Slider(label="5 Double Street Strategy (Wait Streak)", minimum=2, maximum=9, value=4, step=1) d17_strategy_slider = gr.Slider(label="Dynamic 17-Assault (Wait Misses)", minimum=3, maximum=10, value=6, step=1) corner_strategy_slider = gr.Slider(label="5-Corner Stress Shuffle (Wait Misses)", minimum=1, maximum=15, value=6, step=1) # --- NEW COLUMN FOR MANUAL GRIND SETTINGS (UPDATED WITH AUTO) --- with gr.Column(): gr.Markdown("#### 🛡️ Manual Grind Settings") grind_active_checkbox = gr.Checkbox(label="Enable Grind Tracker", value=False, interactive=True) grind_target_dropdown = gr.Dropdown( label="Target Dozen/Column", # UPDATED: Added "Auto (Hottest D/C)" choices=["Auto (Hottest D/C)", "1st Dozen", "2nd Dozen", "3rd Dozen", "1st Column", "2nd Column", "3rd Column"], value="3rd Dozen", interactive=True ) reset_grind_button = gr.Button("Reset Grind Step", size="sm", elem_classes=["action-button"]) # Initial Logic Call (Added 5, 5 as default for Sides) de2d_output = gr.HTML( value=de2d_tracker_logic(10, 9, 8, 7, 6, 7, 5, 5), elem_classes=["de2d-output-box"], label="DE2D Alerts" ) # Define spin_counter before the roulette table to avoid NameError spin_counter = gr.HTML( label="Total Spins", value='Total Spins: 0', elem_classes=["spin-counter"] ) # Last Spins Display and Slider (Row 3) with gr.Row(): with gr.Column(): last_spin_display = gr.HTML( label="Last Spins", value='

Last Spins

No spins yet.

', elem_classes=["last-spins-container"] ) last_spin_count = gr.Slider( label="", minimum=1, maximum=36, step=1, value=36, interactive=True, elem_classes="long-slider" ) # Updated CSS and Debounce Script for Last Spins gr.HTML(""" """) # 2. Row 2: European Roulette Table (unchanged) with gr.Group(): gr.Markdown("### European Roulette Table") table_layout = [ ["", "3", "6", "9", "12", "15", "18", "21", "24", "27", "30", "33", "36"], ["0", "2", "5", "8", "11", "14", "17", "20", "23", "26", "29", "32", "35"], ["", "1", "4", "7", "10", "13", "16", "19", "22", "25", "28", "31", "34"] ] with gr.Column(elem_classes="roulette-table"): for row in table_layout: with gr.Row(elem_classes="table-row"): for num in row: if num == "": gr.Button(value=" ", interactive=False, min_width=40, elem_classes="empty-button") else: color = colors.get(str(num), "black") is_selected = int(num) in state.selected_numbers btn_classes = [f"roulette-button", color] if is_selected: btn_classes.append("selected") btn = gr.Button( value=num, min_width=40, elem_classes=btn_classes ) btn.click( fn=add_spin, inputs=[gr.State(value=num), spins_display, last_spin_count], outputs=[spins_display, spins_textbox, last_spin_display, spin_counter, sides_of_zero_display] ).then( fn=format_spins_as_html, inputs=[spins_display, last_spin_count], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=lambda: print(f"After add_spin: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) # New: Total Spins Section (Placed right after European Roulette Table) with gr.Row(): with gr.Column(scale=1, min_width=200): # Reference the already-defined spin_counter spin_counter # 3. Row 3: Last Spins Display and Show Last Spins Slider with gr.Row(): with gr.Column(): last_spin_display last_spin_count # 4. Row 4: Spin Controls (unchanged) with gr.Row(): with gr.Column(scale=1): undo_button = gr.Button("Undo Spins", elem_classes=["action-button"], elem_id="undo-spins-btn") with gr.Column(scale=1): generate_spins_button = gr.Button("Generate Random Spins", elem_classes=["action-button"]) with gr.Column(scale=1): toggle_trends_button = gr.Button( value="Hide Trends", # Initial string value elem_classes=["action-button"], elem_id="toggle-trends-btn" ) # 5. Row 5: Selected Spins Textbox (Updated to exclude spin_counter) with gr.Row(elem_id="selected-spins-row"): with gr.Column(scale=1, min_width=600): spins_textbox # Define strategy categories and choices strategy_categories = { "Trends": ["Cold Bet Strategy", "Hot Bet Strategy", "Best Dozens + Best Even Money Bets + Top Pick 18 Numbers", "Best Columns + Best Even Money Bets + Top Pick 18 Numbers"], "Even Money Strategies": ["Best Even Money Bets", "Best Even Money Bets + Top Pick 18 Numbers", "Fibonacci To Fortune"], "Dozen Strategies": ["1 Dozen +1 Column Strategy", "Best Dozens", "Best Dozens + Top Pick 18 Numbers", "Best Dozens + Best Even Money Bets + Top Pick 18 Numbers", "Best Dozens + Best Streets", "Fibonacci Strategy", "Romanowksy Missing Dozen"], "Column Strategies": ["1 Dozen +1 Column Strategy", "Best Columns", "Best Columns + Top Pick 18 Numbers", "Best Columns + Best Even Money Bets + Top Pick 18 Numbers", "Best Columns + Best Streets"], "Street Strategies": ["3-8-6 Rising Martingale", "Best Streets", "Best Columns + Best Streets", "Best Dozens + Best Streets"], "Double Street Strategies": ["Best Double Streets", "Non-Overlapping Double Street Strategy"], "Corner Strategies": ["Best Corners", "Non-Overlapping Corner Strategy"], "Split Strategies": ["Best Splits"], "Number Strategies": ["Top Numbers with Neighbours (Tiered)", "Top Pick 18 Numbers without Neighbours"], "Neighbours Strategies": ["Neighbours of Strong Number"] } category_choices = ["None"] + sorted(strategy_categories.keys()) # Define video categories matching strategy categories video_categories = { "Trends": [], "Even Money Strategies": [ { "title": "S.T.Y.W: Zero Jack 2-2-3 Roulette Strategy", "link": "https://youtu.be/I_F9Wys3Ww0" }, { "title": "S.T.Y.W: Fibonacci to Fortune (My Top Strategy) - Follow The Winner", "link": "https://youtu.be/bwa0FUk6Yps" }, { "title": "S.T.Y.W: Triple Entry Max Climax Strategy", "link": "https://youtu.be/64aq0GEPww0" } ], "Dozen Strategies": [ { "title": "S.T.Y.W: Dynamic Play: 1 Dozen with 4 Streets or 2 Double Streets?", "link": "https://youtu.be/8aMHrvuzBGU" }, { "title": "S.T.Y.W: Romanowsky Missing Dozen Strategy", "link": "https://youtu.be/YbBtum5WVCk" }, { "title": "S.T.Y.W: Victory Vortex (Dozen Domination)", "link": "https://youtu.be/aKGA_csI9lY" }, { "title": "S.T.Y.W: The Overlap Jackpot (4 Streets + 2 Dozens) Strategy", "link": "https://youtu.be/rTqdMQk4_I4" }, { "title": "S.T.Y.W: Fibonacci to Fortune (My Top Strategy) - Follow The Winner", "link": "https://youtu.be/bwa0FUk6Yps" }, { "title": "S.T.Y.W: Double Up: Dozen & Street Strategy", "link": "https://youtu.be/Hod5gxusAVE" }, { "title": "S.T.Y.W: Triple Entry Max Climax Strategy", "link": "https://youtu.be/64aq0GEPww0" } ], "Column Strategies": [ { "title": "S.T.Y.W: Zero Jack 2-2-3 Roulette Strategy", "link": "https://youtu.be/I_F9Wys3Ww0" }, { "title": "S.T.Y.W: Victory Vortex (Dozen Domination)", "link": "https://youtu.be/aKGA_csI9lY" }, { "title": "S.T.Y.W: Fibonacci to Fortune (My Top Strategy) - Follow The Winner", "link": "https://youtu.be/bwa0FUk6Yps" } ], "Street Strategies": [ { "title": "S.T.Y.W: Dynamic Play: 1 Dozen with 4 Streets or 2 Double Streets?", "link": "https://youtu.be/8aMHrvuzBGU" }, { "title": "S.T.Y.W: 3-8-6 Rising Martingale", "link": "https://youtu.be/-ZcEUOTHMzA" }, { "title": "S.T.Y.W: The Overlap Jackpot (4 Streets + 2 Dozens) Strategy", "link": "https://youtu.be/rTqdMQk4_I4" }, { "title": "S.T.Y.W: Double Up: Dozen & Street Strategy", "link": "https://youtu.be/Hod5gxusAVE" } ], "Double Street Strategies": [ { "title": "S.T.Y.W: Dynamic Play: 1 Dozen with 4 Streets or 2 Double Streets?", "link": "https://youtu.be/8aMHrvuzBGU" }, { "title": "S.T.Y.W: The Classic Five Double Street", "link": "https://youtu.be/XX7lSDElwWI" } ], "Corner Strategies": [ { "title": "S.T.Y.W: 4-Corners Strategy (Seq:1,1,2,5,8,17,28,50)", "link": "https://youtu.be/zw7eUllTDbg" } ], "Split Strategies": [ { "title": "S.T.Y.W: Triple Entry Max Climax Strategy", "link": "https://youtu.be/64aq0GEPww0" } ], "Number Strategies": [ { "title": "The Pulse Wheel Strategy (6 Numbers +1 Neighbours)", "link": "https://youtu.be/UBajAwUXWS0" }, { "title": "Eighteen Strong Numbers with No Neighbours Strategy", "link": "https://youtu.be/8Nmbi8KmY9c" } ], "Neighbours Strategies": [ { "title": "The Pulse Wheel Strategy (6 Numbers +1 Neighbours)", "link": "https://youtu.be/UBajAwUXWS0" }, { "title": "Triad Spin Strategy: 87.53% (Modified Makarov-Biarritz)", "link": "https://youtu.be/ADhCvxNiWVc" } ] } # 6. Row 6: Analyze Spins, Clear Spins, and Clear All Buttons with gr.Row(): with gr.Column(scale=2): analyze_button = gr.Button("Analyze Spins", elem_classes=["action-button", "green-btn"], interactive=True) with gr.Column(scale=1): clear_spins_button = gr.Button("Clear Spins", elem_classes=["clear-spins-btn", "small-btn"]) with gr.Column(scale=1): clear_all_button = gr.Button("Clear All", elem_classes=["clear-spins-btn", "small-btn"]) # 7. Row 7: Dynamic Roulette Table and Strategy Recommendations with gr.Row(elem_classes="dynamic-table-strategy-row"): # Column for Strategy Recommendations (Left Side) with gr.Column(scale=2, min_width=450, elem_classes="strategy-recommendations-container"): gr.Markdown("### Strategy Recommendations") # Wrap the entire section in a div with class "strategy-card" with gr.Row(elem_classes="strategy-card"): with gr.Column(scale=1): # Use a single column to stack elements vertically with gr.Row(): category_dropdown = gr.Dropdown( label="Select Category", choices=category_choices, value="Even Money Strategies", allow_custom_value=False, elem_id="select-category" ) strategy_dropdown = gr.Dropdown( label="Select Strategy", choices=strategy_categories["Even Money Strategies"], value="Best Even Money Bets", allow_custom_value=False, elem_id="strategy-dropdown" ) reset_strategy_button = gr.Button("Reset Category & Strategy", elem_classes=["action-button"]) neighbours_count_slider = gr.Slider( label="Number of Neighbors (Left + Right)", minimum=1, maximum=5, step=1, value=1, interactive=True, visible=False, elem_classes="long-slider" ) strong_numbers_count_slider = gr.Slider( label="Strong Numbers to Highlight (Neighbours Strategy)", minimum=1, maximum=18, step=1, value=1, interactive=True, visible=False, elem_classes="long-slider" ) strategy_output = gr.HTML( label="Strategy Recommendations", value=show_strategy_recommendations("Best Even Money Bets", 2, 1), elem_classes=["strategy-box"] ) # Column for Dynamic Roulette Table (Right Side) with gr.Column(scale=4, min_width=700, elem_classes="dynamic-table-container"): gr.Markdown("### Dynamic Roulette Table", elem_id="dynamic-table-heading") dynamic_table_output = gr.HTML( label="Dynamic Table", value=create_dynamic_table(strategy_name="Best Even Money Bets"), elem_classes=["scrollable-table", "large-table"] ) # 7.1. Row 7.1: Dozen Tracker with gr.Row(): with gr.Column(scale=3): with gr.Accordion("Create Dozen/Even Bet Triggers", open=False, elem_id="dozen-tracker"): gr.HTML(""" """) with gr.Accordion("Dozen Triggers", open=False, elem_id="dozen-triggers"): dozen_tracker_spins_dropdown = gr.Dropdown( label="Number of Spins to Track", choices=["3", "4", "5", "6", "10", "15", "20", "25", "30", "40", "50", "75", "100", "150", "200"], value="5", interactive=True ) dozen_tracker_consecutive_hits_dropdown = gr.Dropdown( label="Alert on Consecutive Dozen Hits", choices=["3", "4", "5"], value="3", interactive=True ) dozen_tracker_alert_checkbox = gr.Checkbox( label="Enable Consecutive Dozen Hits Alert", value=False, interactive=True ) dozen_tracker_sequence_length_dropdown = gr.Dropdown( label="Sequence Length to Match (X)", choices=["3", "4", "5"], value="4", interactive=True ) dozen_tracker_follow_up_spins_dropdown = gr.Dropdown( label="Follow-Up Spins to Track (Y)", choices=["3", "4", "5", "6", "7", "8", "9", "10"], value="5", interactive=True ) dozen_tracker_sequence_alert_checkbox = gr.Checkbox( label="Enable Sequence Matching Alert", value=False, interactive=True ) dozen_tracker_output = gr.HTML( label="Dozen Tracker", value="

Select the number of spins to track and analyze spins to see the Dozen history.

" ) dozen_tracker_sequence_output = gr.HTML( label="Sequence Matching Results", value="

Enable sequence matching to see results here.

" ) with gr.Accordion("Even Money", open=False, elem_id="even-money-tracker"): even_money_tracker_spins_dropdown = gr.Dropdown( label="Number of Spins to Track", choices=["1", "2", "3", "4", "5", "6", "10", "15", "20", "25", "30", "40", "50", "75", "100", "150", "200"], value="5", interactive=True ) even_money_tracker_consecutive_hits_dropdown = gr.Dropdown( label="Alert on Consecutive Even Money Hits", choices=["1", "2", "3", "4", "5"], value="3", interactive=True ) even_money_tracker_combination_mode_dropdown = gr.Dropdown( label="Combination Mode", choices=["And", "Or"], value="And", interactive=True ) even_money_tracker_identical_traits_checkbox = gr.Checkbox( label="Track Consecutive Identical Traits", value=False, interactive=True ) even_money_tracker_consecutive_identical_dropdown = gr.Dropdown( label="Number of Consecutive Identical Traits", choices=["1", "2", "3", "4", "5"], value="2", interactive=True ) with gr.Row(): even_money_tracker_red_checkbox = gr.Checkbox(label="Red", value=False, interactive=True) even_money_tracker_black_checkbox = gr.Checkbox(label="Black", value=False, interactive=True) even_money_tracker_even_checkbox = gr.Checkbox(label="Even", value=False, interactive=True) even_money_tracker_odd_checkbox = gr.Checkbox(label="Odd", value=False, interactive=True) even_money_tracker_low_checkbox = gr.Checkbox(label="Low", value=False, interactive=True) even_money_tracker_high_checkbox = gr.Checkbox(label="High", value=False, interactive=True) even_money_tracker_alert_checkbox = gr.Checkbox( label="Enable Even Money Hits Alert", value=False, interactive=True ) even_money_tracker_output = gr.HTML( label="Even Money Tracker", value="

Select categories to track and analyze spins to see even money bet history.

" ) with gr.Column(scale=2): pass # 8. Row 8: Betting Progression Tracker with gr.Accordion("Betting Progression Tracker", open=False, elem_id="betting-progression", elem_classes=["betting-progression"]): gr.HTML(""" """) with gr.Row(): bankroll_input = gr.Number(label="Bankroll", value=1000) base_unit_input = gr.Number(label="Base Unit", value=10) stop_loss_input = gr.Number(label="Stop Loss", value=-500) stop_win_input = gr.Number(label="Stop Win", value=200) target_profit_input = gr.Number(label="Target Profit (Units)", value=10, step=1) with gr.Row(): bet_type_dropdown = gr.Dropdown( label="Bet Type", choices=["Even Money", "Dozens", "Columns", "Streets", "Straight Bets"], value="Even Money" ) progression_dropdown = gr.Dropdown( label="Progression", choices=[ "Martingale", "Fibonacci", "Triple Martingale", "Ladder", "D’Alembert", "Double After a Win", "+1 Win / -1 Loss", "+2 Win / -1 Loss", "Double Loss / +50% Win", "Victory Vortex V.2" ], value="Martingale" ) with gr.Row(): win_button = gr.Button("Win") lose_button = gr.Button("Lose") reset_progression_button = gr.Button("Reset Progression") reset_bankroll_button = gr.Button("Reset Bankroll") with gr.Row(): bankroll_output = gr.Textbox(label="Current Bankroll", value="1000", interactive=False) current_bet_output = gr.Textbox(label="Current Bet", value="10", interactive=False) next_bet_output = gr.Textbox(label="Next Bet", value="10", interactive=False) with gr.Row(): message_output = gr.Textbox(label="Message", value="Start with base bet of 10 on Even Money (Martingale)", interactive=False) status_output = gr.HTML(label="Status", value='
Active
') # 8.1. Row 8.1: Casino Data Insights with gr.Row(): with gr.Accordion("Casino Data Insights", open=False, elem_classes=["betting-progression"], elem_id="casino-data-insights"): gr.HTML(""" """) spins_count_dropdown = gr.Dropdown( label="Past Spins Count", choices=["30", "50", "100", "200", "300", "500"], value="100", interactive=True ) with gr.Row(): even_percent = gr.Dropdown( label="Even %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) odd_percent = gr.Dropdown( label="Odd %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) with gr.Row(): red_percent = gr.Dropdown( label="Red %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) black_percent = gr.Dropdown( label="Black %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) with gr.Row(): low_percent = gr.Dropdown( label="Low %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) high_percent = gr.Dropdown( label="High %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) with gr.Row(): dozen1_percent = gr.Dropdown( label="1st Dozen %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) dozen2_percent = gr.Dropdown( label="2nd Dozen %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) dozen3_percent = gr.Dropdown( label="3rd Dozen %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) with gr.Row(): col1_percent = gr.Dropdown( label="1st Column %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) col2_percent = gr.Dropdown( label="2nd Column %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) col3_percent = gr.Dropdown( label="3rd Column %", choices=[f"{i:02d}" for i in range(100)], value="00", interactive=True ) use_winners_checkbox = gr.Checkbox( label="Highlight Casino Winners", value=False, interactive=True ) reset_casino_data_button = gr.Button( "Reset Casino Data", elem_classes=["action-button"] ) casino_data_output = gr.HTML( label="Casino Data Insights", value="

No casino data entered yet.

", elem_classes=["fade-in"] ) with gr.Accordion("Hot and Cold Numbers", open=False, elem_id="hot-cold-numbers"): with gr.Row(): gr.HTML('🔥') hot_numbers_input = gr.Textbox( label="Hot Numbers (1 to 10 comma-separated numbers, e.g., 1, 3, 5, 7, 9)", value="", interactive=True, placeholder="Enter 1 to 10 hot numbers" ) hot_suggestions = gr.Textbox( label="Suggested Hot Numbers (based on recent spins)", value="", interactive=False, elem_classes=["suggestion-box"] ) gr.Button("Use Suggested Hot Numbers", elem_classes=["action-button", "suggestion-btn"]).click( fn=lambda: state.hot_suggestions, inputs=[], outputs=[hot_numbers_input] ) with gr.Row(): gr.HTML('❄️') cold_numbers_input = gr.Textbox( label="Cold Numbers (1 to 10 comma-separated numbers, e.g., 2, 4, 6, 8, 10)", value="", interactive=True, placeholder="Enter 1 to 10 cold numbers" ) cold_suggestions = gr.Textbox( label="Suggested Cold Numbers (based on recent spins)", value="", interactive=False, elem_classes=["suggestion-box"] ) gr.Button("Use Suggested Cold Numbers", elem_classes=["action-button", "suggestion-btn"]).click( fn=lambda: state.cold_suggestions, inputs=[], outputs=[cold_numbers_input] ) with gr.Row(): play_hot_button = gr.Button("Play Hot Numbers", elem_classes=["action-button", "play-btn"]) play_cold_button = gr.Button("Play Cold Numbers", elem_classes=["action-button", "play-btn"]) with gr.Row(): clear_hot_button = gr.Button("Clear Hot Picks", elem_classes=["action-button", "clear-btn"]) clear_cold_button = gr.Button("Clear Cold Picks", elem_classes=["action-button", "clear-btn"]) # 9. Row 9: Color Code Key (Collapsible, with Color Pickers Inside) with gr.Accordion("Color Code Key", open=False, elem_id="color-code-key"): gr.HTML(""" """) with gr.Row(): top_color_picker = gr.ColorPicker( label="Top Tier Color", value="rgba(255, 255, 0, 0.5)", interactive=True, elem_id="top-color-picker" ) middle_color_picker = gr.ColorPicker( label="Middle Tier Color", value="rgba(0, 255, 255, 0.5)", interactive=True ) lower_color_picker = gr.ColorPicker( label="Lower Tier Color", value="rgba(0, 255, 0, 0.5)", interactive=True ) reset_colors_button = gr.Button("Reset Colors", elem_classes=["action-button"]) color_code_output = gr.HTML(label="Color Code Key") # 10. Row 10: Analysis Outputs (Collapsible, Renumbered) with gr.Accordion("Spin Logic Reactor 🧠", open=False, elem_id="spin-analysis"): gr.HTML(""" """) with gr.Row(elem_classes=["spin-analysis-row"]): spin_analysis_output = gr.Textbox( label="", value="", interactive=False, lines=5 ) with gr.Accordion("Strongest Numbers Tables", open=False, elem_id="strongest-numbers-table"): gr.HTML(""" """) with gr.Row(elem_classes=["strongest-numbers-row"]): with gr.Column(): straight_up_html = gr.HTML(label="Strongest Numbers", elem_classes="scrollable-table") with gr.Column(): top_18_html = gr.HTML(label="Top 18 Strongest Numbers (Sorted Lowest to Highest)", elem_classes="scrollable-table") with gr.Row(): strongest_numbers_dropdown = gr.Dropdown( label="Select Number of Strongest Numbers", choices=["3", "6", "9", "12", "15", "18", "21", "24", "27", "30", "33"], value="3", allow_custom_value=False, interactive=True, elem_id="strongest-numbers-dropdown", visible=False # Hide the dropdown ) strongest_numbers_output = gr.Textbox( label="Strongest Numbers (Sorted Lowest to Highest)", value="", lines=2, visible=False # Hide the textbox ) with gr.Accordion("Aggregated Scores", open=False, elem_id="aggregated-scores"): gr.HTML(""" """) with gr.Row(elem_classes=["aggregated-scores-row"]): with gr.Column(): with gr.Accordion("Even Money Bets", open=False): even_money_output = gr.Textbox(label="Even Money Bets", lines=10, max_lines=50) with gr.Column(): with gr.Accordion("Dozens", open=False): dozens_output = gr.Textbox(label="Dozens", lines=10, max_lines=50) with gr.Row(elem_classes=["aggregated-scores-row"]): with gr.Column(): with gr.Accordion("Columns", open=False): columns_output = gr.Textbox(label="Columns", lines=10, max_lines=50) with gr.Column(): with gr.Accordion("Streets", open=False): streets_output = gr.Textbox(label="Streets", lines=10, max_lines=50) with gr.Row(elem_classes=["aggregated-scores-row"]): with gr.Column(): with gr.Accordion("Corners", open=False): corners_output = gr.Textbox(label="Corners", lines=10, max_lines=50) with gr.Column(): with gr.Accordion("Double Streets", open=False): six_lines_output = gr.Textbox(label="Double Streets", lines=10, max_lines=50) with gr.Row(elem_classes=["aggregated-scores-row"]): with gr.Column(): with gr.Accordion("Splits", open=False): splits_output = gr.Textbox(label="Splits", lines=10, max_lines=50) with gr.Column(): with gr.Accordion("Sides of Zero", open=False): sides_output = gr.Textbox(label="Sides of Zero", lines=10, max_lines=50) # In the "Save/Load Session" accordion with gr.Accordion("Save/Load Session", open=False, elem_id="save-load-session"): with gr.Row(elem_classes=["save-load-row"]): # Text input for the file name session_name_input = gr.Textbox( label="Session File Name", placeholder="Enter session name (e.g., MySession)", value="WheelPulse_Session", interactive=True, elem_id="session-name-input" ) save_button = gr.Button("Save Session", elem_id="save-session-btn") load_input = gr.File(label="Upload Session", file_types=[".json"], elem_id="upload-session") save_output = gr.File(label="Download Session", elem_id="download-session") gr.HTML( ''' ''' ) # 11. Row 11: Top Strategies with WheelPulse by S.T.Y.W (Moved to be Independent) with gr.Row(): with gr.Column(): with gr.Accordion("Top Strategies with WheelPulse by S.T.Y.W 📈🎥", open=False, elem_id="top-strategies"): gr.HTML(""" """) gr.Markdown("### Explore Strategies Through Videos") video_category_dropdown = gr.Dropdown( label="Select Video Category", choices=sorted(video_categories.keys()), value="Dozen Strategies", allow_custom_value=False, elem_id="video-category-dropdown" ) video_dropdown = gr.Dropdown( label="Select Video", choices=[video["title"] for video in video_categories["Dozen Strategies"]], value=video_categories["Dozen Strategies"][0]["title"] if video_categories["Dozen Strategies"] else None, allow_custom_value=False, elem_id="video-dropdown" ) video_output = gr.HTML( label="Video", value=f'' if video_categories["Dozen Strategies"] else "

Select a category and video to watch.

" ) # Feedback & Suggestions section removed # CSS (end of the previous section, for context) gr.HTML(""" """) print("CSS Updated") # Shepherd.js Tour Script gr.HTML(""" """) # Event Handlers try: spins_textbox.change( fn=validate_spins_input, inputs=[spins_textbox], outputs=[spins_display, last_spin_display] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=analyze_spins, inputs=[spins_display, strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output, sides_of_zero_display ] ).then( fn=update_spin_counter, inputs=[], outputs=[spin_counter] ).then( fn=dozen_tracker, inputs=[dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox], outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=even_money_tracker, inputs=[ even_money_tracker_spins_dropdown, even_money_tracker_consecutive_hits_dropdown, even_money_tracker_alert_checkbox, even_money_tracker_combination_mode_dropdown, even_money_tracker_red_checkbox, even_money_tracker_black_checkbox, even_money_tracker_even_checkbox, even_money_tracker_odd_checkbox, even_money_tracker_low_checkbox, even_money_tracker_high_checkbox, even_money_tracker_identical_traits_checkbox, even_money_tracker_consecutive_identical_dropdown ], outputs=[gr.State(), even_money_tracker_output] ).then( fn=summarize_spin_traits, # Use summarize_spin_traits directly for now inputs=[last_spin_count], outputs=[traits_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, # Add voisins_slider to the list inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After spins_textbox change: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in spins_textbox.change handler: {str(e)}") gr.Warning(f"Error during spin analysis: {str(e)}") try: spins_display.change( fn=update_spin_counter, inputs=[], outputs=[spin_counter] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, # Add voisins_slider to the list inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After spins_display change: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in spins_display.change handler: {str(e)}") try: clear_spins_button.click( fn=clear_spins, inputs=[], outputs=[spins_display, spins_textbox, spin_analysis_output, last_spin_display, spin_counter, sides_of_zero_display] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, # Add voisins_slider to the list inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After clear_spins_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in clear_spins_button.click handler: {str(e)}") try: clear_all_button.click( fn=clear_all, inputs=[], outputs=[ spins_display, spins_textbox, spin_analysis_output, last_spin_display, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, spin_counter, sides_of_zero_display ] ).then( fn=clear_outputs, inputs=[], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output, color_code_output ] ).then( fn=dozen_tracker, inputs=[ dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox ], outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, # Add voisins_slider to the list inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After clear_all_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in clear_all_button.click handler: {str(e)}") try: generate_spins_button.click( fn=generate_random_spins, inputs=[gr.State(value="5"), spins_display, last_spin_count], outputs=[spins_display, spins_textbox, spin_analysis_output, spin_counter, sides_of_zero_display] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, # Add voisins_slider to the list inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After generate_spins_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in generate_spins_button.click handler: {str(e)}") # Line 1: Slider change handler (updated) try: last_spin_count.change( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, # Add voisins_slider to the list inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After last_spin_count change: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in last_spin_count.change handler: {str(e)}") def update_strategy_dropdown(category): if category == "None": return gr.update(choices=["None"], value="None") return gr.update(choices=strategy_categories[category], value=strategy_categories[category][0]) try: category_dropdown.change( fn=update_strategy_dropdown, inputs=category_dropdown, outputs=strategy_dropdown ) except Exception as e: print(f"Error in category_dropdown.change handler: {str(e)}") try: reset_strategy_button.click( fn=reset_strategy_dropdowns, inputs=[], outputs=[category_dropdown, strategy_dropdown, strategy_dropdown] ).then( fn=lambda category: gr.update(choices=strategy_categories[category], value=strategy_categories[category][0]), inputs=[category_dropdown], outputs=[strategy_dropdown] ) except Exception as e: print(f"Error in reset_strategy_button.click handler: {str(e)}") def toggle_neighbours_slider(strategy_name): is_visible = strategy_name == "Neighbours of Strong Number" return ( gr.update(visible=is_visible), gr.update(visible=is_visible) ) # New: Orchestrating function to combine analysis steps def orchestrate_analysis(spins_display, strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, dozen_consecutive_hits, dozen_alert, dozen_sequence_length, dozen_follow_up_spins, dozen_sequence_alert, even_money_spins, even_money_consecutive_hits, even_money_alert, even_money_combination_mode, red, black, even, odd, low, high, identical_traits, consecutive_identical, top_color, middle_color, lower_color): """Orchestrate analysis, producing all outputs in one pass with scores always reset.""" import time start_time = time.time() # Run analysis (scores are always reset in analyze_spins) spins_analysis, even_money, dozens, columns, streets, corners, six_lines, splits, sides, straight_up, top_18, strongest_numbers = analyze_spins(spins_display, strategy, neighbours_count, strong_numbers_count) # Run trackers and dynamic table dozen_text, dozen_html, dozen_sequence_html = dozen_tracker( dozen_tracker_spins, dozen_consecutive_hits, dozen_alert, dozen_sequence_length, dozen_follow_up_spins, dozen_sequence_alert ) even_money_text, even_money_html = even_money_tracker( even_money_spins, even_money_consecutive_hits, even_money_alert, even_money_combination_mode, red, black, even, odd, low, high, identical_traits, consecutive_identical ) dynamic_table = create_dynamic_table( strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color ) color_code = create_color_code_table() print(f"Analysis completed in {time.time() - start_time:.3f} seconds") return ( spins_analysis, even_money, dozens, columns, streets, corners, six_lines, splits, sides, straight_up, top_18, strongest_numbers, dynamic_table, show_strategy_recommendations(strategy, neighbours_count, strong_numbers_count), render_sides_of_zero_display(), dozen_text, dozen_html, dozen_sequence_html, even_money_text, even_money_html, color_code, analysis_cache.value ) try: strategy_dropdown.change( fn=toggle_neighbours_slider, inputs=[strategy_dropdown], outputs=[neighbours_count_slider, strong_numbers_count_slider] ).then( fn=show_strategy_recommendations, inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[strategy_output] ).then( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: (print(f"Updating Dynamic Table with Strategy: {strategy}, Neighbours Count: {neighbours_count}, Strong Numbers Count: {strong_numbers_count}, Dozen Tracker Spins: {dozen_tracker_spins}, Colors: {top_color}, {middle_color}, {lower_color}"), create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color))[-1], inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in strategy_dropdown.change handler: {str(e)}") # --------------------------------------------------------- # FIXED EVENT LISTENERS (With d17_strategy_slider included everywhere) # --------------------------------------------------------- # 1. Analyze Button try: analyze_button.click( fn=analyze_spins, inputs=[ spins_display, strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox, even_money_tracker_spins_dropdown, even_money_tracker_consecutive_hits_dropdown, even_money_tracker_alert_checkbox, even_money_tracker_combination_mode_dropdown, even_money_tracker_red_checkbox, even_money_tracker_black_checkbox, even_money_tracker_even_checkbox, even_money_tracker_odd_checkbox, even_money_tracker_low_checkbox, even_money_tracker_high_checkbox, even_money_tracker_identical_traits_checkbox, even_money_tracker_consecutive_identical_dropdown ], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output, sides_of_zero_display ] ).then( fn=lambda: ("", ""), inputs=[], outputs=[dynamic_table_output, strategy_output] ).then( fn=update_casino_data, inputs=[ spins_count_dropdown, even_percent, odd_percent, red_percent, black_percent, low_percent, high_percent, dozen1_percent, dozen2_percent, dozen3_percent, col1_percent, col2_percent, col3_percent, use_winners_checkbox ], outputs=[casino_data_output] ).then( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table( strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color ), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ).then( fn=create_color_code_table, inputs=[], outputs=[color_code_output] ).then( fn=dozen_tracker, inputs=[ dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox ], outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=even_money_tracker, inputs=[ even_money_tracker_spins_dropdown, even_money_tracker_consecutive_hits_dropdown, even_money_tracker_alert_checkbox, even_money_tracker_combination_mode_dropdown, even_money_tracker_red_checkbox, even_money_tracker_black_checkbox, even_money_tracker_even_checkbox, even_money_tracker_odd_checkbox, even_money_tracker_low_checkbox, even_money_tracker_high_checkbox, even_money_tracker_identical_traits_checkbox, even_money_tracker_consecutive_identical_dropdown ], outputs=[gr.State(), even_money_tracker_output] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After analyze_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in analyze_button.click handler: {str(e)}") gr.Warning(f"Error during analysis: {str(e)}") # 2. Save Session Button try: save_button.click( fn=save_session, inputs=[session_name_input], outputs=[save_output] ) except Exception as e: print(f"Error in save_button.click handler: {str(e)}") # 3. Load Session Input try: load_input.change( fn=load_session, inputs=[load_input, strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[ spins_display, spins_textbox, spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output ] ).then( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table( strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color ), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=create_color_code_table, inputs=[], outputs=[color_code_output] ).then( fn=dozen_tracker, inputs=[dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox], outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=even_money_tracker, inputs=[even_money_tracker_spins_dropdown, even_money_tracker_consecutive_hits_dropdown, even_money_tracker_alert_checkbox, even_money_tracker_combination_mode_dropdown, even_money_tracker_red_checkbox, even_money_tracker_black_checkbox, even_money_tracker_even_checkbox, even_money_tracker_odd_checkbox, even_money_tracker_low_checkbox, even_money_tracker_high_checkbox, even_money_tracker_identical_traits_checkbox, even_money_tracker_consecutive_identical_dropdown], outputs=[gr.State(), even_money_tracker_output] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After load_input change: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in load_input.change handler: {str(e)}") # 4. Undo Button try: undo_button.click( fn=undo_last_spin, inputs=[spins_display, gr.State(value=1), strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, spins_textbox, spins_display, dynamic_table_output, strategy_output, color_code_output, spin_counter, sides_of_zero_display ] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table( strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color ), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ).then( fn=dozen_tracker, inputs=[dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox], outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After undo_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in undo_button.click handler: {str(e)}") # 5. Neighbours Count Slider try: neighbours_count_slider.change( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ).then( fn=show_strategy_recommendations, inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[strategy_output] ) except Exception as e: print(f"Error in neighbours_count_slider.change handler: {str(e)}") # 6. Strong Numbers Count Slider try: strong_numbers_count_slider.change( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ).then( fn=show_strategy_recommendations, inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[strategy_output] ) except Exception as e: print(f"Error in strong_numbers_count_slider.change handler: {str(e)}") # 7. Reset Colors Button try: reset_colors_button.click( fn=reset_colors, inputs=[], outputs=[top_color_picker, middle_color_picker, lower_color_picker] ).then( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in reset_colors_button.click handler: {str(e)}") # 8. Toggle Trends Button try: toggle_trends_button.click( fn=toggle_trends, inputs=[show_trends_state, toggle_trends_label], outputs=[show_trends_state, toggle_trends_label, toggle_trends_button] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After toggle_trends_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in toggle_trends_button.click handler: {str(e)}") # 9. Color Pickers (Top, Middle, Lower) try: top_color_picker.change( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in top_color_picker.change handler: {str(e)}") try: middle_color_picker.change( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in middle_color_picker.change handler: {str(e)}") try: lower_color_picker.change( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in lower_color_picker.change handler: {str(e)}") # 10. Dozen Tracker Dropdowns/Checkboxes try: dozen_inputs = [dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox] for inp in dozen_inputs: inp.change( fn=dozen_tracker, inputs=dozen_inputs, outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in dozen_tracker inputs handler: {str(e)}") # 11. Even Money Tracker Dropdowns/Checkboxes try: even_money_inputs = [ even_money_tracker_spins_dropdown, even_money_tracker_consecutive_hits_dropdown, even_money_tracker_alert_checkbox, even_money_tracker_combination_mode_dropdown, even_money_tracker_red_checkbox, even_money_tracker_black_checkbox, even_money_tracker_even_checkbox, even_money_tracker_odd_checkbox, even_money_tracker_low_checkbox, even_money_tracker_high_checkbox, even_money_tracker_identical_traits_checkbox, even_money_tracker_consecutive_identical_dropdown ] for inp in even_money_inputs: inp.change( fn=even_money_tracker, inputs=even_money_inputs, outputs=[gr.State(), even_money_tracker_output] ) except Exception as e: print(f"Error in even_money_tracker inputs handler: {str(e)}") # 12. Casino Data Inputs (Dropdowns + Checkbox) casino_inputs = [ spins_count_dropdown, even_percent, odd_percent, red_percent, black_percent, low_percent, high_percent, dozen1_percent, dozen2_percent, dozen3_percent, col1_percent, col2_percent, col3_percent, use_winners_checkbox ] try: for inp in casino_inputs: inp.change( fn=update_casino_data, inputs=casino_inputs, outputs=[casino_data_output] ).then( fn=lambda strategy, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color: create_dynamic_table(strategy if strategy != "None" else None, neighbours_count, strong_numbers_count, dozen_tracker_spins, top_color, middle_color, lower_color), inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in casino data inputs handler: {str(e)}") # 13. Reset Casino Data Button try: reset_casino_data_button.click( fn=lambda: ( "100", "00", "00", "00", "00", "00", "00", "00", "00", "00", "00", "00", "00", False, "", "", "

Casino data reset to defaults.

" ), inputs=[], outputs=[ spins_count_dropdown, even_percent, odd_percent, red_percent, black_percent, low_percent, high_percent, dozen1_percent, dozen2_percent, dozen3_percent, col1_percent, col2_percent, col3_percent, use_winners_checkbox, hot_numbers_input, cold_numbers_input, casino_data_output ] ).then( fn=create_dynamic_table, inputs=[strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider, dozen_tracker_spins_dropdown, top_color_picker, middle_color_picker, lower_color_picker], outputs=[dynamic_table_output] ) except Exception as e: print(f"Error in reset_casino_data_button.click handler: {str(e)}") # 14. Play Hot/Cold Buttons try: play_hot_button.click( fn=play_specific_numbers, inputs=[hot_numbers_input, gr.State(value="Hot"), spins_display, last_spin_count], outputs=[spins_display, spins_textbox, casino_data_output, spin_counter, sides_of_zero_display] ).then( fn=sync_spins_display, inputs=[spins_display], outputs=[spins_display] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=analyze_spins, inputs=[spins_display, strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output, sides_of_zero_display ] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=suggest_hot_cold_numbers, inputs=[], outputs=[hot_suggestions, cold_suggestions] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After play_hot_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in play_hot_button.click handler: {str(e)}") try: play_cold_button.click( fn=play_specific_numbers, inputs=[cold_numbers_input, gr.State(value="Cold"), spins_display, last_spin_count], outputs=[spins_display, spins_textbox, casino_data_output, spin_counter, sides_of_zero_display] ).then( fn=sync_spins_display, inputs=[spins_display], outputs=[spins_display] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=analyze_spins, inputs=[spins_display, strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output, sides_of_zero_display ] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After play_cold_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in play_cold_button.click handler: {str(e)}") # 15. Clear Hot/Cold Buttons try: clear_hot_button.click( fn=clear_hot_cold_picks, inputs=[gr.State(value="Hot"), spins_display], outputs=[hot_numbers_input, casino_data_output, spin_counter, sides_of_zero_display, spins_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After clear_hot_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in clear_hot_button.click handler: {str(e)}") try: clear_cold_button.click( fn=clear_hot_cold_picks, inputs=[gr.State(value="Cold"), spins_display], outputs=[cold_numbers_input, casino_data_output, spin_counter, sides_of_zero_display, spins_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After clear_cold_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in clear_cold_button.click handler: {str(e)}") # 16. Spins Textbox/Display Changes (FIXED: Prevents Double Execution & Freezing) # 1. Textbox Input: ONLY Validates and Updates State (Lightweight) try: spins_textbox.change( fn=validate_spins_input, inputs=[spins_textbox], outputs=[spins_display, last_spin_display] ) # Note: We removed the .then() chain here. Updating 'spins_display' # automatically triggers the 'spins_display.change' listener below. except Exception as e: print(f"Error in spins_textbox.change handler: {str(e)}") # 2. State Change: The Single Source of Truth (Heavy Logic runs ONCE here) try: spins_display.change( fn=update_spin_counter, inputs=[], outputs=[spin_counter] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=analyze_spins, inputs=[spins_display, strategy_dropdown, neighbours_count_slider, strong_numbers_count_slider], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output, sides_of_zero_display ] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( # FIXED: Includes all 10 inputs for the new DE2D Logic fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=dozen_tracker, inputs=[dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox], outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=even_money_tracker, inputs=[ even_money_tracker_spins_dropdown, even_money_tracker_consecutive_hits_dropdown, even_money_tracker_alert_checkbox, even_money_tracker_combination_mode_dropdown, even_money_tracker_red_checkbox, even_money_tracker_black_checkbox, even_money_tracker_even_checkbox, even_money_tracker_odd_checkbox, even_money_tracker_low_checkbox, even_money_tracker_high_checkbox, even_money_tracker_identical_traits_checkbox, even_money_tracker_consecutive_identical_dropdown ], outputs=[gr.State(), even_money_tracker_output] ).then( fn=lambda: print(f"State Updated: Analysis Complete."), inputs=[], outputs=[] ) except Exception as e: print(f"Error in spins_display.change handler: {str(e)}") try: clear_spins_button.click( fn=clear_spins, inputs=[], outputs=[spins_display, spins_textbox, spin_analysis_output, last_spin_display, spin_counter, sides_of_zero_display] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( # FIXED: Added d17_strategy_slider fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After clear_spins_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in clear_spins_button.click handler: {str(e)}") try: clear_all_button.click( fn=clear_all, inputs=[], outputs=[ spins_display, spins_textbox, spin_analysis_output, last_spin_display, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, spin_counter, sides_of_zero_display ] ).then( fn=clear_outputs, inputs=[], outputs=[ spin_analysis_output, even_money_output, dozens_output, columns_output, streets_output, corners_output, six_lines_output, splits_output, sides_output, straight_up_html, top_18_html, strongest_numbers_output, dynamic_table_output, strategy_output, color_code_output ] ).then( fn=dozen_tracker, inputs=[dozen_tracker_spins_dropdown, dozen_tracker_consecutive_hits_dropdown, dozen_tracker_alert_checkbox, dozen_tracker_sequence_length_dropdown, dozen_tracker_follow_up_spins_dropdown, dozen_tracker_sequence_alert_checkbox], outputs=[gr.State(), dozen_tracker_output, dozen_tracker_sequence_output] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( # FIXED: Added d17_strategy_slider fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After clear_all_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in clear_all_button.click handler: {str(e)}") try: generate_spins_button.click( fn=generate_random_spins, inputs=[gr.State(value="5"), spins_display, last_spin_count], outputs=[spins_display, spins_textbox, spin_analysis_output, spin_counter, sides_of_zero_display] ).then( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( # FIXED: Added d17_strategy_slider fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After generate_spins_button click: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in generate_spins_button.click handler: {str(e)}") try: last_spin_count.change( fn=lambda spins_display, count, show_trends: format_spins_as_html(spins_display, count, show_trends), inputs=[spins_display, last_spin_count, show_trends_state], outputs=[last_spin_display] ).then( fn=summarize_spin_traits, inputs=[last_spin_count], outputs=[traits_display] ).then( fn=calculate_hit_percentages, inputs=[last_spin_count], outputs=[hit_percentage_display] ).then( fn=select_next_spin_top_pick, inputs=[top_pick_spin_count], outputs=[top_pick_display] ).then( # FIXED: Added d17_strategy_slider fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After last_spin_count change: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in last_spin_count.change handler: {str(e)}") # Toggle Labouchere try: progression_dropdown.change( fn=toggle_labouchere, inputs=[progression_dropdown], outputs=[] # No output needed for hidden element management in this snippet ) except Exception as e: print(f"Error in progression_dropdown.change handler: {str(e)}") # 17. Weight Inputs for Top Pick for weight_input in [ trait_match_weight, secondary_match_weight, wheel_side_weight, section_weight, recency_weight, hit_bonus_weight, neighbor_weight ]: try: weight_input.change( fn=select_next_spin_top_pick, inputs=[ top_pick_spin_count, trait_filter, trait_match_weight, secondary_match_weight, wheel_side_weight, section_weight, recency_weight, hit_bonus_weight, neighbor_weight ], outputs=[top_pick_display] ).then( # FIXED: Added d17_strategy_slider fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After weight change: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in weight_input.change handler: {str(e)}") # 18. Trait Filter try: trait_filter.change( fn=select_next_spin_top_pick, inputs=[ top_pick_spin_count, trait_filter, trait_match_weight, secondary_match_weight, wheel_side_weight, section_weight, recency_weight, hit_bonus_weight, neighbor_weight ], outputs=[top_pick_display] ).then( # FIXED: Added grind_active_checkbox and grind_target_dropdown fn=de2d_tracker_logic, inputs=[miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown], outputs=[de2d_output] ).then( fn=lambda: print(f"After trait_filter change: state.last_spins = {state.last_spins}"), inputs=[], outputs=[] ) except Exception as e: print(f"Error in trait_filter.change handler: {str(e)}") # 19. Reset Weights try: reset_weights_button.click( fn=lambda spin_count, traits: ( 100, 10, 5, 10, 1, 5, 2, select_next_spin_top_pick(spin_count, traits, 100, 10, 5, 10, 1, 5, 2) ), inputs=[top_pick_spin_count, trait_filter], outputs=[ trait_match_weight, secondary_match_weight, wheel_side_weight, section_weight, recency_weight, hit_bonus_weight, neighbor_weight, top_pick_display ] ) except Exception as e: print(f"Error in reset_weights_button.click handler: {str(e)}") # --- NEW: Reset Grind Button Handler --- def reset_grind_step(miss, even, streak, pattern, voisins, tiers, left, right, ds, d17, corner, active, target): state.grind_step_index = 0 state.grind_last_spin_count = len(state.last_spins) # Sync spin count so it doesn't auto-advance # Re-run logic to update display immediately return de2d_tracker_logic(miss, even, streak, pattern, voisins, tiers, left, right, ds, d17, corner, active, target) try: reset_grind_button.click( fn=reset_grind_step, inputs=[ miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown ], outputs=[de2d_output] ) except Exception as e: print(f"Error in reset_grind_button.click handler: {str(e)}") # 20. DE2D Sliders (The final catch-all for slider changes) try: # UPDATED: de2d_inputs now includes the grind controls de2d_inputs = [ miss_slider, even_slider, streak_slider, pattern_slider, voisins_slider, tiers_slider, left_side_slider, right_side_slider, ds_strategy_slider, d17_strategy_slider, corner_strategy_slider, grind_active_checkbox, grind_target_dropdown ] miss_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) even_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) streak_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) pattern_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) voisins_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) tiers_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) left_side_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) right_side_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) ds_strategy_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) d17_strategy_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) corner_strategy_slider.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) # Also trigger logic when Grind controls change grind_active_checkbox.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) grind_target_dropdown.change(fn=de2d_tracker_logic, inputs=de2d_inputs, outputs=[de2d_output]) except Exception as e: print(f"Error in DE2D slider handlers: {str(e)}") # Launch the interface print("Starting Gradio launch...") demo.launch() print("Gradio launch completed.")