Autosave: 20260218-044310

This commit is contained in:
Flatlogic Bot 2026-02-18 04:43:11 +00:00
parent b9983c7f67
commit 40e64da82f
8 changed files with 265 additions and 16 deletions

View File

@ -36,6 +36,9 @@ class LotterySimulatorForm(forms.Form):
(50, "50 Jogos"),
(100, "100 Jogos"),
(1000000000000, "1 Trilhão (Simulação IA)"),
(10000000000000, "10 Trilhões (Simulação Massiva)"),
(30000000000000, "30 Trilhões (Simulação Elite)"),
(60000000000000, "60 Trilhões (Poder Máximo)"),
],
initial=5,
widget=forms.Select(

View File

@ -14,9 +14,13 @@
<h2>Editor: {{ lottery.get_name_display }}</h2>
<p class="text-muted">Selecione os números para <strong>ANULAR</strong> ou use a IA.</p>
</div>
<a href="{% url 'ai_predict' lottery.id %}" class="btn btn-warning">
<i class="fas fa-robot"></i> Auto-Previsão IA
</a>
<div class="text-end">
<a href="{% url 'ai_predict' lottery.id %}" class="btn btn-warning mb-1">
<i class="fas fa-robot"></i> Auto-Previsão IA Rotativa
</a>
<br>
<small class="text-warning" style="font-size: 0.75rem;">Recalcula Verdes ignorando Anulados</small>
</div>
</div>
<form method="post">

View File

@ -130,12 +130,13 @@
</div>
<div class="number-groups">
<div>
<div class="group-title text-success">Probabilidades Quentes (Verde)</div>
<div class="group-title text-success">Probabilidades Quentes (Elite Verde)</div>
<div class="badge-grid">
{% for number in result.hot_numbers %}
<span class="badge" style="background-color: #198754; font-size: 1.1rem;">{{ number|stringformat:"02d" }}</span>
<span class="badge shadow-sm" style="background: linear-gradient(135deg, #198754, #28a745); font-size: 1.1rem; border: none;">{{ number|stringformat:"02d" }}</span>
{% endfor %}
</div>
<small class="text-muted mt-2 d-block" style="font-size: 0.75rem;">* Atualizado ao vivo conforme anulações no editor.</small>
</div>
<div class="mt-3">
<div class="group-title text-danger">Números Anulados (Vermelho)</div>
@ -159,9 +160,9 @@
</div>
<div class="suggestions-grid">
{% if result.is_trillion %}
<div class="alert alert-warning mb-3">
<strong>Simulação de 1 Trilhão Concluída!</strong><br>
A IA processou 1.000.000.000.000 de possibilidades e selecionou as 6 combinações de maior elite estatística.
<div class="alert alert-success mb-3 shadow-sm border-0" style="background: linear-gradient(135deg, #198754, #20c997); color: white;">
<strong>Simulação de {{ result.trillion_label }} Concluída!</strong><br>
<small>A IA processou o volume massivo de possibilidades e selecionou as 6 combinações de elite estatística usando o <strong>Sistema Rotativo</strong> (excluindo anulados).</small>
</div>
{% endif %}
{% for suggestion in result.suggestions %}
@ -189,6 +190,172 @@
</div>
</section>
<section class="manual-selection-section py-5" style="background: #f8f9fa;">
<div class="container">
<div class="section-header text-center mb-5">
<h2 class="display-6 fw-bold">Seleção Manual & Matemática Ao Vivo</h2>
<p class="text-muted">Escolha 6 ou mais números e peça à IA para calcular a elite estatística agora.</p>
</div>
<div class="row g-4">
<div class="col-lg-8">
<div class="card shadow-sm border-0 p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0">Quadro de Dezenas</h4>
<span class="badge bg-success" id="selected-count">0 selecionados</span>
</div>
<div id="number-grid" class="d-flex flex-wrap gap-2 justify-content-center p-3" style="background: #fff; border-radius: 12px; min-height: 200px;">
<p class="text-muted mt-4">Selecione uma loteria no simulador acima para carregar o quadro.</p>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card shadow-sm border-0 p-4 h-100 text-center" style="background: linear-gradient(135deg, #1e293b, #0f172a); color: white;">
<h4>Análise Ao Vivo</h4>
<hr class="border-secondary">
<div id="live-result-area" class="py-4">
<div class="display-1 fw-bold mb-0" id="live-score">--</div>
<div class="text-uppercase tracking-wider small opacity-75">Score de Elite IA</div>
</div>
<div class="mt-auto">
<button id="btn-live-math" class="btn btn-success btn-lg w-100 py-3 fw-bold shadow">
<i class="bi bi-cpu"></i> CÁLCULO MATEMÁTICO AO VIVO
</button>
<p class="small mt-3 opacity-50">Mínimo de 6 dezenas para processar.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<script>
document.addEventListener('DOMContentLoaded', function() {
const grid = document.getElementById('number-grid');
const selectCount = document.getElementById('selected-count');
const lotterySelect = document.querySelector('select[name="lottery_type"]');
const btnLive = document.getElementById('btn-live-math');
const liveScore = document.getElementById('live-score');
const liveResultArea = document.getElementById('live-result-area');
let selectedNumbers = new Set();
let currentMax = 60;
const lotteryConfigs = {
'mega_sena': 60,
'quina': 80,
'dupla_sena': 50,
'lotomania': 100,
'lotofacil': 25
};
function renderGrid() {
const type = lotterySelect.value;
currentMax = lotteryConfigs[type] || 60;
grid.innerHTML = '';
selectedNumbers.clear();
updateUI();
for (let i = 1; i <= currentMax; i++) {
const ball = document.createElement('div');
ball.className = 'live-ball';
ball.textContent = i.toString().padStart(2, '0');
ball.onclick = () => toggleNumber(i, ball);
grid.appendChild(ball);
}
}
function toggleNumber(num, element) {
if (selectedNumbers.has(num)) {
selectedNumbers.delete(num);
element.classList.remove('active');
} else {
selectedNumbers.add(num);
element.classList.add('active');
}
updateUI();
}
function updateUI() {
selectCount.textContent = `${selectedNumbers.size} selecionados`;
}
lotterySelect.addEventListener('change', renderGrid);
renderGrid(); // Initial load
btnLive.onclick = async () => {
if (selectedNumbers.size < 6) {
alert('Por favor, selecione pelo menos 6 números.');
return;
}
btnLive.disabled = true;
btnLive.textContent = 'PROCESSANDO MATEMÁTICA...';
try {
const response = await fetch('{% url "live_math" %}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({
lottery: lotterySelect.value,
numbers: Array.from(selectedNumbers)
})
});
const data = await response.json();
if (data.error) throw new Error(data.error);
liveScore.textContent = data.score + '%';
liveScore.style.color = data.status === 'success' ? '#22c55e' : '#eab308';
// Efeito visual de sucesso
liveResultArea.style.transform = 'scale(1.1)';
setTimeout(() => liveResultArea.style.transform = 'scale(1)', 200);
} catch (err) {
alert('Erro no cálculo: ' + err.message);
} finally {
btnLive.disabled = false;
btnLive.textContent = 'CÁLCULO MATEMÁTICO AO VIVO';
}
};
});
</script>
<style>
.live-ball {
width: 45px;
height: 45px;
border: 2px solid #e2e8f0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-weight: bold;
transition: all 0.2s;
user-select: none;
background: white;
color: #475569;
}
.live-ball:hover {
border-color: #198754;
transform: translateY(-2px);
}
.live-ball.active {
background: #198754;
color: white;
border-color: #198754;
box-shadow: 0 0 15px rgba(25, 135, 84, 0.4);
}
#live-result-area {
transition: all 0.3s;
}
</style>
<section id="jogos" class="games-section">
<div class="container">
<div class="section-header">

