195 lines
6.0 KiB
Python
195 lines
6.0 KiB
Python
import math
|
|
import os
|
|
import platform
|
|
import random
|
|
from collections import Counter
|
|
|
|
from django import get_version as django_version
|
|
from django.shortcuts import render
|
|
from django.utils import timezone
|
|
|
|
from .forms import LotterySimulatorForm
|
|
|
|
|
|
LOTTERY_CONFIGS = {
|
|
"mega_sena": {
|
|
"label": "Mega-Sena",
|
|
"range_max": 60,
|
|
"picks": 6,
|
|
"sample_draws": 15,
|
|
"seed": 1982,
|
|
"tagline": "6 dezenas entre 60",
|
|
},
|
|
"quina": {
|
|
"label": "Quina",
|
|
"range_max": 80,
|
|
"picks": 5,
|
|
"sample_draws": 15,
|
|
"seed": 1971,
|
|
"tagline": "5 dezenas entre 80",
|
|
},
|
|
"dupla_sena": {
|
|
"label": "Dupla Sena",
|
|
"range_max": 50,
|
|
"picks": 6,
|
|
"sample_draws": 15,
|
|
"seed": 1990,
|
|
"tagline": "6 dezenas entre 50",
|
|
},
|
|
"lotomania": {
|
|
"label": "Lotomania",
|
|
"range_max": 100,
|
|
"picks": 50,
|
|
"sample_draws": 12,
|
|
"seed": 1999,
|
|
"tagline": "50 dezenas entre 100",
|
|
},
|
|
"lotofacil": {
|
|
"label": "Lotofacil",
|
|
"range_max": 25,
|
|
"picks": 15,
|
|
"sample_draws": 20,
|
|
"seed": 1994,
|
|
"tagline": "15 dezenas entre 25",
|
|
},
|
|
}
|
|
|
|
|
|
def _generate_sample_draws(config):
|
|
rng = random.Random(config["seed"])
|
|
draws = []
|
|
population = list(range(1, config["range_max"] + 1))
|
|
for _ in range(config["sample_draws"]):
|
|
draws.append(sorted(rng.sample(population, config["picks"])))
|
|
return draws
|
|
|
|
|
|
def _weighted_unique_sample(numbers, weights, picks, rng):
|
|
available = list(zip(numbers, weights))
|
|
selection = []
|
|
for _ in range(picks):
|
|
total_weight = sum(weight for _, weight in available)
|
|
if total_weight <= 0:
|
|
choice = rng.choice(available)
|
|
selection.append(choice[0])
|
|
available.remove(choice)
|
|
continue
|
|
pick = rng.uniform(0, total_weight)
|
|
cumulative = 0
|
|
for index, (number, weight) in enumerate(available):
|
|
cumulative += weight
|
|
if cumulative >= pick:
|
|
selection.append(number)
|
|
del available[index]
|
|
break
|
|
return selection
|
|
|
|
|
|
def _format_odds(total_combinations):
|
|
if total_combinations >= 1_000_000_000_000:
|
|
return f"1 em {total_combinations:.2e}"
|
|
return f"1 em {total_combinations:,}".replace(",", ".")
|
|
|
|
|
|
def _format_percent(odds):
|
|
percent = 100 / odds
|
|
if percent < 0.000001:
|
|
return "<0,000001%"
|
|
return f"{percent:.6f}%".replace(".", ",")
|
|
|
|
|
|
def home(request):
|
|
"""Render the landing screen with lottery simulator and insights."""
|
|
host_name = request.get_host().lower()
|
|
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
|
now = timezone.now()
|
|
|
|
lottery_choices = [
|
|
(key, config["label"]) for key, config in LOTTERY_CONFIGS.items()
|
|
]
|
|
lottery_cards = []
|
|
for key, config in LOTTERY_CONFIGS.items():
|
|
total_combinations = math.comb(config["range_max"], config["picks"])
|
|
lottery_cards.append(
|
|
{
|
|
"key": key,
|
|
"label": config["label"],
|
|
"tagline": config["tagline"],
|
|
"range_max": config["range_max"],
|
|
"picks": config["picks"],
|
|
"odds": _format_odds(total_combinations),
|
|
}
|
|
)
|
|
|
|
form = LotterySimulatorForm(
|
|
request.POST or None,
|
|
lottery_choices=lottery_choices,
|
|
)
|
|
result = None
|
|
|
|
if form.is_valid():
|
|
lottery_key = form.cleaned_data["lottery_type"]
|
|
draws_to_consider = form.cleaned_data["draws_to_consider"]
|
|
games_to_generate = form.cleaned_data["games_to_generate"]
|
|
config = LOTTERY_CONFIGS[lottery_key]
|
|
draws = _generate_sample_draws(config)
|
|
draws_to_consider = min(draws_to_consider, len(draws))
|
|
recent_draws = draws[-draws_to_consider:]
|
|
frequency = Counter(
|
|
number for draw in recent_draws for number in draw
|
|
)
|
|
numbers = list(range(1, config["range_max"] + 1))
|
|
weights = [frequency.get(number, 0) + 1 for number in numbers]
|
|
rng = random.Random(f"{lottery_key}-{draws_to_consider}-{games_to_generate}")
|
|
|
|
suggestions = []
|
|
for _ in range(games_to_generate):
|
|
suggestion = _weighted_unique_sample(
|
|
numbers, weights, config["picks"], rng
|
|
)
|
|
suggestions.append(sorted(suggestion))
|
|
|
|
total_combinations = math.comb(config["range_max"], config["picks"])
|
|
hot_numbers = [
|
|
number for number, _ in frequency.most_common(8)
|
|
]
|
|
cold_numbers = [
|
|
number
|
|
for number, _ in sorted(
|
|
frequency.items(), key=lambda item: item[1]
|
|
)
|
|
]
|
|
missing_numbers = [
|
|
number for number in numbers if number not in frequency
|
|
]
|
|
cold_numbers = (missing_numbers + cold_numbers)[:8]
|
|
result = {
|
|
"lottery": config["label"],
|
|
"draws_used": draws_to_consider,
|
|
"total_combinations": f"{total_combinations:,}".replace(",", "."),
|
|
"odds": _format_odds(total_combinations),
|
|
"percent": _format_percent(total_combinations),
|
|
"suggestions": suggestions,
|
|
"hot_numbers": hot_numbers,
|
|
"cold_numbers": cold_numbers,
|
|
}
|
|
|
|
context = {
|
|
"project_name": "LotoPulse",
|
|
"project_description": (
|
|
"Analise loterias brasileiras, gere jogos e acompanhe "
|
|
"probabilidades com base nos sorteios mais recentes."
|
|
),
|
|
"agent_brand": agent_brand,
|
|
"django_version": django_version(),
|
|
"python_version": platform.python_version(),
|
|
"current_time": now,
|
|
"host_name": host_name,
|
|
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
|
"deployment_timestamp": now.strftime("%Y%m%d%H%M%S"),
|
|
"form": form,
|
|
"result": result,
|
|
"lottery_cards": lottery_cards,
|
|
}
|
|
return render(request, "core/index.html", context)
|