diff --git a/scripts/generators/ultimate_ai_ml_hybrid_generator.py b/scripts/generators/ultimate_ai_ml_hybrid_generator.py index 35ba3c6..f045295 100644 --- a/scripts/generators/ultimate_ai_ml_hybrid_generator.py +++ b/scripts/generators/ultimate_ai_ml_hybrid_generator.py @@ -74,7 +74,12 @@ class UltimateAIMLHybridGenerator: """ Ultimate Generator - OPTIMIZED VERSION """ - + + # Empirisch belegte "griffige" Einzelzahlen, die Spieler überproportional oft frei + # wählen (Quelle: Analysen realer Tippscheine, z.B. frz. 6aus49 über 25 Jahre: + # populärste Zahlen 5,7,9,11,12,13). Unabhängig vom Geburtstags-Bias (<=31). + POPULAR_PLAYER_PICKS = {5, 7, 9, 11, 12, 13} + def __init__(self, data_path, fast_mode=True): self.data_path = data_path self.df = None @@ -406,7 +411,7 @@ class UltimateAIMLHybridGenerator: """High-EV: 2–3 Zahlen >31, max. 1 Lucky Number, soft Consecutive-Vermeidung.""" random.seed(42 + tip_number * 41) - lucky_numbers = {3, 7, 9, 11, 13, 17, 19, 21, 23} + lucky_numbers = UltimateAIMLHybridGenerator.POPULAR_PLAYER_PICKS above_31_target = 2 + (tip_number % 2) # wechselt zwischen 2 und 3 selected = [] @@ -628,49 +633,66 @@ class UltimateAIMLHybridGenerator: return (distance_score * 0.4 + range_score * 0.4 + balance_score * 0.2) def _get_smart_superzahl(self, tip_number): - """Intelligente Superzahl.""" - base_sz = [7, 6, 3, 2, 0, 1, 4, 5, 8, 9] - - try: - if 'SZ' in self.df.columns and len(self.df) > 10: - recent_sz = self.df['SZ'].tail(30).dropna() - if len(recent_sz) > 0: - sz_freq = Counter(recent_sz) - top_sz = [int(sz) for sz, _ in sz_freq.most_common(7) if 0 <= sz <= 9] - if top_sz: - base_sz = top_sz + [7, 6, 3] - except: - pass - - if tip_number <= 3: - sz = base_sz[0] - else: - sz = base_sz[tip_number % len(base_sz)] - - return int(sz) if 0 <= int(sz) <= 9 else 7 + """ + EV-optimierte Superzahl: bevorzugt Ziffern, die Spieler erfahrungsgemäß + seltener frei wählen (0 wirkt nicht wie eine "eigene" Zahl; 7/3 gelten als + klassische Glückszahlen und werden überproportional gewählt). Historische + Ziehungshäufigkeit fließt bewusst NICHT mehr ein - die SZ wird pro Ziehung + unabhängig und gleichverteilt (0-9) gezogen, "häufig gezogen in letzten 30 + Ziehungen" ist reines Rauschen ohne Vorhersagewert. + """ + ev_order = [0, 9, 8, 4, 2, 6, 1, 5, 3, 7] # unpopulärste zuerst + return ev_order[tip_number % len(ev_order)] @staticmethod def _calculate_popularity_score(numbers): """ Schätzt den Erwartungswert-Vorteil durch Vermeidung populärer Zahlenkombinationen. - Höher = unpopulärer = höherer Gewinnanteil bei einem Treffer. - Basis: Spieler bevorzugen Geburtstagszahlen (1-31), Glückszahlen und Zahlenfolgen. + Höher = unpopulärer bei Mitspielern = höherer Gewinnanteil bei einem Treffer + (Hit-Wahrscheinlichkeit selbst ist bei i.i.d. Ziehungen nicht beeinflussbar). + + Basis: dokumentierte Spielerverhalten-Biases (Henze/Riedwyl-artige Analysen + realer Tippscheine): Geburtstagszahlen (<=31), einzelne "griffige" Zahlen, + Zahlenfolgen/arithmetische Muster (z.B. 5-10-15-20-25-30) und "zufällig + aussehende" ausgeglichene Odd/Even-Splits + mittige Summen werden von + Menschen überproportional oft gewählt - jede einzelne Kombination ist aber + exakt gleich wahrscheinlich, unabhängig von diesen Eigenschaften. """ n = len(numbers) sorted_nums = sorted(numbers) - # Anteil Zahlen > 31 (Geburtstags-Range vermeiden) + # 1) Geburtstags-Range (1-31) meiden above_31_ratio = sum(1 for x in numbers if x > 31) / n - # Aufeinanderfolgende Zahlen vermeiden (visuelle Muster) - consecutive_pairs = sum(1 for i in range(n - 1) if sorted_nums[i + 1] - sorted_nums[i] == 1) - consecutive_ratio = consecutive_pairs / (n - 1) if n > 1 else 0 + # 2) Empirisch belegte populäre Einzelzahlen meiden + popular_ratio = sum( + 1 for x in numbers + if x in UltimateAIMLHybridGenerator.POPULAR_PLAYER_PICKS + ) / n - # Häufig gespielte "Glückszahlen" vermeiden - lucky_numbers = {3, 7, 9, 11, 13, 17, 19, 21, 23} - lucky_ratio = sum(1 for x in numbers if x in lucky_numbers) / n + # 3) Muster/Zahlenfolgen meiden: direkte Nachbarn UND allgemeine + # arithmetische Folgen (konstante Schrittweite, z.B. 5-10-15-20-25-30 + # zählt zu den meistgespielten Kombinationen überhaupt) + diffs = [sorted_nums[i + 1] - sorted_nums[i] for i in range(n - 1)] + small_step_ratio = sum(1 for d in diffs if d <= 5) / (n - 1) if diffs else 0 + is_perfect_progression = len(diffs) > 1 and len(set(diffs)) == 1 + pattern_penalty = small_step_ratio * (0.6 if not is_perfect_progression else 1.0) - score = above_31_ratio * 0.5 + (1 - consecutive_ratio) * 0.3 + (1 - lucky_ratio) * 0.2 + # 4) Odd/Even-Split: Menschen bevorzugen "ausgeglichen aussehende" 3-3 + # Splits, obwohl jede einzelne Kombination gleich wahrscheinlich ist + even_count = sum(1 for x in numbers if x % 2 == 0) + split_extremity = abs(even_count - 3) / 3 # 0.0 bei 3-3, 1.0 bei 6-0/0-6 + + # 5) Summe: Menschen bevorzugen Summen nahe dem statistischen Mittel (~150) + sum_extremity = min(abs(sum(numbers) - 150) / 100.0, 1.0) + + score = ( + above_31_ratio * 0.25 + + (1 - popular_ratio) * 0.15 + + (1 - pattern_penalty) * 0.20 + + split_extremity * 0.20 + + sum_extremity * 0.20 + ) return min(max(score, 0.0), 1.0) def _calculate_quality_score(self, numbers, ai_predictions, pattern_weight): @@ -702,12 +724,16 @@ class UltimateAIMLHybridGenerator: else: recency_quality = 0.5 # neutral wenn noch nicht berechnet + # Popularity/EV dominiert bewusst: AI-Score, Pattern und Recency sagen nichts + # über die (unbeeinflussbare) Trefferwahrscheinlichkeit aus - Recency/Pattern + # folgen zudem Heuristiken, die auch andere Systemspieler nutzen und damit die + # Popularity eher untergraben statt sie zu unterstützen. quality = ( - ai_quality * 0.30 - + pattern_quality * 0.20 + ai_quality * 0.15 + + pattern_quality * 0.10 + diversity_quality * 0.15 - + popularity_quality * 0.20 - + recency_quality * 0.15 + + popularity_quality * 0.50 + + recency_quality * 0.10 ) return min(quality, 1.0)