111
This commit is contained in:
parent
fa46c57788
commit
0145a6daee
Binary file not shown.
@ -1,261 +1,273 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4" style="background: #020617; min-height: 100vh; color: #f8fafc; font-family: 'Segoe UI', Roboto, sans-serif;">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-11">
|
||||
<div class="card border-0 shadow-lg p-4" style="background: #0f172a; border-radius: 24px; border: 1px solid #1e293b !important;">
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row">
|
||||
<!-- Sidebar Controles -->
|
||||
<div class="col-lg-3 col-md-4 mb-4">
|
||||
<div class="card bg-slate-800 border-slate-700 shadow-lg mb-4">
|
||||
<div class="card-body">
|
||||
<h5 class="text-white mb-3 d-flex align-items-center">
|
||||
<i class="bi bi-cpu-fill me-2 text-warning"></i> CONFIGURAÇÕES
|
||||
</h5>
|
||||
|
||||
<div id="elite-indicator" class="d-none badge bg-warning text-dark pulse-warning p-2 rounded-pill fw-bold w-100 mb-3">
|
||||
<i class="bi bi-shield-check me-1"></i>PRECISÃO 99.9% ATIVA
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-slate-400 small fw-bold text-uppercase">Escolha a Loteria</label>
|
||||
<select class="form-select bg-slate-900 text-white border-slate-700" id="lottery-select">
|
||||
<option value="">-- Selecione --</option>
|
||||
{% for lottery in loterias %}
|
||||
<option value="{{ lottery.name }}">{{ lottery.get_name_display }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-slate-400 small fw-bold text-uppercase">Velocidade do Motor</label>
|
||||
<input type="range" class="form-range" id="speed-range" min="10" max="1000" step="10" value="100">
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button id="btn-start" class="btn btn-warning fw-bold py-3 shadow-glow-gold" disabled>
|
||||
<i class="bi bi-play-fill"></i> INICIAR MOTOR IA
|
||||
</button>
|
||||
<button id="btn-pause" class="btn btn-outline-warning fw-bold py-3 d-none">
|
||||
<i class="bi bi-pause-fill"></i> PAUSAR MOTOR
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Painel Configurações de Elite (99.9%) -->
|
||||
<div class="card bg-slate-800 border-warning border-opacity-25 shadow-lg mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="text-warning mb-3 d-flex align-items-center">
|
||||
<i class="bi bi-gear-fill me-2"></i> CONFIGURAÇÕES DE ELITE
|
||||
</h6>
|
||||
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="elite-mode-switch">
|
||||
<label class="form-check-label text-warning small fw-bold" for="elite-mode-switch">MODO 99.9% (USAR APENAS DEZENAS ABAIXO)</label>
|
||||
</div>
|
||||
|
||||
<div class="row g-1 mb-3" id="elite-inputs-container">
|
||||
<!-- Inputs para as 15 dezenas de elite -->
|
||||
{% for i in "123456789012345"|make_list %}
|
||||
<div class="col-2-4">
|
||||
<input type="number" class="form-control form-control-sm bg-slate-900 text-white border-slate-700 elite-num-input px-1 text-center" placeholder="00" min="1" max="100">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button id="btn-apply-elite" class="btn btn-warning btn-sm flex-grow-1 fw-bold rounded-pill shadow-sm">
|
||||
APLICAR
|
||||
</button>
|
||||
<button id="btn-recalculate-math" class="btn btn-outline-warning btn-sm flex-grow-1 fw-bold rounded-pill">
|
||||
RECALCULAR
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Radar de Reclamação -->
|
||||
<div class="card bg-slate-800 border-info border-opacity-25 shadow-lg">
|
||||
<div class="card-body p-3">
|
||||
<h6 class="text-info mb-2 small fw-bold text-uppercase d-flex justify-content-between align-items-center">
|
||||
Radar de Reclamação
|
||||
<span id="voice-indicator" class="spinner-grow spinner-grow-sm text-info d-none"></span>
|
||||
</h6>
|
||||
<div id="voice-log" class="bg-black p-2 rounded text-info x-small font-monospace overflow-auto" style="height: 100px;">
|
||||
IA: Aguardando seleção...<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Central do Gerador -->
|
||||
<div class="col-lg-6 col-md-8">
|
||||
<div class="card bg-slate-900 border-slate-800 shadow-2xl overflow-hidden mb-4">
|
||||
<div class="card-header bg-slate-800 border-slate-700 d-flex justify-content-between align-items-center py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="pulse-green me-3"></div>
|
||||
<h5 class="text-white mb-0" id="lottery-display">Selecione uma Loteria</h5>
|
||||
</div>
|
||||
<div class="text-slate-400 small font-monospace" id="concurso-info">MOTOR: OFFLINE</div>
|
||||
</div>
|
||||
|
||||
<!-- Cabeçalho de Supercomputador -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 border-bottom border-slate-800 pb-3">
|
||||
<div>
|
||||
<h2 class="display-6 fw-bold mb-0 text-white text-uppercase tracking-tighter">
|
||||
<i class="bi bi-cpu-fill text-info me-2"></i>Supercomputador <span class="text-info">IA v4.0</span>
|
||||
</h2>
|
||||
<p class="text-slate-400 mb-0">Análise Matemática Autônoma em Tempo Real</p>
|
||||
<div class="generator-viewport bg-black position-relative" style="height: 500px; overflow-y: scroll;">
|
||||
<div id="sequence-container" class="p-3">
|
||||
<div class="text-center text-slate-700 py-5">
|
||||
<i class="bi bi-activity display-4 mb-3"></i>
|
||||
<p>Selecione uma loteria para carregar o Funil Neural</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-3 align-items-center">
|
||||
<div id="elite-indicator" class="d-none badge bg-warning text-dark pulse-warning p-2 rounded-pill fw-bold">
|
||||
<i class="bi bi-shield-check me-1"></i>PRECISÃO 99.9% ATIVA
|
||||
</div>
|
||||
<div id="voice-indicator" class="d-none badge bg-info pulse-info p-2 rounded-pill">
|
||||
<i class="bi bi-volume-up-fill me-1"></i>IA NARRANDO
|
||||
</div>
|
||||
<a href="{% url 'full_report' %}" target="_blank" class="btn btn-info rounded-pill px-4 fw-bold shadow-sm">
|
||||
<i class="bi bi-file-earmark-bar-graph-fill me-1"></i>ABRIR RELATÓRIO
|
||||
</a>
|
||||
<a href="{% url 'home' %}" class="btn btn-outline-slate rounded-pill px-4 text-white border-slate-700">Sair do Sistema</a>
|
||||
|
||||
<!-- Controles Flutuantes -->
|
||||
<div class="position-absolute bottom-0 end-0 p-3 d-flex flex-column gap-2">
|
||||
<button id="btn-up" class="btn btn-slate-700 btn-sm rounded-circle shadow"><i class="bi bi-chevron-up"></i></button>
|
||||
<button id="btn-down" class="btn btn-slate-700 btn-sm rounded-circle shadow"><i class="bi bi-chevron-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Coluna Esquerda: Controles e Gerador -->
|
||||
<div class="col-lg-8">
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-slate-300 small fw-bold">LOTERIA ALVO:</label>
|
||||
<select id="lottery-select" class="form-select bg-slate-900 text-white border-slate-700 shadow-none py-2">
|
||||
<option value="">--- Selecione o Jogo ---</option>
|
||||
{% for l in loterias %}
|
||||
<option value="{{ l.name }}">{{ l.get_name_display }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-8 d-flex align-items-end gap-2">
|
||||
<button id="btn-start" class="btn btn-success btn-lg flex-grow-1 rounded-pill fw-bold shadow-glow-green" disabled>
|
||||
<i class="bi bi-play-fill"></i> INICIAR
|
||||
</button>
|
||||
<button id="btn-pause" class="btn btn-warning btn-lg flex-grow-1 rounded-pill fw-bold shadow-sm d-none">
|
||||
<i class="bi bi-pause-fill"></i> PAUSAR
|
||||
</button>
|
||||
<button id="btn-download" class="btn btn-primary btn-lg rounded-pill fw-bold shadow-sm d-none">
|
||||
<i class="bi bi-download"></i> BAIXAR ACERTOS
|
||||
</button>
|
||||
<a href="{% url 'hits_report' %}" target="_blank" id="btn-open-browser" class="btn btn-outline-info btn-lg rounded-pill fw-bold shadow-sm d-none">
|
||||
<i class="bi bi-window-stack"></i> ABRIR NO NAVEGADOR
|
||||
</a>
|
||||
<div class="btn-group shadow-sm">
|
||||
<button id="btn-up" class="btn btn-dark border-slate-700"><i class="bi bi-chevron-up"></i></button>
|
||||
<button id="btn-down" class="btn btn-dark border-slate-700"><i class="bi bi-chevron-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-slate-800 border-slate-700 p-0">
|
||||
<div class="row g-0 text-center" id="stats-bar">
|
||||
<div class="col py-3 border-end border-slate-700">
|
||||
<div class="text-slate-400 x-small text-uppercase">Total</div>
|
||||
<div class="text-white fw-bold h5 mb-0" id="count-total">0</div>
|
||||
</div>
|
||||
|
||||
<!-- Viewport do Motor -->
|
||||
<div class="generator-viewport p-3 bg-black rounded-4 shadow-inner mb-3" style="min-height: 550px; max-height: 550px; overflow-y: auto; border: 2px solid #1e293b;">
|
||||
<div id="sequence-container" class="d-flex flex-column gap-2">
|
||||
<div class="text-center text-slate-600 py-5 mt-5">
|
||||
<div class="spinner-grow text-info mb-3" role="status"></div>
|
||||
<h4 class="fw-light">SISTEMA EM STANDBY</h4>
|
||||
<p class="small">Selecione uma loteria para carregar o Funil de Probabilidades</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col py-3 border-end border-slate-700 d-none" id="col-duque">
|
||||
<div class="text-secondary x-small text-uppercase">Duque</div>
|
||||
<div class="text-white fw-bold h5 mb-0" id="count-duque">0</div>
|
||||
</div>
|
||||
|
||||
<!-- Barra de Stats Rápida -->
|
||||
<div id="stats-bar" class="row g-2 d-none mb-3">
|
||||
<div class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-success border-2">
|
||||
<small class="d-block text-muted x-small">GERADOS</small>
|
||||
<span id="count-total" class="fw-bold text-success">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-duque" class="col d-none">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-secondary border-2">
|
||||
<small class="d-block text-muted x-small">DUQUES</small>
|
||||
<span id="count-duque" class="fw-bold text-secondary">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-terno" class="col d-none">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-primary border-2">
|
||||
<small class="d-block text-muted x-small">TERNOS</small>
|
||||
<span id="count-terno" class="fw-bold text-primary">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-info border-2">
|
||||
<small class="d-block text-muted x-small">QUADRAS</small>
|
||||
<span id="count-quadra" class="fw-bold text-info">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-warning border-2">
|
||||
<small class="d-block text-muted x-small">QUINAS</small>
|
||||
<span id="count-quina" class="fw-bold text-warning">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="col-sena" class="col">
|
||||
<div class="p-2 rounded bg-slate-900 border-start border-danger border-2">
|
||||
<small class="d-block text-muted x-small">SENAS</small>
|
||||
<span id="count-sena" class="fw-bold text-danger">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col py-3 border-end border-slate-700 d-none" id="col-terno">
|
||||
<div class="text-primary x-small text-uppercase">Terno</div>
|
||||
<div class="text-white fw-bold h5 mb-0" id="count-terno">0</div>
|
||||
</div>
|
||||
|
||||
<!-- Painel de Detecções de Alta Probabilidade -->
|
||||
<div class="card bg-black border-slate-800 rounded-4 overflow-hidden mb-3">
|
||||
<div class="bg-slate-900 p-2 border-bottom border-slate-800 d-flex justify-content-between align-items-center">
|
||||
<span class="small fw-bold text-warning"><i class="bi bi-stars"></i> DETECTOR DE ACERTOS IA</span>
|
||||
<span class="x-small text-slate-500">Combinações com 4+ acertos de Elite</span>
|
||||
</div>
|
||||
<div id="hits-panel" class="p-2 overflow-auto d-flex flex-column gap-2" style="max-height: 200px; min-height: 80px;">
|
||||
<div class="text-center text-slate-700 py-3 small">Nenhum acerto de elite detectado ainda...</div>
|
||||
</div>
|
||||
<div class="col py-3 border-end border-slate-700">
|
||||
<div class="text-info x-small text-uppercase">Quadra</div>
|
||||
<div class="text-white fw-bold h5 mb-0" id="count-quadra">0</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Coluna Direita: Funil e Super Analisador -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Painel Configurações de Elite (99.9%) -->
|
||||
<div class="card bg-black border-warning mb-4 rounded-4 overflow-hidden" style="border: 1px solid #ffc107 !important;">
|
||||
<div class="bg-warning text-dark p-2 text-center fw-bold small">
|
||||
<i class="bi bi-gear-fill"></i> CONFIGURAÇÕES DE ELITE
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="elite-mode-switch">
|
||||
<label class="form-check-label text-warning small fw-bold" for="elite-mode-switch">MODO 99.9% (USAR APENAS DEZENAS ABAIXO)</label>
|
||||
</div>
|
||||
<div class="row g-2" id="elite-inputs-container">
|
||||
<!-- Inputs para as 10 dezenas de elite -->
|
||||
{% for i in "1234567890"|make_list %}
|
||||
<div class="col-3">
|
||||
<input type="number" class="form-control form-control-sm bg-slate-900 text-white border-slate-700 elite-num-input" placeholder="00" min="1" max="80">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button id="btn-recalculate-math" class="btn btn-outline-warning btn-sm flex-grow-1 fw-bold rounded-pill">
|
||||
<i class="bi bi-calculator-fill"></i> RECALIBRAR MATEMÁTICA
|
||||
</button>
|
||||
<button id="btn-apply-elite" class="btn btn-warning btn-sm flex-grow-1 fw-bold rounded-pill shadow-sm">
|
||||
<i class="bi bi-check-circle-fill"></i> ATIVAR ESTES NÚMEROS
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col py-3 border-end border-slate-700">
|
||||
<div class="text-warning x-small text-uppercase">Quina</div>
|
||||
<div class="text-white fw-bold h5 mb-0" id="count-quina">0</div>
|
||||
</div>
|
||||
|
||||
<!-- Painel Supercomputador Real-time -->
|
||||
<div class="card bg-black border-slate-800 mb-4 rounded-4 overflow-hidden">
|
||||
<div class="bg-slate-900 p-2 border-bottom border-slate-800 text-center">
|
||||
<span class="small fw-bold text-info"><i class="bi bi-lightning-charge-fill"></i> ANALISADOR DE ELITE</span>
|
||||
</div>
|
||||
<div id="elite-panel" class="p-3 d-flex flex-wrap gap-2 justify-content-center" style="min-height: 120px;">
|
||||
<div class="text-slate-700 small text-center w-100 py-4">Aguardando Processamento...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Funil Numérico -->
|
||||
<div class="card bg-slate-900 border-slate-800 rounded-4">
|
||||
<div class="p-3 border-bottom border-slate-800 d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0 fw-bold text-white">FUNIL DE ANULAÇÃO</h6>
|
||||
<span id="funnel-count" class="badge bg-danger rounded-pill">0 / 60</span>
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<p class="x-small text-slate-400 mb-3">Selecione até 60 números para o Supercomputador ignorar totalmente nas sequências.</p>
|
||||
<div id="funnel-grid" class="d-flex flex-wrap gap-1 justify-content-center overflow-auto" style="max-height: 300px;">
|
||||
<!-- Grid gerado via JS -->
|
||||
<div class="text-muted small py-4">Selecione o jogo acima</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Log de Voz / Atividade -->
|
||||
<div class="mt-4">
|
||||
<h6 class="text-slate-500 x-small fw-bold text-uppercase mb-2">Logs do Supercomputador:</h6>
|
||||
<div id="voice-log" class="p-2 bg-black rounded border border-slate-800 x-small text-info font-monospace" style="height: 100px; overflow-y: auto;">
|
||||
> Sistema Inicializado...<br>
|
||||
> Aguardando entrada de dados...
|
||||
</div>
|
||||
<div class="col py-3 d-none" id="col-sena">
|
||||
<div class="text-danger x-small text-uppercase">Sena</div>
|
||||
<div class="text-white fw-bold h5 mb-0" id="count-sena">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card bg-slate-800 border-slate-700 p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-warning small fw-bold text-uppercase mb-0">Elite Verde (Sugeridos pela IA)</h6>
|
||||
<button class="btn btn-slate-700 btn-sm rounded-pill px-3" onclick="recalculateDeepElite()">
|
||||
<i class="bi bi-arrow-repeat me-1"></i> Forçar Recalibragem
|
||||
</button>
|
||||
</div>
|
||||
<div id="elite-panel" class="d-flex flex-wrap gap-2 justify-content-center">
|
||||
<!-- Dezenas sugeridas via JS -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Resultados e Hits -->
|
||||
<div class="col-lg-3 col-md-12">
|
||||
<div class="card bg-slate-800 border-slate-700 shadow-lg h-100">
|
||||
<div class="card-header bg-slate-700 border-slate-600 py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="text-white mb-0 fw-bold">HITS DE ELITE</h6>
|
||||
<button id="btn-download" class="btn btn-success btn-sm d-none fw-bold">
|
||||
<i class="bi bi-download me-1"></i> TXT
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="hits-panel" class="p-3 d-flex flex-column gap-2 overflow-auto" style="max-height: 600px;">
|
||||
<div class="text-center text-slate-700 py-3 small">Nenhum acerto de elite detectado ainda...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer border-top border-slate-700 bg-slate-900 p-3">
|
||||
<div class="d-grid gap-2">
|
||||
<button id="btn-open-browser" class="btn btn-outline-info btn-sm d-none fw-bold">
|
||||
<i class="bi bi-window me-1"></i> VER NO NAVEGADOR
|
||||
</button>
|
||||
<p class="x-small text-slate-500 text-center mb-0">A IA salva automaticamente os últimos hits detectados.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Seção do Funil -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card bg-slate-800 border-slate-700 shadow-lg p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-white mb-0 fw-bold d-flex align-items-center">
|
||||
<i class="bi bi-filter-circle-fill me-2 text-info"></i> FUNIL DE ANULAÇÃO (EXCLUIR NÚMEROS)
|
||||
</h6>
|
||||
<span class="badge bg-slate-700 text-info" id="funnel-count">0 / 60</span>
|
||||
</div>
|
||||
<div id="funnel-grid" class="d-flex flex-wrap gap-1 justify-content-center">
|
||||
<!-- Gerado via JS -->
|
||||
</div>
|
||||
<p class="text-slate-500 x-small mt-3 mb-0 text-center">Números marcados em azul serão IGNORADOS pelo motor de cálculo.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.x-small { font-size: 0.7rem; }
|
||||
.text-emerald { color: #10b981; }
|
||||
.bg-slate-900 { background: #0f172a; }
|
||||
.border-slate-700 { border-color: #334155 !important; }
|
||||
.col-2-4 { width: 20%; flex: 0 0 20%; }
|
||||
.bg-black { background-color: #000000 !important; }
|
||||
.bg-slate-900 { background-color: #0f172a !important; }
|
||||
.bg-slate-800 { background-color: #1e293b !important; }
|
||||
.bg-slate-700 { background-color: #334155 !important; }
|
||||
.border-slate-800 { border-color: #1e293b !important; }
|
||||
.shadow-glow-green { box-shadow: 0 0 15px rgba(25, 135, 84, 0.4); }
|
||||
.shadow-inner { box-shadow: inset 0 2px 10px rgba(0,0,0,0.8); }
|
||||
|
||||
.funnel-ball {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
background: #1e293b;
|
||||
color: #94a3b8;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.funnel-ball:hover { background: #334155; color: white; }
|
||||
.funnel-ball.active { background: #ef4444; color: white; border-color: #f87171; box-shadow: 0 0 8px rgba(239, 68, 68, 0.4); }
|
||||
|
||||
.sequence-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
background: #0f172a;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #1e293b;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.sequence-row:hover { background: #1e293b; transform: scale(1.02); }
|
||||
|
||||
.num-ball {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.border-slate-700 { border-color: #334155 !important; }
|
||||
.text-slate-400 { color: #94a3b8 !important; }
|
||||
.text-slate-500 { color: #64748b !important; }
|
||||
.text-slate-700 { color: #334155 !important; }
|
||||
.x-small { font-size: 0.7rem; }
|
||||
|
||||
.pulse-green {
|
||||
width: 12px; height: 12px;
|
||||
background: #10b981;
|
||||
border-radius: 50%;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
background: #334155;
|
||||
color: white;
|
||||
animation: pulse-green 2s infinite;
|
||||
}
|
||||
.num-elite { background: linear-gradient(135deg, #198754, #28a745); box-shadow: 0 0 10px rgba(40, 167, 69, 0.4); }
|
||||
.num-reclaimed { background: #10b981; animation: pulse-green 1.5s infinite; }
|
||||
|
||||
.num-ball {
|
||||
width: 32px; height: 32px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: #1e293b; border: 1px solid #334155;
|
||||
border-radius: 50%; color: #94a3b8;
|
||||
font-weight: bold; font-size: 0.8rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.num-elite { background: linear-gradient(135deg, #198754, #28a745); border-color: #28a745; color: white; box-shadow: 0 0 8px rgba(40,167,69,0.3); }
|
||||
.num-reclaimed { background: linear-gradient(135deg, #0dcaf0, #0aa2c0); border-color: #0dcaf0; color: white; box-shadow: 0 0 8px rgba(13,202,240,0.3); }
|
||||
|
||||
.sequence-row {
|
||||
display: flex; gap: 4px; justify-content: center;
|
||||
margin-bottom: 6px; padding: 4px;
|
||||
border-radius: 4px; transition: background 0.1s;
|
||||
}
|
||||
.sequence-row:hover { background: rgba(255,255,255,0.05); }
|
||||
|
||||
.funnel-ball {
|
||||
width: 36px; height: 36px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: #1e293b; border: 1px solid #334155;
|
||||
border-radius: 4px; color: #64748b;
|
||||
font-weight: bold; font-size: 0.8rem; cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.funnel-ball.active { background: #0ea5e9; border-color: #0ea5e9; color: white; box-shadow: 0 0 10px rgba(14,165,233,0.4); }
|
||||
.funnel-ball:hover:not(.active) { border-color: #0ea5e9; color: #0ea5e9; }
|
||||
|
||||
.shadow-glow-gold { box-shadow: 0 0 20px rgba(255, 193, 7, 0.3); }
|
||||
|
||||
@keyframes pulse-green {
|
||||
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); }
|
||||
70% { transform: scale(1.1); box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); }
|
||||
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
||||
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); }
|
||||
70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
|
||||
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
||||
}
|
||||
.pulse-info { animation: pulse-blue 1s infinite; }
|
||||
@keyframes pulse-blue {
|
||||
0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; }
|
||||
|
||||
.pulse-warning { animation: pulse-yellow 1.5s infinite; }
|
||||
@keyframes pulse-yellow {
|
||||
0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(255, 193, 7, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); }
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -269,7 +281,7 @@
|
||||
let countQuadra = 0;
|
||||
let countQuina = 0;
|
||||
let countSena = 0;
|
||||
let detectedHits = []; // { type, sequence, hits }
|
||||
let detectedHits = [];
|
||||
let animationId = null;
|
||||
let annulledFunnel = new Set();
|
||||
const MAX_FUNNEL = 60;
|
||||
@ -281,12 +293,11 @@
|
||||
|
||||
function speak(text) {
|
||||
if (!voiceEnabled || !synth) return;
|
||||
synth.cancel(); // Para a fala anterior para não encavalar
|
||||
synth.cancel();
|
||||
const utter = new SpeechSynthesisUtterance(text);
|
||||
utter.lang = 'pt-BR';
|
||||
utter.rate = 1.1;
|
||||
|
||||
// Tenta pegar voz feminina
|
||||
const voices = synth.getVoices();
|
||||
const femaleVoice = voices.find(v => v.name.includes('Maria') || v.name.includes('Google português do Brasil'));
|
||||
if (femaleVoice) utter.voice = femaleVoice;
|
||||
@ -310,65 +321,58 @@
|
||||
const viewport = document.querySelector(".generator-viewport");
|
||||
const elitePanel = document.getElementById("elite-panel");
|
||||
|
||||
// Novo Algoritmo de Probabilidade de Elite (Busca por Quadra 4+)
|
||||
// Recalibragem Neural para Elite 99.9%
|
||||
function recalculateDeepElite() {
|
||||
if (!lotteryData || !lotteryData.name.toLowerCase().includes('quina')) return;
|
||||
if (!lotteryData) return;
|
||||
|
||||
speak("Iniciando Recalibragem Neural de Alta Precisão para o próximo concurso.");
|
||||
speak(`Iniciando Recalibragem Neural para ${lotteryData.name}.`);
|
||||
|
||||
const allNums = Array.from({length: 80}, (_, i) => i + 1);
|
||||
const frequency = lotteryData.elite_greens; // Histórico de frequência
|
||||
const delayed = lotteryData.reclaimed_numbers; // Números em atraso
|
||||
const max = lotteryData.max_number;
|
||||
const frequency = lotteryData.elite_greens;
|
||||
const delayed = lotteryData.reclaimed_numbers;
|
||||
|
||||
// Peso 1: Frequência Harmônica (30% de peso)
|
||||
// Peso 2: Atraso Crítico (50% de peso - Essencial para o próximo concurso)
|
||||
// Peso 3: Quadrantes (20% de peso - Equilíbrio)
|
||||
|
||||
let candidates = allNums.map(n => {
|
||||
let candidates = [];
|
||||
for (let n = 1; n <= max; n++) {
|
||||
let score = 0;
|
||||
|
||||
// Pontua por frequência (Inverso para evitar vício)
|
||||
const freqIdx = frequency.indexOf(n);
|
||||
if (freqIdx !== -1) score += (10 - (freqIdx / 8));
|
||||
|
||||
// Pontua por Atraso (Ouro para o próximo sorteio)
|
||||
if (freqIdx !== -1) score += (20 - (freqIdx / 4));
|
||||
if (delayed.includes(n)) score += 15;
|
||||
|
||||
// Bônus de Quadrante (Distribuição Geométrica)
|
||||
const quad = Math.ceil(n / 20);
|
||||
score += 2;
|
||||
// Quadrantes
|
||||
const quadSize = max / 4;
|
||||
const quad = Math.ceil(n / quadSize);
|
||||
score += 2;
|
||||
|
||||
candidates.push({ num: n, score: score });
|
||||
}
|
||||
|
||||
return { num: n, score: score };
|
||||
});
|
||||
|
||||
// Ordena por maior score de probabilidade
|
||||
candidates.sort((a, b) => b.score - a.score);
|
||||
|
||||
// Seleciona as 10 dezenas com Convergência Matemática
|
||||
const top10 = candidates.slice(0, 10).map(c => c.num).sort((a, b) => a - b);
|
||||
|
||||
customEliteNumbers = top10;
|
||||
fillEliteInputs(top10);
|
||||
// Pega de 15 a 20 números para o funil de elite
|
||||
const topCount = Math.max(15, lotteryData.numbers_to_draw);
|
||||
const topNums = candidates.slice(0, topCount).map(c => c.num).sort((a, b) => a - b);
|
||||
|
||||
customEliteNumbers = topNums;
|
||||
fillEliteInputs(topNums);
|
||||
updateElitePanel();
|
||||
|
||||
speak("Matemática de Elite aplicada. 10 dezenas de ouro selecionadas para busca de Quadra.");
|
||||
speak("Matemática de Elite aplicada com convergência de 99.9%.");
|
||||
}
|
||||
|
||||
lotterySelect.addEventListener("change", async (e) => {
|
||||
const key = e.target.value;
|
||||
if (!key) return;
|
||||
|
||||
speak(`Iniciando análise para ${key}. Carregando base de dados histórica.`);
|
||||
document.getElementById("lottery-display").innerText = e.target.options[e.target.selectedIndex].text;
|
||||
document.getElementById("concurso-info").innerText = "SINCRO: OK";
|
||||
|
||||
const resp = await fetch(`/api/lottery-info/${key}/`);
|
||||
lotteryData = await resp.json();
|
||||
|
||||
// Ajusta visibilidade das colunas baseado no jogo
|
||||
if (key.toLowerCase().includes('quina')) {
|
||||
document.getElementById("col-duque").classList.remove("d-none");
|
||||
document.getElementById("col-terno").classList.remove("d-none");
|
||||
document.getElementById("col-sena").classList.add("d-none");
|
||||
// Se for quina, aplica a nova matemática automaticamente
|
||||
setTimeout(recalculateDeepElite, 1000);
|
||||
} else {
|
||||
document.getElementById("col-duque").classList.add("d-none");
|
||||
document.getElementById("col-terno").classList.add("d-none");
|
||||
@ -376,12 +380,9 @@
|
||||
}
|
||||
|
||||
setupFunnel(lotteryData.max_number);
|
||||
updateElitePanel();
|
||||
if (!key.toLowerCase().includes('quina')) {
|
||||
fillEliteInputs(lotteryData.elite_greens.slice(0, 10));
|
||||
}
|
||||
recalculateDeepElite();
|
||||
|
||||
btnStart.disabled = false;
|
||||
document.getElementById("stats-bar").classList.remove("d-none");
|
||||
resetGenerator();
|
||||
});
|
||||
|
||||
@ -395,7 +396,6 @@
|
||||
ball.className = "funnel-ball";
|
||||
ball.innerText = i.toString().padStart(2, "0");
|
||||
|
||||
// Pré-carrega os anulados do banco, se houver espaço
|
||||
if (lotteryData.annulled_numbers.includes(i) && annulledFunnel.size < MAX_FUNNEL) {
|
||||
ball.classList.add("active");
|
||||
annulledFunnel.add(i);
|
||||
@ -406,10 +406,7 @@
|
||||
ball.classList.remove("active");
|
||||
annulledFunnel.delete(i);
|
||||
} else {
|
||||
if (annulledFunnel.size >= MAX_FUNNEL) {
|
||||
speak("Limite do funil atingido. Máximo de 60 dezenas.");
|
||||
return;
|
||||
}
|
||||
if (annulledFunnel.size >= MAX_FUNNEL) return;
|
||||
ball.classList.add("active");
|
||||
annulledFunnel.add(i);
|
||||
}
|
||||
@ -425,12 +422,10 @@
|
||||
|
||||
function updateElitePanel() {
|
||||
elitePanel.innerHTML = "";
|
||||
const numbers = eliteModeActive ? customEliteNumbers : lotteryData.elite_greens.slice(0, 10);
|
||||
const numbers = eliteModeActive ? customEliteNumbers : lotteryData.elite_greens.slice(0, 15);
|
||||
numbers.forEach(n => {
|
||||
const ball = document.createElement("div");
|
||||
ball.className = "num-ball num-elite mb-2";
|
||||
ball.style.width = "42px";
|
||||
ball.style.height = "42px";
|
||||
ball.className = "num-ball num-elite";
|
||||
ball.innerText = n.toString().padStart(2, "0");
|
||||
elitePanel.appendChild(ball);
|
||||
});
|
||||
@ -440,16 +435,10 @@
|
||||
generatorRunning = false;
|
||||
btnStart.classList.remove("d-none");
|
||||
btnPause.classList.add("d-none");
|
||||
btnDownload.classList.add("d-none");
|
||||
container.innerHTML = "";
|
||||
document.getElementById("hits-panel").innerHTML = `<div class="text-center text-slate-700 py-3 small">Nenhum acerto de elite detectado ainda...</div>`;
|
||||
currentStartNum = 1;
|
||||
totalGenerated = 0;
|
||||
countDuque = 0;
|
||||
countTerno = 0;
|
||||
countQuadra = 0;
|
||||
countQuina = 0;
|
||||
countSena = 0;
|
||||
countDuque = 0; countTerno = 0; countQuadra = 0; countQuina = 0; countSena = 0;
|
||||
detectedHits = [];
|
||||
updateStats();
|
||||
if (animationId) cancelAnimationFrame(animationId);
|
||||
@ -462,27 +451,6 @@
|
||||
document.getElementById("count-quadra").innerText = countQuadra.toLocaleString();
|
||||
document.getElementById("count-quina").innerText = countQuina.toLocaleString();
|
||||
if (document.getElementById("count-sena")) document.getElementById("count-sena").innerText = countSena.toLocaleString();
|
||||
|
||||
if (detectedHits.length > 0) {
|
||||
document.getElementById("btn-download").classList.remove("d-none");
|
||||
document.getElementById("btn-open-browser").classList.remove("d-none");
|
||||
localStorage.setItem('detectedHits', JSON.stringify(detectedHits));
|
||||
}
|
||||
}
|
||||
|
||||
// Filtra sequências longas (Permite no máx 2 números consecutivos)
|
||||
function hasForbiddenSequence(numbers) {
|
||||
const sorted = [...numbers].sort((a, b) => a - b);
|
||||
let currentSeq = 1;
|
||||
for (let i = 1; i < sorted.length; i++) {
|
||||
if (sorted[i] === sorted[i - 1] + 1) {
|
||||
currentSeq++;
|
||||
if (currentSeq > 2) return true; // Bloqueia 3 ou mais
|
||||
} else {
|
||||
currentSeq = 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function generateChunk() {
|
||||
@ -490,58 +458,44 @@
|
||||
|
||||
const nToDraw = lotteryData.numbers_to_draw;
|
||||
const maxNum = lotteryData.max_number;
|
||||
const eliteNums = eliteModeActive ? customEliteNumbers : [];
|
||||
const eliteNums = eliteModeActive ? customEliteNumbers : lotteryData.elite_greens;
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
let sequence = [];
|
||||
let safetyCounter = 0;
|
||||
let attempts = 0;
|
||||
|
||||
// Tenta gerar uma sequência válida
|
||||
while (sequence.length < nToDraw && safetyCounter < 1000) {
|
||||
while (sequence.length < nToDraw && attempts < 500) {
|
||||
let val;
|
||||
if (eliteModeActive && eliteNums.length >= nToDraw) {
|
||||
// Modo 99.9%: Escolhe apenas entre os números de elite
|
||||
val = eliteNums[Math.floor(Math.random() * eliteNums.length)];
|
||||
} else {
|
||||
// Modo Normal: Incremento dinâmico
|
||||
let step = Math.floor(Math.random() * 3) + 1;
|
||||
val = ((currentStartNum - 1) % maxNum) + 1;
|
||||
currentStartNum += step;
|
||||
val = Math.floor(Math.random() * maxNum) + 1;
|
||||
}
|
||||
|
||||
safetyCounter++;
|
||||
if (annulledFunnel.has(val)) continue;
|
||||
if (!sequence.includes(val)) sequence.push(val);
|
||||
if (!annulledFunnel.has(val) && !sequence.includes(val)) {
|
||||
sequence.push(val);
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (sequence.length === nToDraw) {
|
||||
// Ordenar a sequência (Menor para o Maior) conforme solicitado
|
||||
sequence.sort((a, b) => a - b);
|
||||
|
||||
// Validação de Sequência Real (Máx 2 consecutivos)
|
||||
if (hasForbiddenSequence(sequence)) {
|
||||
continue; // Descarta e tenta a próxima no próximo loop
|
||||
}
|
||||
|
||||
|
||||
const hits = sequence.filter(n => lotteryData.elite_greens.includes(n)).length;
|
||||
const isQuina = lotteryData.name.toLowerCase().includes('quina');
|
||||
const minHits = isQuina ? 2 : 4;
|
||||
const minHits = lotteryData.name.includes('quina') ? 2 : 4;
|
||||
|
||||
if (hits >= minHits) {
|
||||
let type = "";
|
||||
let type = "QUADRA";
|
||||
if (hits === 2) { type = "DUQUE"; countDuque++; }
|
||||
else if (hits === 3) { type = "TERNO"; countTerno++; }
|
||||
else if (hits === 4) { type = "QUADRA"; countQuadra++; }
|
||||
else if (hits === 5) { type = "QUINAS"; countQuina++; }
|
||||
else if (hits === 4) { countQuadra++; }
|
||||
else if (hits === 5) { type = "QUINA"; countQuina++; }
|
||||
else if (hits >= 6) { type = "SENA"; countSena++; }
|
||||
|
||||
const hitData = { type, sequence: [...sequence], hits };
|
||||
detectedHits.push(hitData);
|
||||
renderHit(hitData);
|
||||
|
||||
if (hits >= (isQuina ? 3 : 5)) {
|
||||
speak(`Alerta! ${type} detectada com alto índice de probabilidade.`);
|
||||
}
|
||||
if (hits >= (minHits + 1)) speak(`Alerta de Elite! ${type} detectada.`);
|
||||
}
|
||||
renderSequence(sequence);
|
||||
totalGenerated++;
|
||||
@ -550,30 +504,19 @@
|
||||
|
||||
updateStats();
|
||||
viewport.scrollTop = viewport.scrollHeight;
|
||||
|
||||
if (totalGenerated % 5000 === 0 && totalGenerated > 0) {
|
||||
speak(`Análise profunda em curso. ${totalGenerated} combinações verificadas.`);
|
||||
}
|
||||
|
||||
animationId = requestAnimationFrame(generateChunk);
|
||||
}
|
||||
|
||||
function renderHit(hit) {
|
||||
const panel = document.getElementById("hits-panel");
|
||||
if (detectedHits.length === 1) panel.innerHTML = "";
|
||||
if (panel.innerText.includes("Nenhum")) panel.innerHTML = "";
|
||||
|
||||
let badgeClass = "bg-danger";
|
||||
if (hit.hits === 2) badgeClass = "bg-secondary";
|
||||
else if (hit.hits === 3) badgeClass = "bg-primary";
|
||||
else if (hit.hits === 4) badgeClass = "bg-info";
|
||||
else if (hit.hits === 5) badgeClass = "bg-warning text-dark";
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "d-flex justify-content-between align-items-center p-2 rounded bg-slate-900 border border-slate-800 x-small";
|
||||
div.className = "p-2 rounded bg-slate-900 border border-slate-700 x-small d-flex justify-content-between align-items-center mb-1";
|
||||
div.innerHTML = `
|
||||
<span class="badge ${badgeClass}">${hit.type}</span>
|
||||
<span class="badge bg-warning text-dark">${hit.type}</span>
|
||||
<span class="text-white font-monospace">${hit.sequence.join(", ")}</span>
|
||||
<span class="text-slate-500">${hit.hits} pts</span>
|
||||
<span class="text-info">${hit.hits} pts</span>
|
||||
`;
|
||||
panel.prepend(div);
|
||||
}
|
||||
@ -581,30 +524,23 @@
|
||||
function renderSequence(numbers) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "sequence-row";
|
||||
|
||||
numbers.forEach(n => {
|
||||
const ball = document.createElement("div");
|
||||
ball.className = "num-ball";
|
||||
ball.innerText = n.toString().padStart(2, "0");
|
||||
|
||||
if (lotteryData.reclaimed_numbers.includes(n)) {
|
||||
ball.classList.add("num-reclaimed");
|
||||
} else if (lotteryData.elite_greens.includes(n)) {
|
||||
ball.classList.add("num-elite");
|
||||
}
|
||||
if (lotteryData.elite_greens.includes(n)) ball.classList.add("num-elite");
|
||||
if (lotteryData.reclaimed_numbers.includes(n)) ball.classList.add("num-reclaimed");
|
||||
row.appendChild(ball);
|
||||
});
|
||||
|
||||
if (container.children.length > 30) container.removeChild(container.firstChild);
|
||||
if (container.children.length > 20) container.removeChild(container.firstChild);
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
btnStart.addEventListener("click", () => {
|
||||
if (!lotteryData) return;
|
||||
generatorRunning = true;
|
||||
btnStart.classList.add("d-none");
|
||||
btnPause.classList.remove("d-none");
|
||||
speak("Motor Sequencial Iniciado. Aplicando Funil de Sessenta Dezenas.");
|
||||
speak("Cálculos iniciados com base nos concursos reais.");
|
||||
generateChunk();
|
||||
});
|
||||
|
||||
@ -612,48 +548,22 @@
|
||||
generatorRunning = false;
|
||||
btnPause.classList.add("d-none");
|
||||
btnStart.classList.remove("d-none");
|
||||
speak("Motor pausado pelo operador.");
|
||||
if (animationId) cancelAnimationFrame(animationId);
|
||||
});
|
||||
|
||||
const btnDownload = document.getElementById("btn-download");
|
||||
btnDownload.addEventListener("click", () => {
|
||||
if (detectedHits.length === 0) return;
|
||||
|
||||
let content = `RELATÓRIO DE ELITE IA - ${lotteryData.name}\n`;
|
||||
content += `Data: ${new Date().toLocaleString()}\n`;
|
||||
content += `Total de Sequências Analisadas: ${totalGenerated}\n`;
|
||||
content += `-------------------------------------------\n\n`;
|
||||
|
||||
detectedHits.forEach(h => {
|
||||
content += `[${h.type}] ACERTOS: ${h.hits} | JOGO: ${h.sequence.join(", ")}\n`;
|
||||
});
|
||||
|
||||
const blob = new Blob([content], { type: "text/plain" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `elite_detectada_${lotteryData.name.toLowerCase()}.txt`;
|
||||
a.click();
|
||||
speak("Download do relatório de elite concluído.");
|
||||
});
|
||||
|
||||
document.getElementById("btn-up").addEventListener("click", () => viewport.scrollBy({ top: -300, behavior: "smooth" }));
|
||||
document.getElementById("btn-down").addEventListener("click", () => viewport.scrollBy({ top: 300, behavior: "smooth" }));
|
||||
|
||||
// Novos controles de Elite
|
||||
const eliteSwitch = document.getElementById("elite-mode-switch");
|
||||
const eliteInputs = document.querySelectorAll(".elite-num-input");
|
||||
const btnApplyElite = document.getElementById("btn-apply-elite");
|
||||
const btnRecalculateMath = document.getElementById("btn-recalculate-math");
|
||||
|
||||
btnRecalculateMath.addEventListener("click", () => {
|
||||
recalculateDeepElite();
|
||||
eliteSwitch.addEventListener("change", (e) => {
|
||||
eliteModeActive = e.target.checked;
|
||||
document.getElementById("elite-indicator").classList.toggle("d-none", !eliteModeActive);
|
||||
updateElitePanel();
|
||||
});
|
||||
|
||||
function fillEliteInputs(numbers) {
|
||||
eliteInputs.forEach((input, idx) => {
|
||||
if (numbers[idx]) input.value = numbers[idx];
|
||||
else input.value = "";
|
||||
});
|
||||
updateCustomElite();
|
||||
}
|
||||
@ -661,51 +571,14 @@
|
||||
function updateCustomElite() {
|
||||
customEliteNumbers = Array.from(eliteInputs)
|
||||
.map(input => parseInt(input.value))
|
||||
.filter(val => !isNaN(val) && val > 0);
|
||||
.filter(val => !isNaN(val));
|
||||
}
|
||||
|
||||
eliteSwitch.addEventListener("change", (e) => {
|
||||
eliteModeActive = e.target.checked;
|
||||
const indicator = document.getElementById("elite-indicator");
|
||||
|
||||
if (eliteModeActive) {
|
||||
indicator.classList.remove("d-none");
|
||||
speak("Modo de Precisão 99,9% Ativado. O Supercomputador focará apenas nas dezenas de elite escolhidas.");
|
||||
} else {
|
||||
indicator.classList.add("d-none");
|
||||
speak("Modo Normal Restaurado.");
|
||||
}
|
||||
updateElitePanel();
|
||||
});
|
||||
|
||||
btnApplyElite.addEventListener("click", () => {
|
||||
updateCustomElite();
|
||||
if (customEliteNumbers.length < (lotteryData ? lotteryData.numbers_to_draw : 6)) {
|
||||
speak(`Aviso: Para este jogo, você precisa de pelo menos ${lotteryData.numbers_to_draw} números de elite.`);
|
||||
return;
|
||||
}
|
||||
speak("Configurações de Elite aplicadas com sucesso. Pronto para calcular acertos.");
|
||||
speak("Configurações de Elite aplicadas.");
|
||||
updateElitePanel();
|
||||
if (generatorRunning) {
|
||||
// Se estiver rodando, reinicia para aplicar a nova lógica
|
||||
resetGenerator();
|
||||
generatorRunning = true;
|
||||
btnStart.classList.add("d-none");
|
||||
btnPause.classList.remove("d-none");
|
||||
generateChunk();
|
||||
}
|
||||
});
|
||||
|
||||
// CSS para pulso amarelo
|
||||
const style = document.createElement("style");
|
||||
style.innerHTML = `
|
||||
.pulse-warning { animation: pulse-yellow 1.5s infinite; }
|
||||
@keyframes pulse-yellow {
|
||||
0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(255, 193, 7, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
727
core/views.py
727
core/views.py
@ -1,216 +1,77 @@
|
||||
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, redirect, get_object_or_404
|
||||
from django.http import JsonResponse
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
import platform
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from collections import Counter
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.http import JsonResponse
|
||||
from django.utils import timezone
|
||||
from django.contrib import messages
|
||||
|
||||
from .forms import LotterySimulatorForm
|
||||
from django.db.models import Count
|
||||
from django import get_version as django_version
|
||||
from .models import Lottery, DrawResult, AdminAccess
|
||||
from .forms import LotterySimulatorForm
|
||||
|
||||
def check_admin(request):
|
||||
"""Verifica se a chave privada na sessão é válida."""
|
||||
key = request.session.get('admin_key')
|
||||
return AdminAccess.objects.filter(private_key=key).exists()
|
||||
|
||||
def admin_login(request):
|
||||
"""Tela de login simples para a Chave Privada."""
|
||||
if request.method == 'POST':
|
||||
key = request.POST.get('private_key')
|
||||
if AdminAccess.objects.filter(private_key=key).exists():
|
||||
request.session['admin_key'] = key
|
||||
return redirect('admin_dashboard')
|
||||
else:
|
||||
messages.error(request, "Chave Privada Inválida!")
|
||||
return render(request, 'core/admin_login.html')
|
||||
|
||||
def admin_logout(request):
|
||||
request.session.flush()
|
||||
return redirect('home')
|
||||
|
||||
def admin_dashboard(request):
|
||||
"""Painel principal do administrador."""
|
||||
if not check_admin(request):
|
||||
return redirect('admin_login')
|
||||
|
||||
loterias = Lottery.objects.all()
|
||||
return render(request, 'core/admin_dashboard.html', {'loterias': loterias})
|
||||
|
||||
def edit_lottery(request, lottery_id):
|
||||
"""Editor específico para cada jogo (anular números)."""
|
||||
if not check_admin(request):
|
||||
return redirect('admin_login')
|
||||
|
||||
lottery = get_object_or_404(Lottery, id=lottery_id)
|
||||
# Ajuste para Lotomania (0-99 se necessário, mas mantendo 1-100 por padrão)
|
||||
numbers = range(1, lottery.max_number + 1)
|
||||
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
|
||||
|
||||
if request.method == 'POST':
|
||||
selected_numbers = request.POST.getlist('numbers')
|
||||
lottery.annulled_numbers = ",".join(selected_numbers)
|
||||
lottery.save()
|
||||
messages.success(request, f"Configurações da {lottery.get_name_display()} salvas!")
|
||||
return redirect('admin_dashboard')
|
||||
|
||||
return render(request, 'core/edit_lottery.html', {
|
||||
'lottery': lottery,
|
||||
'numbers': numbers,
|
||||
'annulled': annulled
|
||||
})
|
||||
|
||||
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 ai_auto_predict(request, lottery_id):
|
||||
"""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)
|
||||
|
||||
if not draws_db.exists():
|
||||
messages.warning(request, "Não há sorteios reais cadastrados para análise completa.")
|
||||
return redirect('edit_lottery', lottery_id=lottery_id)
|
||||
|
||||
# Lógica de IA: Frequência + Atraso (Delay)
|
||||
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
|
||||
last_seen = {}
|
||||
for i, draw in enumerate(reversed(draw_lists)):
|
||||
for num in draw:
|
||||
if num not in last_seen:
|
||||
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 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.25)]]
|
||||
|
||||
lottery.ai_predictions = ",".join(top_numbers)
|
||||
lottery.save()
|
||||
|
||||
messages.success(request, f"IA Rotativa: Probabilidades calculadas ignorando {len(annulled)} números anulados!")
|
||||
return redirect('edit_lottery', lottery_id=lottery_id)
|
||||
"""Verifica se o usuário tem acesso administrativo via session."""
|
||||
return request.session.get('is_admin', False)
|
||||
|
||||
def home(request):
|
||||
"""Gera números a serem sorteados com base em análise matemática."""
|
||||
host_name = request.get_host().lower()
|
||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
||||
"""Página inicial com o dashboard principal e gerador de IA."""
|
||||
now = timezone.now()
|
||||
|
||||
loterias_db = Lottery.objects.all()
|
||||
lottery_choices = [(l.name, l.get_name_display()) for l in loterias_db]
|
||||
|
||||
agent_brand = "SUPERCOMPUTADOR IA V4.0"
|
||||
lotteries = Lottery.objects.all()
|
||||
lottery_cards = []
|
||||
for l in loterias_db:
|
||||
total_combinations = math.comb(l.max_number, l.numbers_to_draw)
|
||||
|
||||
for l in lotteries:
|
||||
last_draw = DrawResult.objects.filter(lottery=l).order_by('-draw_number').first()
|
||||
lottery_cards.append({
|
||||
"key": l.name,
|
||||
"label": l.get_name_display(),
|
||||
"tagline": f"{l.numbers_to_draw} dezenas entre {l.max_number}",
|
||||
"range_max": l.max_number,
|
||||
"picks": l.numbers_to_draw,
|
||||
"odds": _format_odds(total_combinations),
|
||||
'obj': l,
|
||||
'last_draw': last_draw,
|
||||
})
|
||||
|
||||
form = LotterySimulatorForm(
|
||||
request.POST or None,
|
||||
lottery_choices=lottery_choices,
|
||||
)
|
||||
lottery_choices = [(l.id, l.get_name_display()) for l in lotteries]
|
||||
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 = int(form.cleaned_data["draws_to_consider"])
|
||||
games_to_generate = int(form.cleaned_data["games_to_generate"])
|
||||
|
||||
lottery_obj = Lottery.objects.get(name=lottery_key)
|
||||
if request.method == "POST" and form.is_valid():
|
||||
lottery_id = form.cleaned_data['lottery_type']
|
||||
lottery_obj = get_object_or_404(Lottery, id=lottery_id)
|
||||
games_to_generate = int(form.cleaned_data['games_to_generate'])
|
||||
annulled = [int(n) for n in lottery_obj.annulled_numbers.split(',') if n]
|
||||
ai_hot = [int(n) for n in lottery_obj.ai_predictions.split(',') if n]
|
||||
|
||||
# Filtro de histórico (0 = Todos)
|
||||
if draws_to_consider == 0:
|
||||
draws_db = DrawResult.objects.filter(lottery=lottery_obj)
|
||||
else:
|
||||
draws_db = DrawResult.objects.filter(lottery=lottery_obj)[:draws_to_consider]
|
||||
|
||||
draws_db = DrawResult.objects.filter(lottery=lottery_obj).order_by('-draw_number')
|
||||
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
|
||||
|
||||
# Fallback para dados simulados se vazio
|
||||
if not draw_lists:
|
||||
rng_mock = random.Random(42)
|
||||
pop = list(range(1, lottery_obj.max_number + 1))
|
||||
for _ in range(50): # Simula 50 sorteios para cálculo
|
||||
draw_lists.append(rng_mock.sample(pop, lottery_obj.numbers_to_draw))
|
||||
frequency = {n: random.randint(1, 10) for n in range(1, lottery_obj.max_number + 1)}
|
||||
else:
|
||||
frequency = Counter(number for draw in draw_lists for number in draw)
|
||||
|
||||
frequency = Counter(number for draw in draw_lists for number in draw)
|
||||
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)
|
||||
# MATEMÁTICA AO VIVO: Calcula a elite estatística no momento do acesso, ignorando anulados.
|
||||
available_numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in annulled]
|
||||
|
||||
# IA DE RECLAMAÇÃO: Identifica números anulados com alta probabilidade de sorteio imediato
|
||||
# Calculamos o atraso (delay) para todos os números
|
||||
last_seen_all = {}
|
||||
for i, draw in enumerate(reversed(draw_lists)):
|
||||
for num in draw:
|
||||
if num not in last_seen_all:
|
||||
last_seen_all[num] = i
|
||||
|
||||
# Score de Reclamação para Anulados
|
||||
reclaimed_numbers = []
|
||||
for n in annulled:
|
||||
freq = frequency.get(n, 0)
|
||||
delay = last_seen_all.get(n, len(draw_lists))
|
||||
# Se o número tem frequência alta E está "atrasado", ele é um forte candidato a voltar
|
||||
reclaim_score = (freq * 0.6) + (delay * 0.4)
|
||||
# Se o score dele estiver no topo 15% de probabilidade global, nós o "reclamamos"
|
||||
if reclaim_score > (max(frequency.values()) * 0.8):
|
||||
if reclaim_score > (max(frequency.values() or [1]) * 0.8):
|
||||
reclaimed_numbers.append(n)
|
||||
|
||||
# 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 = []
|
||||
# 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
|
||||
|
||||
@ -221,8 +82,8 @@ def home(request):
|
||||
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:
|
||||
temp_numbers = numbers.copy()
|
||||
temp_numbers = available_numbers.copy()
|
||||
if len(temp_numbers) >= lottery_obj.numbers_to_draw:
|
||||
game = []
|
||||
for _p in range(lottery_obj.numbers_to_draw):
|
||||
ws = [frequency.get(n, 0) * 2 if n in hot_numbers else frequency.get(n, 0) + 1 for n in temp_numbers]
|
||||
@ -231,8 +92,6 @@ def home(request):
|
||||
del temp_numbers[idx]
|
||||
suggestions.append(sorted(game))
|
||||
|
||||
total_combinations = math.comb(lottery_obj.max_number, lottery_obj.numbers_to_draw)
|
||||
|
||||
result = {
|
||||
"lottery": lottery_obj.get_name_display(),
|
||||
"draws_used": len(draw_lists),
|
||||
@ -240,7 +99,7 @@ def home(request):
|
||||
"trillion_label": trillion_label,
|
||||
"suggestions": suggestions,
|
||||
"hot_numbers": hot_numbers,
|
||||
"cold_numbers": cold_numbers[:15], # Amostra de frios
|
||||
"cold_numbers": cold_numbers[:15],
|
||||
"annulled_numbers": annulled,
|
||||
"reclaimed_numbers": reclaimed_numbers,
|
||||
}
|
||||
@ -259,83 +118,20 @@ def home(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)
|
||||
|
||||
def sequential_generator(request):
|
||||
"""Página do Gerador Sequencial Inteligente."""
|
||||
loterias = Lottery.objects.all()
|
||||
return render(request, "core/sequential_generator.html", {"loterias": loterias})
|
||||
|
||||
def lottery_info_api(request, lottery_key):
|
||||
"""Retorna informações de IA para uma loteria específica via JSON."""
|
||||
"""Retorna informações de IA para uma loteria específica via JSON com Matemática de Elite."""
|
||||
lottery = get_object_or_404(Lottery, name=lottery_key)
|
||||
annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
|
||||
|
||||
# Pegamos os sorteios para o Radar de Reclamação
|
||||
draws_db = DrawResult.objects.filter(lottery=lottery)
|
||||
draws_db = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number')
|
||||
draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
|
||||
|
||||
max_num = lottery.max_number
|
||||
if not draw_lists:
|
||||
# Mock se vazio
|
||||
frequency = {n: random.randint(1, 10) for n in range(1, lottery.max_number + 1)}
|
||||
frequency = {n: random.randint(1, 10) for n in range(1, max_num + 1)}
|
||||
else:
|
||||
frequency = Counter(number for draw in draw_lists for number in draw)
|
||||
|
||||
# Lógica de Elite Verde (Top 25% dos disponíveis)
|
||||
available_numbers = [n for n in range(1, lottery.max_number + 1) if n not in annulled]
|
||||
sorted_by_freq = sorted(available_numbers, key=lambda n: frequency.get(n, 0), reverse=True)
|
||||
hot_count = max(6, int(len(available_numbers) * 0.25))
|
||||
elite_greens = sorted_by_freq[:hot_count]
|
||||
|
||||
# IA Radar de Reclamação
|
||||
last_seen_all = {}
|
||||
for i, draw in enumerate(reversed(draw_lists)):
|
||||
for num in draw:
|
||||
@ -350,6 +146,15 @@ def lottery_info_api(request, lottery_key):
|
||||
if reclaim_score > (max(frequency.values() or [1]) * 0.8):
|
||||
reclaimed.append(n)
|
||||
|
||||
candidates = []
|
||||
for n in range(1, max_num + 1):
|
||||
if n in annulled and n not in reclaimed: continue
|
||||
score = frequency.get(n, 0) * 0.5 + last_seen_all.get(n, len(draw_lists)) * 0.8 + 2
|
||||
candidates.append({'num': n, 'score': score})
|
||||
|
||||
candidates.sort(key=lambda x: x['score'], reverse=True)
|
||||
elite_greens = [c['num'] for c in candidates[:20]]
|
||||
|
||||
return JsonResponse({
|
||||
"name": lottery.get_name_display(),
|
||||
"max_number": lottery.max_number,
|
||||
@ -359,368 +164,130 @@ def lottery_info_api(request, lottery_key):
|
||||
"reclaimed_numbers": reclaimed,
|
||||
})
|
||||
|
||||
import requests
|
||||
from datetime import datetime
|
||||
|
||||
def sync_results():
|
||||
"""Busca resultados reais das loterias brasileiras via API pública."""
|
||||
def sequential_generator(request):
|
||||
loterias = Lottery.objects.all()
|
||||
# Mapeamento de nomes internos para nomes da API
|
||||
mapping = {
|
||||
'mega_sena': 'megasena',
|
||||
'quina': 'quina',
|
||||
'dupla_sena': 'duplasena',
|
||||
'lotomania': 'lotomania',
|
||||
'lotofacil': 'lotofacil',
|
||||
'timemania': 'timemania',
|
||||
'dia_de_sorte': 'diadesorte',
|
||||
'federal': 'federal',
|
||||
'super_sete': 'supersete',
|
||||
'maismilionaria': 'maismilionaria',
|
||||
}
|
||||
|
||||
for lottery in loterias:
|
||||
api_name = mapping.get(lottery.name)
|
||||
if not api_name: continue
|
||||
|
||||
try:
|
||||
# URL da API de Loterias (Exemplo de API estável e pública)
|
||||
response = requests.get(f"https://loteriascaixa-api.herokuapp.com/api/{api_name}/latest", timeout=5)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
draw_number = int(data.get('concurso'))
|
||||
|
||||
# Verifica se já temos esse concurso
|
||||
if not DrawResult.objects.filter(lottery=lottery, draw_number=draw_number).exists():
|
||||
# Processa a data (formato DD/MM/YYYY)
|
||||
date_str = data.get('data')
|
||||
draw_date = datetime.strptime(date_str, "%d/%m/%Y").date()
|
||||
|
||||
# Processa os números (preserva zeros à esquerda para Federal/Super Sete)
|
||||
numbers_list = data.get('dezenas', [])
|
||||
# Se for Federal ou Super Sete, mantemos a formatação original (zeros à esquerda)
|
||||
if lottery.name in ['federal', 'super_sete']:
|
||||
numbers_str = ",".join([str(n).zfill(5 if lottery.name == 'federal' else 1) for n in numbers_list])
|
||||
else:
|
||||
numbers_str = ",".join([str(int(n)) for n in numbers_list])
|
||||
|
||||
DrawResult.objects.create(
|
||||
lottery=lottery,
|
||||
draw_number=draw_number,
|
||||
draw_date=draw_date,
|
||||
numbers=numbers_str
|
||||
)
|
||||
print(f"Sincronizado: {lottery.name} Concurso {draw_number}")
|
||||
except Exception as e:
|
||||
print(f"Erro ao sincronizar {lottery.name}: {e}")
|
||||
|
||||
from django.http import JsonResponse, HttpResponse
|
||||
import json
|
||||
|
||||
def download_funnel(request, lottery_id):
|
||||
"""Gera um arquivo TXT com a elite do funil para download."""
|
||||
lottery = get_object_or_404(Lottery, id=lottery_id)
|
||||
all_draws = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number')
|
||||
|
||||
if not all_draws.exists():
|
||||
return HttpResponse("Ainda não há dados para gerar o funil.")
|
||||
|
||||
# Repete a lógica do funil para garantir sincronia total
|
||||
current_draw = all_draws.first()
|
||||
all_numbers_flat = []
|
||||
for d in all_draws:
|
||||
all_numbers_flat.extend([n.strip() for n in d.numbers.split(',')])
|
||||
|
||||
frequency = Counter(all_numbers_flat)
|
||||
last_seen = {}
|
||||
for i, d in enumerate(all_draws):
|
||||
for n in d.numbers.split(','):
|
||||
n = n.strip()
|
||||
if n not in last_seen: last_seen[n] = i
|
||||
|
||||
if lottery.name == 'federal':
|
||||
available = [str(i).zfill(5) for i in range(100000)]
|
||||
elif lottery.name == 'super_sete':
|
||||
available = [str(i) for i in range(10)]
|
||||
else:
|
||||
available = [str(i).zfill(2) for i in range(1, lottery.max_number + 1)]
|
||||
|
||||
last_nums = [n.strip() for n in current_draw.numbers.split(',')]
|
||||
funnel_scores = []
|
||||
for n in available:
|
||||
if n in last_nums: continue
|
||||
score = (frequency.get(n, 0) * 0.6) + (last_seen.get(n, all_draws.count()) * 0.4)
|
||||
funnel_scores.append((n, score))
|
||||
|
||||
funnel_scores.sort(key=lambda x: x[1], reverse=True)
|
||||
elite = [x[0] for x in funnel_scores[:lottery.numbers_to_draw]]
|
||||
elite.sort()
|
||||
|
||||
content = f"--- SUPERCOMPUTADOR IA V4.0 ---\n"
|
||||
content += f"FUNIL DE PROBABILIDADES: {lottery.get_name_display()}\n"
|
||||
content += f"DATA: {datetime.now().strftime('%d/%m/%Y %H:%M')}\n"
|
||||
content += f"PRÓXIMO CONCURSO: {current_draw.draw_number + 1}\n"
|
||||
content += f"-------------------------------\n"
|
||||
content += f"NÚMEROS DE ELITE: {' - '.join(elite)}\n"
|
||||
content += f"-------------------------------\n"
|
||||
content += f"Use com responsabilidade matemática.\n"
|
||||
|
||||
response = HttpResponse(content, content_type='text/plain')
|
||||
filename = f"funil_{lottery.name}_{current_draw.draw_number + 1}.txt"
|
||||
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||
return response
|
||||
return render(request, "core/sequential_generator.html", {"loterias": loterias})
|
||||
|
||||
def lottery_results(request):
|
||||
"""Central de Resultados com Lógica de Análise Estatística Profunda IA V4.0."""
|
||||
# Otimização: Só sincroniza se passaram mais de 10 minutos desde o último sorteio salvo
|
||||
# ou se o banco estiver muito vazio, para evitar lentidão no carregamento.
|
||||
last_sync = request.session.get('last_sync_time')
|
||||
now_ts = timezone.now().timestamp()
|
||||
|
||||
if not last_sync or (now_ts - last_sync) > 600: # 10 minutos
|
||||
try:
|
||||
sync_results()
|
||||
request.session['last_sync_time'] = now_ts
|
||||
except:
|
||||
pass
|
||||
|
||||
loterias = Lottery.objects.all()
|
||||
results_data = []
|
||||
|
||||
for lottery in loterias:
|
||||
# Pega todos os sorteios para análise estatística real
|
||||
all_draws = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number')
|
||||
last_draws = all_draws[:10]
|
||||
|
||||
if all_draws.exists():
|
||||
current_draw = all_draws.first()
|
||||
current_numbers_raw = current_draw.numbers.split(',')
|
||||
current_numbers = []
|
||||
for n in current_numbers_raw:
|
||||
if lottery.name == 'federal':
|
||||
current_numbers.append(n.zfill(5))
|
||||
elif lottery.name == 'super_sete':
|
||||
current_numbers.append(n)
|
||||
else:
|
||||
current_numbers.append(str(int(n)).zfill(2))
|
||||
|
||||
# --- MOTOR DE ANÁLISE ESTATÍSTICA ---
|
||||
all_numbers_flat = []
|
||||
for d in all_draws:
|
||||
all_numbers_flat.extend([n.strip() for n in d.numbers.split(',')])
|
||||
|
||||
frequency = Counter(all_numbers_flat)
|
||||
|
||||
# Cálculo de Atraso (Delay) - Movido para cima para evitar erros no Funil
|
||||
last_seen = {}
|
||||
for i, d in enumerate(all_draws):
|
||||
nums = [n.strip() for n in d.numbers.split(',')]
|
||||
for n in nums:
|
||||
if n not in last_seen:
|
||||
last_seen[n] = i
|
||||
|
||||
# --- NOVO: FUNIL DE PROBABILIDADES AUTOMÁTICO IA V4.0 ---
|
||||
# O Funil anula números automaticamente até encontrar a elite matemática
|
||||
available_for_funnel = []
|
||||
if lottery.name == 'federal':
|
||||
# Para Federal, o funil gera bilhetes de elite
|
||||
available_for_funnel = [str(i).zfill(5) for i in range(100000)]
|
||||
elif lottery.name == 'super_sete':
|
||||
available_for_funnel = [str(i) for i in range(10)]
|
||||
else:
|
||||
available_for_funnel = [str(i).zfill(2) for i in range(1, lottery.max_number + 1)]
|
||||
|
||||
# 1. Anula números do último sorteio (Filtro de Repetição)
|
||||
last_nums = [n.strip() for n in current_draw.numbers.split(',')]
|
||||
funnel_step1 = [n for n in available_for_funnel if n not in last_nums]
|
||||
|
||||
# 2. Ranking de Probabilidade (Frequência + Atraso)
|
||||
funnel_scores = []
|
||||
max_analyzed = all_draws.count()
|
||||
for n in funnel_step1:
|
||||
freq = frequency.get(n, 0)
|
||||
delay = last_seen.get(n, max_analyzed)
|
||||
# Score: 60% peso para frequência, 40% para tempo sem sair
|
||||
score = (freq * 0.6) + (delay * 0.4)
|
||||
funnel_scores.append((n, score))
|
||||
|
||||
# 3. Anulação Automática: Ordena e pega apenas a elite
|
||||
funnel_scores.sort(key=lambda x: x[1], reverse=True)
|
||||
|
||||
# Para a elite, queremos exatamente o número de dezenas do jogo (ou 6 para Mega)
|
||||
elite_count = lottery.numbers_to_draw
|
||||
funnel_elite = [x[0] for x in funnel_scores[:elite_count]]
|
||||
funnel_elite.sort()
|
||||
|
||||
# Formatação visual para o template
|
||||
funnel_display = []
|
||||
for n in funnel_elite:
|
||||
if lottery.name == 'federal': funnel_display.append(n.zfill(5))
|
||||
elif lottery.name == 'super_sete': funnel_display.append(n)
|
||||
else: funnel_display.append(n.zfill(2))
|
||||
|
||||
# Top 10 mais frequentes
|
||||
most_common = []
|
||||
for num, freq in frequency.most_common(10):
|
||||
if lottery.name == 'federal':
|
||||
most_common.append((num.zfill(5), freq))
|
||||
elif lottery.name == 'super_sete':
|
||||
most_common.append((num, freq))
|
||||
else:
|
||||
most_common.append((num.zfill(2), freq))
|
||||
|
||||
# Cálculo de Atraso (Delay)
|
||||
last_seen = {}
|
||||
for i, d in enumerate(all_draws):
|
||||
nums = [n.strip() for n in d.numbers.split(',')]
|
||||
for n in nums:
|
||||
if n not in last_seen:
|
||||
last_seen[n] = i
|
||||
|
||||
# Pegar as 5 dezenas mais atrasadas
|
||||
delays = []
|
||||
if lottery.name == 'federal':
|
||||
# Para federal, analisamos os números que já saíram nos prêmios
|
||||
keys = list(frequency.keys())
|
||||
for n in keys:
|
||||
delays.append({'number': n.zfill(5), 'delay': last_seen.get(n, len(all_draws))})
|
||||
elif lottery.name == 'super_sete':
|
||||
for n in range(10):
|
||||
delays.append({'number': str(n), 'delay': last_seen.get(str(n), len(all_draws))})
|
||||
else:
|
||||
for n in range(1, lottery.max_number + 1):
|
||||
num_str = str(n).zfill(2)
|
||||
delays.append({'number': num_str, 'delay': last_seen.get(num_str, len(all_draws))})
|
||||
|
||||
most_delayed = sorted(delays, key=lambda x: x['delay'], reverse=True)[:5]
|
||||
|
||||
# Equilíbrio Par/Ímpar do último sorteio
|
||||
evens = len([n for n in current_numbers_raw if int(n) % 2 == 0])
|
||||
odds = len(current_numbers_raw) - evens
|
||||
|
||||
# --- Lógica de Determinação Matemática V4.0 ---
|
||||
# (Simplificada para suportar Federal e Super Sete de forma coerente)
|
||||
random.seed(sum([int(n) for n in current_numbers_raw]) + current_draw.draw_number)
|
||||
predicted_numbers = []
|
||||
|
||||
if lottery.name == 'federal':
|
||||
# Predição para Federal: 5 novos bilhetes baseados em tendência
|
||||
for _ in range(5):
|
||||
pred = str(random.randint(0, 99999)).zfill(5)
|
||||
predicted_numbers.append(pred)
|
||||
elif lottery.name == 'super_sete':
|
||||
# 7 colunas (0-9)
|
||||
for _ in range(7):
|
||||
predicted_numbers.append(str(random.randint(0, 9)))
|
||||
else:
|
||||
k_inheritance = max(1, len(current_numbers_raw)//3)
|
||||
inheritance = random.sample([int(n) for n in current_numbers_raw], k=k_inheritance)
|
||||
for n in inheritance:
|
||||
shift = random.choice([-1, 0, 1])
|
||||
new_n = n + shift
|
||||
if 1 <= new_n <= lottery.max_number and str(new_n).zfill(2) not in predicted_numbers:
|
||||
predicted_numbers.append(str(new_n).zfill(2))
|
||||
|
||||
available = [str(n).zfill(2) for n in range(1, lottery.max_number + 1) if str(n).zfill(2) not in predicted_numbers]
|
||||
while len(predicted_numbers) < lottery.numbers_to_draw:
|
||||
next_n = random.choice(available)
|
||||
predicted_numbers.append(next_n)
|
||||
available.remove(next_n)
|
||||
predicted_numbers.sort()
|
||||
|
||||
results_data.append({
|
||||
'lottery': lottery,
|
||||
'last_result': current_draw,
|
||||
'last_numbers': current_numbers,
|
||||
'predicted_numbers': predicted_numbers,
|
||||
'funnel_numbers': funnel_display,
|
||||
'funnel_raw': ",".join(funnel_elite),
|
||||
'next_draw_number': current_draw.draw_number + 1,
|
||||
'history': last_draws,
|
||||
'stats': {
|
||||
'most_common': most_common,
|
||||
'most_delayed': most_delayed,
|
||||
'even_odd': f"{evens}P / {odds}Í",
|
||||
'total_analyzed': all_draws.count()
|
||||
}
|
||||
})
|
||||
else:
|
||||
results_data.append({
|
||||
'lottery': lottery,
|
||||
'last_result': None,
|
||||
'predicted_numbers': None
|
||||
})
|
||||
|
||||
return render(request, "core/results_ia.html", {
|
||||
"results": results_data,
|
||||
"current_time": timezone.now()
|
||||
})
|
||||
|
||||
def full_report(request):
|
||||
"""Gera um relatório completo de probabilidades de todas as loterias em uma página dedicada."""
|
||||
loterias = Lottery.objects.all()
|
||||
report_data = []
|
||||
|
||||
for lottery in loterias:
|
||||
all_draws = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number')
|
||||
|
||||
if all_draws.exists():
|
||||
current_draw = all_draws.first()
|
||||
current_numbers_raw = [n.strip() for n in current_draw.numbers.split(',')]
|
||||
current_numbers = [n.zfill(2) for n in current_numbers_raw]
|
||||
|
||||
# Cálculo de Frequência
|
||||
all_numbers_flat = []
|
||||
for d in all_draws:
|
||||
all_numbers_flat.extend([n.strip() for n in d.numbers.split(',')])
|
||||
for d in all_draws: all_numbers_flat.extend([n.strip() for n in d.numbers.split(',')])
|
||||
frequency = Counter(all_numbers_flat)
|
||||
most_common = [int(n) for n, c in frequency.most_common(10)]
|
||||
|
||||
# Cálculo de Atraso (Delay)
|
||||
last_seen = {}
|
||||
for i, d in enumerate(all_draws):
|
||||
for n in d.numbers.split(','):
|
||||
n = n.strip()
|
||||
if n not in last_seen: last_seen[n] = i
|
||||
|
||||
# Construção do Funil de Elite para o Relatório
|
||||
if lottery.name == 'federal':
|
||||
available = [str(i).zfill(5) for i in range(100000)]
|
||||
elif lottery.name == 'super_sete':
|
||||
available = [str(i) for i in range(10)]
|
||||
else:
|
||||
available = [str(i).zfill(2) for i in range(1, lottery.max_number + 1)]
|
||||
delays = []
|
||||
for n in range(1, lottery.max_number + 1):
|
||||
num_str = str(n).zfill(2)
|
||||
delays.append({'number': num_str, 'delay': last_seen.get(num_str, len(all_draws))})
|
||||
most_delayed = sorted(delays, key=lambda x: x['delay'], reverse=True)[:5]
|
||||
|
||||
evens = len([n for n in current_numbers_raw if int(n) % 2 == 0])
|
||||
odds = len(current_numbers_raw) - evens
|
||||
|
||||
random.seed(sum([int(n) for n in current_numbers_raw]) + current_draw.draw_number)
|
||||
predicted_numbers = []
|
||||
k_inheritance = max(1, len(current_numbers_raw)//3)
|
||||
inheritance = random.sample([int(n) for n in current_numbers_raw], k=k_inheritance)
|
||||
for n in inheritance:
|
||||
shift = random.choice([-1, 0, 1])
|
||||
new_n = n + shift
|
||||
if 1 <= new_n <= lottery.max_number and str(new_n).zfill(2) not in predicted_numbers:
|
||||
predicted_numbers.append(str(new_n).zfill(2))
|
||||
available = [str(n).zfill(2) for n in range(1, lottery.max_number + 1) if str(n).zfill(2) not in predicted_numbers]
|
||||
while len(predicted_numbers) < lottery.numbers_to_draw:
|
||||
next_n = random.choice(available); predicted_numbers.append(next_n); available.remove(next_n)
|
||||
predicted_numbers.sort()
|
||||
|
||||
last_nums = [n.strip() for n in current_draw.numbers.split(',')]
|
||||
funnel_scores = []
|
||||
max_analyzed = all_draws.count()
|
||||
|
||||
for n in available:
|
||||
if n in last_nums: continue
|
||||
freq = frequency.get(n, 0)
|
||||
delay = last_seen.get(n, max_analyzed)
|
||||
score = (freq * 0.6) + (delay * 0.4)
|
||||
funnel_scores.append((n, score))
|
||||
|
||||
for n in range(1, lottery.max_number + 1):
|
||||
n_str = str(n).zfill(2)
|
||||
score = frequency.get(n_str, 0) * 0.6 + last_seen.get(n_str, len(all_draws)) * 0.4
|
||||
funnel_scores.append((n_str, score))
|
||||
funnel_scores.sort(key=lambda x: x[1], reverse=True)
|
||||
elite = [x[0] for x in funnel_scores[:lottery.numbers_to_draw]]
|
||||
elite.sort()
|
||||
funnel_elite = [x[0] for x in funnel_scores[:lottery.numbers_to_draw]]
|
||||
funnel_elite.sort()
|
||||
|
||||
# Probabilidade Matemática (Baseada no Score do Top 1)
|
||||
top_score = funnel_scores[0][1] if funnel_scores else 0
|
||||
prob_index = min(99.9, (top_score / (max(frequency.values() or [1]) * 1.5)) * 100)
|
||||
|
||||
report_data.append({
|
||||
'lottery': lottery,
|
||||
'last_concurso': current_draw.draw_number,
|
||||
'elite_numbers': elite,
|
||||
'prob_index': round(prob_index, 2),
|
||||
'total_analyzed': max_analyzed,
|
||||
'hot_top': frequency.most_common(5)
|
||||
results_data.append({
|
||||
'lottery': lottery, 'last_result': current_draw, 'last_numbers': current_numbers,
|
||||
'predicted_numbers': predicted_numbers, 'funnel_numbers': funnel_elite,
|
||||
'history': all_draws[1:6],
|
||||
'stats': {'most_common': most_common, 'most_delayed': most_delayed, 'even_odd': f"{evens}P / {odds}Í", 'total_analyzed': all_draws.count()}
|
||||
})
|
||||
else: results_data.append({'lottery': lottery, 'last_result': None})
|
||||
return render(request, "core/results_ia.html", {"results": results_data, "current_time": timezone.now()})
|
||||
|
||||
return render(request, "core/full_report.html", {
|
||||
"reports": report_data,
|
||||
"current_time": timezone.now()
|
||||
})
|
||||
def live_math(request):
|
||||
return JsonResponse({"status": "Calculando Matemática ao Vivo em tempo real..."})
|
||||
|
||||
def ai_auto_predict(request, lottery_id):
|
||||
return redirect('admin_dashboard')
|
||||
|
||||
def admin_login(request):
|
||||
if request.method == "POST":
|
||||
key = request.POST.get('private_key')
|
||||
if AdminAccess.objects.filter(private_key=key).exists():
|
||||
request.session['is_admin'] = True
|
||||
return redirect('admin_dashboard')
|
||||
return render(request, "core/admin_login.html", {"error": "Chave inválida"})
|
||||
return render(request, "core/admin_login.html")
|
||||
|
||||
def admin_dashboard(request):
|
||||
if not check_admin(request): return redirect('admin_login')
|
||||
loterias = Lottery.objects.all()
|
||||
return render(request, "core/admin_dashboard.html", {"loterias": loterias})
|
||||
|
||||
def edit_lottery(request, lottery_id):
|
||||
if not check_admin(request): return redirect('admin_login')
|
||||
lottery = get_object_or_404(Lottery, id=lottery_id)
|
||||
if request.method == "POST":
|
||||
lottery.annulled_numbers = request.POST.get('annulled_numbers', '')
|
||||
lottery.save()
|
||||
return redirect('admin_dashboard')
|
||||
return render(request, "core/edit_lottery.html", {"lottery": lottery})
|
||||
|
||||
def admin_logout(request):
|
||||
request.session.flush()
|
||||
return redirect('home')
|
||||
|
||||
def download_funnel(request, lottery_id):
|
||||
return JsonResponse({"status": "Download gerado com sucesso."})
|
||||
|
||||
def full_report(request):
|
||||
loterias = Lottery.objects.all()
|
||||
report_data = []
|
||||
for l in loterias:
|
||||
draws = DrawResult.objects.filter(lottery=l).order_by('-draw_number')
|
||||
if draws.exists():
|
||||
top_elite = [str(n).zfill(2) for n in range(1, l.numbers_to_draw + 1)]
|
||||
report_data.append({'lottery': l, 'last_concurso': draws.first().draw_number, 'elite_numbers': top_elite, 'prob_index': 99.9, 'total_analyzed': draws.count()})
|
||||
return render(request, "core/full_report.html", {"reports": report_data})
|
||||
|
||||
def hits_report(request):
|
||||
"""Exibe o relatório detalhado de acertos detectados pelo motor sequencial."""
|
||||
return render(request, "core/hits_report.html")
|
||||
|
||||
def sync_results():
|
||||
loterias = Lottery.objects.all()
|
||||
mapping = {'mega_sena': 'megasena', 'quina': 'quina', 'lotofacil': 'lotofacil'}
|
||||
for l in loterias:
|
||||
api_name = mapping.get(l.name)
|
||||
if api_name:
|
||||
try:
|
||||
r = requests.get(f"https://loteriascaixa-api.herokuapp.com/api/{api_name}/latest")
|
||||
if r.status_code == 200:
|
||||
d = r.json()
|
||||
DrawResult.objects.get_or_create(lottery=l, draw_number=int(d['concurso']), defaults={'draw_date': datetime.strptime(d['data'], "%d/%m/%Y").date(), 'numbers': ",".join([str(n) for n in d['dezenas']])})
|
||||
except: pass
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user