View File

@ -8,4 +8,5 @@ urlpatterns = [
path('admin-loto/dashboard/', views.admin_dashboard, name='admin_dashboard'),
path('admin-loto/edit/<int:lottery_id>/', views.edit_lottery, name='edit_lottery'),
path('admin-loto/ai-predict/<int:lottery_id>/', views.ai_auto_predict, name='ai_predict'),
path('live-math/', views.live_math, name='live_math'),
]

View File

@ -6,6 +6,8 @@ from collections import Counter
from django import get_version as django_version
from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse
import json
from django.utils import timezone
from django.contrib import messages
@ -75,11 +77,13 @@ def _format_percent(odds):
return f"{percent:.6f}%".replace(".", ",")
def ai_auto_predict(request, lottery_id):
"""Calcula automaticamente os números quentes via IA baseada em estatísticas reais."""
"""Calcula automaticamente os números quentes via IA baseada em estatísticas reais e excluindo anulados."""
if not check_admin(request):
return redirect('admin_login')
lottery = get_object_or_404(Lottery, id=lottery_id)
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
# Pega todos os sorteios (Histórico Completo)
draws_db = DrawResult.objects.filter(lottery=lottery)
@ -91,7 +95,7 @@ def ai_auto_predict(request, lottery_id):
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
frequency = Counter(number for draw in draw_lists for number in draw)
# Calcular atraso (há quantos sorteios o número não sai)
# Calcular atraso
last_seen = {}
for i, draw in enumerate(reversed(draw_lists)):
for num in draw:
@ -99,21 +103,24 @@ def ai_auto_predict(request, lottery_id):
last_seen[num] = i
# Score IA = (Frequência * 0.7) + (Atraso * 0.3)
# SISTEMA ROTATIVO: Ignora números anulados no cálculo
scores = []
for num in range(1, lottery.max_number + 1):
if num in annulled:
continue
freq = frequency.get(num, 0)
delay = last_seen.get(num, len(draw_lists))
score = (freq * 0.7) + (delay * 0.3)
scores.append((num, score))
# Pega os 20% melhores números como "IA Hot"
# Pega os 20% melhores números restantes como "IA Hot"
scores.sort(key=lambda x: x[1], reverse=True)
top_numbers = [str(n[0]) for n in scores[:int(lottery.max_number * 0.2)]]
top_numbers = [str(n[0]) for n in scores[:int(lottery.max_number * 0.25)]]
lottery.ai_predictions = ",".join(top_numbers)
lottery.save()
messages.success(request, f"IA calculou as probabilidades para {lottery.get_name_display()} com sucesso!")
messages.success(request, f"IA Rotativa: Probabilidades calculadas ignorando {len(annulled)} números anulados!")
return redirect('edit_lottery', lottery_id=lottery_id)
def home(request):
@ -171,13 +178,29 @@ def home(request):
numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in annulled]
# Definição de Cores: Verde (Hot) e Vermelho (Frio)
hot_numbers = ai_hot if ai_hot else [n for n, c in frequency.most_common(10)]
cold_numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in hot_numbers and n not in annulled]
# MATEMÁTICA AO VIVO: Calcula a elite estatística no momento do acesso, ignorando anulados.
# Pegamos os números com maior frequência que NÃO estão anulados.
available_numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in annulled]
# Ordena os números disponíveis pela frequência (quentes primeiro)
sorted_by_freq = sorted(available_numbers, key=lambda n: frequency.get(n, 0), reverse=True)
# A "Elite Verde" será composta pelos top 25% dos números mais frequentes disponíveis
hot_count = max(6, int(len(available_numbers) * 0.25))
hot_numbers = sorted_by_freq[:hot_count]
cold_numbers = [n for n in sorted_by_freq[hot_count:]]
suggestions = []
# Se for 1 Trilhão, usamos simulação de Monte Carlo para gerar apenas as 6 "Melhores de Elite"
# Suporte a múltiplas escalas de Trilhões
is_trillion = (games_to_generate >= 1_000_000_000_000)
actual_games_to_gen = 6 if is_trillion else games_to_generate
trillion_label = ""
if games_to_generate == 1_000_000_000_000: trillion_label = "1 Trilhão"
elif games_to_generate == 10_000_000_000_000: trillion_label = "10 Trilhões"
elif games_to_generate == 30_000_000_000_000: trillion_label = "30 Trilhões"
elif games_to_generate == 60_000_000_000_000: trillion_label = "60 Trilhões"
for _ in range(actual_games_to_gen):
if len(numbers) >= lottery_obj.numbers_to_draw:
@ -196,6 +219,7 @@ def home(request):
"lottery": lottery_obj.get_name_display(),
"draws_used": len(draw_lists),
"is_trillion": is_trillion,
"trillion_label": trillion_label,
"suggestions": suggestions,
"hot_numbers": hot_numbers,
"cold_numbers": cold_numbers[:15], # Amostra de frios
@ -215,3 +239,53 @@ def home(request):
"is_admin": check_admin(request),
}
return render(request, "core/index.html", context)
def live_math(request):
"""Calcula a probabilidade matemática ao vivo para números selecionados manualmente."""
if request.method == "POST":
try:
data = json.loads(request.body)
lottery_key = data.get("lottery")
manual_numbers = [int(n) for n in data.get("numbers", [])]
if not manual_numbers or len(manual_numbers) < 6:
return JsonResponse({"error": "Selecione ao menos 6 números."}, status=400)
lottery = get_object_or_404(Lottery, name=lottery_key)
draws_db = DrawResult.objects.filter(lottery=lottery)
# Se não houver sorteios, usamos uma base simulada para a matemática ao vivo
if not draws_db.exists():
frequency = {n: random.randint(5, 15) for n in range(1, lottery.max_number + 1)}
else:
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
frequency = Counter(number for draw in draw_lists for number in draw)
ai_hot = [int(n) for n in lottery.ai_predictions.split(',') if n]
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
# Cálculo de "Calor" (Heat Score)
# 1. Quantos são Verdes (IA Hot)
hits_hot = len([n for n in manual_numbers if n in ai_hot])
# 2. Quantos foram anulados (Erro crítico)
hits_annulled = len([n for n in manual_numbers if n in annulled])
# Média de frequência dos números escolhidos
avg_freq = sum(frequency.get(n, 0) for n in manual_numbers) / len(manual_numbers)
max_freq = max(frequency.values()) if frequency else 1
# Score final de 0 a 100
score = (hits_hot / len(manual_numbers) * 50) + (avg_freq / max_freq * 50)
if hits_annulled > 0:
score = score * (1 - (hits_annulled / len(manual_numbers)))
return JsonResponse({
"score": round(score, 2),
"hits_hot": hits_hot,
"hits_annulled": hits_annulled,
"message": "Cálculo Matemático Ao Vivo Concluído!",
"status": "success" if score > 50 else "warning"
})
except Exception as e:
return JsonResponse({"error": str(e)}, status=500)
return JsonResponse({"error": "Método inválido"}, status=405)