diff --git a/scripts/generators/ultimate_ai_ml_eurojackpot_generator.py b/scripts/generators/ultimate_ai_ml_eurojackpot_generator.py index 7cc1b51..b0fa6ae 100644 --- a/scripts/generators/ultimate_ai_ml_eurojackpot_generator.py +++ b/scripts/generators/ultimate_ai_ml_eurojackpot_generator.py @@ -92,7 +92,12 @@ class UltimateAIMLEurojackpotGenerator: """ Ultimate Eurojackpot Generator - ALLE Strategien kombiniert """ - + + # 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 @@ -441,7 +446,7 @@ class UltimateAIMLEurojackpotGenerator: """High-EV: 2 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 = UltimateAIMLEurojackpotGenerator.POPULAR_PLAYER_PICKS above_31_target = 2 # für 5 Zahlen: 2 above-31 = solide EV-Basis selected_main = [] @@ -671,41 +676,91 @@ class UltimateAIMLEurojackpotGenerator: return (distance_score * 0.6 + range_score * 0.4) def _get_smart_euro_numbers(self, tip_number, euro_preds): - """Intelligente Euro-Zahlen Auswahl.""" - sorted_euro = sorted(euro_preds.items(), key=lambda x: x[1], reverse=True) - - # Strategy based on tip number - if tip_number <= 3: - # Top predictions - return sorted([num for num, _ in sorted_euro[:2]]) - else: - # Mix of top and diversity - top_euros = [num for num, _ in sorted_euro[:6]] - random.seed(42 + tip_number * 7) - return sorted(random.sample(top_euros, 2)) + """ + EV-bewusste Euro-Zahlen-Auswahl: gewichtete Zufallswahl aus den + AI-Predictions statt starrer Top-2 (vorher: Tipps 1-3 bekamen IMMER + exakt dasselbe Euro-Paar), mit Abwertung klassischer Glückszahlen + (3, 7) - analog zur Hauptzahlen-Logik in _generate_high_ev_tip(). + + Schwächer empirisch belegt als die Hauptzahlen-Heuristik: 1-12 + überschneidet sich mit Kalendermonaten, es gibt keine klare + Geburtstags-Grenze wie bei den Hauptzahlen (>31). Deshalb bewusst + konservativ: nur die zwei bekanntesten Glückszahlen abgewertet, + keine erfundene Rangfolge aller 12 Ziffern. + """ + random.seed(100 + tip_number * 13) + lucky_euro = {3, 7} + + pool = list(euro_preds.keys()) + weights = [] + for n in pool: + w = euro_preds.get(n, 0.1) + if n in lucky_euro: + w *= 0.5 + weights.append(max(0.001, w)) + + chosen = [] + for _ in range(2): + pick = random.choices(pool, weights=weights)[0] + idx = pool.index(pick) + pool.pop(idx) + weights.pop(idx) + chosen.append(pick) + + return sorted(chosen) @staticmethod def _calculate_popularity_score(main_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) und "zufällig + aussehende" ausgeglichene Odd/Even-Splits werden von Menschen + überproportional oft gewählt - jede einzelne Kombination ist aber exakt + gleich wahrscheinlich, unabhängig von diesen Eigenschaften. + + Hinweis: eine "Summe nahe am Mittel"-Komponente wurde bewusst NICHT + aufgenommen - sie widerspricht sich mit der Muster-Erkennung, da gerade + die meistgespielten sequenziellen Kombinationen (z.B. 1-2-3-4-5) durch + ihre enge Zahlen-Clusterung zugleich eine extreme Summe erzeugen. """ n = len(main_numbers) sorted_nums = sorted(main_numbers) - # Anteil Zahlen > 31 (Geburtstags-Range vermeiden) + # 1) Geburtstags-Range (1-31) meiden above_31_ratio = sum(1 for x in main_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 main_numbers + if x in UltimateAIMLEurojackpotGenerator.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 main_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 + # 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" Splits, + # obwohl jede einzelne Kombination gleich wahrscheinlich ist. Bei 5 + # Zahlen ist der ausgeglichenste Split 2-3/3-2 (kein exaktes Halbieren + # möglich), daher Distanz zum Mittelpunkt 2.5 statt n/2. + even_count = sum(1 for x in main_numbers if x % 2 == 0) + split_extremity = abs(even_count - (n / 2)) / (n / 2) + + score = ( + above_31_ratio * 0.30 + + (1 - popular_ratio) * 0.20 + + (1 - pattern_penalty) * 0.30 + + split_extremity * 0.20 + ) return min(max(score, 0.0), 1.0) def _calculate_quality_score(self, main_numbers, euro_numbers, main_preds, euro_preds, pattern_weight): @@ -741,13 +796,17 @@ class UltimateAIMLEurojackpotGenerator: else: recency_quality = 0.5 + # 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 = ( - main_quality * 0.25 - + euro_quality * 0.15 - + pattern_quality * 0.15 + main_quality * 0.15 + + euro_quality * 0.10 + + pattern_quality * 0.10 + diversity_quality * 0.10 - + popularity_quality * 0.20 - + recency_quality * 0.15 + + popularity_quality * 0.45 + + recency_quality * 0.10 ) return min(quality, 1.0)