prophetdiceskills 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prophetdiceskills-1.0.0/PKG-INFO +59 -0
- prophetdiceskills-1.0.0/README.md +31 -0
- prophetdiceskills-1.0.0/prophetdiceskills/__init__.py +29 -0
- prophetdiceskills-1.0.0/prophetdiceskills/advanced_bot.py +198 -0
- prophetdiceskills-1.0.0/prophetdiceskills/advanced_dl.py +146 -0
- prophetdiceskills-1.0.0/prophetdiceskills/anomaly.py +81 -0
- prophetdiceskills-1.0.0/prophetdiceskills/bot.py +62 -0
- prophetdiceskills-1.0.0/prophetdiceskills/charting.py +81 -0
- prophetdiceskills-1.0.0/prophetdiceskills/core.py +1049 -0
- prophetdiceskills-1.0.0/prophetdiceskills/meta_learning.py +103 -0
- prophetdiceskills-1.0.0/prophetdiceskills/ml.py +352 -0
- prophetdiceskills-1.0.0/prophetdiceskills/online_learning.py +72 -0
- prophetdiceskills-1.0.0/prophetdiceskills/quantum.py +85 -0
- prophetdiceskills-1.0.0/prophetdiceskills.egg-info/PKG-INFO +59 -0
- prophetdiceskills-1.0.0/prophetdiceskills.egg-info/SOURCES.txt +18 -0
- prophetdiceskills-1.0.0/prophetdiceskills.egg-info/dependency_links.txt +1 -0
- prophetdiceskills-1.0.0/prophetdiceskills.egg-info/requires.txt +9 -0
- prophetdiceskills-1.0.0/prophetdiceskills.egg-info/top_level.txt +1 -0
- prophetdiceskills-1.0.0/setup.cfg +4 -0
- prophetdiceskills-1.0.0/setup.py +28 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prophetdiceskills
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A library converting RNG cracking algorithms into callable functions with bot emulation.
|
|
5
|
+
Author: Prophet Dice Skills
|
|
6
|
+
Author-email: prophet@example.com
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.6
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: numba
|
|
12
|
+
Requires-Dist: numpy
|
|
13
|
+
Requires-Dist: scipy
|
|
14
|
+
Requires-Dist: scikit-learn
|
|
15
|
+
Requires-Dist: PyWavelets
|
|
16
|
+
Requires-Dist: matplotlib
|
|
17
|
+
Requires-Dist: seaborn
|
|
18
|
+
Requires-Dist: river
|
|
19
|
+
Requires-Dist: torch
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: requires-dist
|
|
26
|
+
Dynamic: requires-python
|
|
27
|
+
Dynamic: summary
|
|
28
|
+
|
|
29
|
+
# Prophet Dice Skills
|
|
30
|
+
|
|
31
|
+
This library provides all the fundamental RNG cracking functions converted into callable API methods.
|
|
32
|
+
It also brings a full object-oriented dice bot simulation suite, letting you retrieve balances, place conditions, and wager effectively using standard interfaces.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from prophetdiceskills import ProphetBot, aggressive_time_crack
|
|
38
|
+
|
|
39
|
+
bot = ProphetBot(initial_balance=5000.0)
|
|
40
|
+
bot.setwager(10)
|
|
41
|
+
bot.setcondition("<")
|
|
42
|
+
bot.settarget(49.5)
|
|
43
|
+
|
|
44
|
+
result = 45.12
|
|
45
|
+
bot.simulate_roll(result)
|
|
46
|
+
|
|
47
|
+
print(bot.getbalance())
|
|
48
|
+
print(bot.getprofit())
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Functions Included
|
|
52
|
+
- `getbalance()`
|
|
53
|
+
- `setcondition()`
|
|
54
|
+
- `setwager()`
|
|
55
|
+
- `getresult()`
|
|
56
|
+
- ... and more, along with all `beatstake` / `stakeenhanced` capabilities!
|
|
57
|
+
- `GroundbreakingPredictor` for Deep Learning (Transformer/MLP/RF) time-series forecasting.
|
|
58
|
+
- `AdvancedBettingSystem` with Anti-Human Burst Strategies.
|
|
59
|
+
- `RegimeDetector` and `SimpleKalmanFilter` for signal processing and anomaly detection.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Prophet Dice Skills
|
|
2
|
+
|
|
3
|
+
This library provides all the fundamental RNG cracking functions converted into callable API methods.
|
|
4
|
+
It also brings a full object-oriented dice bot simulation suite, letting you retrieve balances, place conditions, and wager effectively using standard interfaces.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from prophetdiceskills import ProphetBot, aggressive_time_crack
|
|
10
|
+
|
|
11
|
+
bot = ProphetBot(initial_balance=5000.0)
|
|
12
|
+
bot.setwager(10)
|
|
13
|
+
bot.setcondition("<")
|
|
14
|
+
bot.settarget(49.5)
|
|
15
|
+
|
|
16
|
+
result = 45.12
|
|
17
|
+
bot.simulate_roll(result)
|
|
18
|
+
|
|
19
|
+
print(bot.getbalance())
|
|
20
|
+
print(bot.getprofit())
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Functions Included
|
|
24
|
+
- `getbalance()`
|
|
25
|
+
- `setcondition()`
|
|
26
|
+
- `setwager()`
|
|
27
|
+
- `getresult()`
|
|
28
|
+
- ... and more, along with all `beatstake` / `stakeenhanced` capabilities!
|
|
29
|
+
- `GroundbreakingPredictor` for Deep Learning (Transformer/MLP/RF) time-series forecasting.
|
|
30
|
+
- `AdvancedBettingSystem` with Anti-Human Burst Strategies.
|
|
31
|
+
- `RegimeDetector` and `SimpleKalmanFilter` for signal processing and anomaly detection.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .core import StakeRNG, aggressive_time_crack, aggressive_collision_farm, analyze_n_gram_transitions, verify_server_seed
|
|
2
|
+
from .bot import ProphetBot
|
|
3
|
+
from .ml import GroundbreakingPredictor, AdvancedFeatureEngineer, RegimeDetector, SimpleKalmanFilter
|
|
4
|
+
from .advanced_bot import AdvancedBettingSystem, ConfigManager
|
|
5
|
+
from .charting import RealTimePlotter
|
|
6
|
+
from .online_learning import OnlineLearningTracker
|
|
7
|
+
from .advanced_dl import TransformerPredictor
|
|
8
|
+
from .anomaly import RNGAnomalyDetector
|
|
9
|
+
from .quantum import QuantumInspiredOptimizer
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"StakeRNG",
|
|
13
|
+
"aggressive_time_crack",
|
|
14
|
+
"aggressive_collision_farm",
|
|
15
|
+
"analyze_n_gram_transitions",
|
|
16
|
+
"verify_server_seed",
|
|
17
|
+
"ProphetBot",
|
|
18
|
+
"GroundbreakingPredictor",
|
|
19
|
+
"AdvancedFeatureEngineer",
|
|
20
|
+
"RegimeDetector",
|
|
21
|
+
"SimpleKalmanFilter",
|
|
22
|
+
"AdvancedBettingSystem",
|
|
23
|
+
"ConfigManager",
|
|
24
|
+
"RealTimePlotter",
|
|
25
|
+
"OnlineLearningTracker",
|
|
26
|
+
"TransformerPredictor",
|
|
27
|
+
"RNGAnomalyDetector",
|
|
28
|
+
"QuantumInspiredOptimizer"
|
|
29
|
+
]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import random
|
|
4
|
+
from collections import deque
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
CONFIG_FILE = "auditor_config.json"
|
|
8
|
+
|
|
9
|
+
class ConfigManager:
|
|
10
|
+
"""Manages persistent settings"""
|
|
11
|
+
def __init__(self, filename=CONFIG_FILE):
|
|
12
|
+
self.filename = filename
|
|
13
|
+
self.config = {
|
|
14
|
+
"base_bet": 0.0000001,
|
|
15
|
+
"risk_level": "balanced", # conservative, balanced, aggressive
|
|
16
|
+
"stop_loss_pct": 0.15,
|
|
17
|
+
"take_profit_pct": 0.50,
|
|
18
|
+
"auto_retrain": True
|
|
19
|
+
}
|
|
20
|
+
self.load()
|
|
21
|
+
|
|
22
|
+
def load(self):
|
|
23
|
+
if os.path.exists(self.filename):
|
|
24
|
+
try:
|
|
25
|
+
with open(self.filename, 'r') as f:
|
|
26
|
+
loaded = json.load(f)
|
|
27
|
+
self.config.update(loaded)
|
|
28
|
+
except:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def save(self):
|
|
32
|
+
try:
|
|
33
|
+
with open(self.filename, 'w') as f:
|
|
34
|
+
json.dump(self.config, f, indent=4)
|
|
35
|
+
except:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def get(self, key):
|
|
39
|
+
return self.config.get(key)
|
|
40
|
+
|
|
41
|
+
def set(self, key, value):
|
|
42
|
+
self.config[key] = value
|
|
43
|
+
self.save()
|
|
44
|
+
|
|
45
|
+
class AdvancedBettingSystem:
|
|
46
|
+
"""
|
|
47
|
+
Short Burst Reset Anti-Human Non-Standard Strategy
|
|
48
|
+
Focuses on short, aggressive profit taking sequences with frequent resets
|
|
49
|
+
to avoid pattern detection and long-term negative variance.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, config_manager=None):
|
|
53
|
+
self.config = config_manager or ConfigManager()
|
|
54
|
+
self.base_bet = self.config.get("base_bet")
|
|
55
|
+
self.bankroll = 1.0
|
|
56
|
+
self.peak_bankroll = 1.0
|
|
57
|
+
self.drawdown = 0.0
|
|
58
|
+
|
|
59
|
+
# Performance tracking
|
|
60
|
+
self.trade_history = deque(maxlen=100)
|
|
61
|
+
self.short_term_memory = deque(maxlen=10) # For circuit breaker
|
|
62
|
+
self.win_rate = 0.5
|
|
63
|
+
self.sharpe_ratio = 0.0
|
|
64
|
+
|
|
65
|
+
# Burst Strategy State
|
|
66
|
+
self.burst_active = False
|
|
67
|
+
self.burst_step = 0
|
|
68
|
+
self.burst_history = []
|
|
69
|
+
self.cooldown_steps = 0
|
|
70
|
+
self.circuit_breaker_active = False
|
|
71
|
+
self.circuit_breaker_steps = 0
|
|
72
|
+
self.retrain_requested = False # Request model retraining
|
|
73
|
+
|
|
74
|
+
# Configuration
|
|
75
|
+
self.max_burst_length = 4
|
|
76
|
+
self.burst_multiplier = 2.5 # Aggressive start
|
|
77
|
+
self.compound_rate = 1.5 # Reinvest 50% of winnings in burst
|
|
78
|
+
self.reset_on_loss = True # Anti-Martingale (Reset immediately on loss)
|
|
79
|
+
|
|
80
|
+
# Anti-Human Parameters
|
|
81
|
+
self.jitter_factor = 0.13 # Add randomness to bet size
|
|
82
|
+
self.random_reset_prob = 0.05 # Randomly reset burst
|
|
83
|
+
|
|
84
|
+
# Flaw Detection / Risk Management
|
|
85
|
+
self.min_confidence_threshold = 0.55
|
|
86
|
+
self.max_drawdown_pct = 0.15 # Tightened stop loss
|
|
87
|
+
|
|
88
|
+
self.apply_risk_profile()
|
|
89
|
+
|
|
90
|
+
def apply_risk_profile(self):
|
|
91
|
+
"""Adjust parameters based on risk profile"""
|
|
92
|
+
profile = self.config.get("risk_level")
|
|
93
|
+
if profile == "conservative":
|
|
94
|
+
self.burst_multiplier = 1.5
|
|
95
|
+
self.compound_rate = 1.1
|
|
96
|
+
self.min_confidence_threshold = 0.65
|
|
97
|
+
self.max_drawdown_pct = 0.10
|
|
98
|
+
elif profile == "aggressive":
|
|
99
|
+
self.burst_multiplier = 3.0
|
|
100
|
+
self.compound_rate = 1.8
|
|
101
|
+
self.min_confidence_threshold = 0.50
|
|
102
|
+
self.max_drawdown_pct = 0.25
|
|
103
|
+
else: # balanced
|
|
104
|
+
self.burst_multiplier = 2.5
|
|
105
|
+
self.compound_rate = 1.5
|
|
106
|
+
self.min_confidence_threshold = 0.55
|
|
107
|
+
self.max_drawdown_pct = 0.15
|
|
108
|
+
|
|
109
|
+
def get_bet_size(self, prediction_data):
|
|
110
|
+
"""Calculate bet size based on Short Burst Reset Strategy"""
|
|
111
|
+
# Refresh base bet from config in case it changed
|
|
112
|
+
self.base_bet = self.config.get("base_bet")
|
|
113
|
+
|
|
114
|
+
if not prediction_data:
|
|
115
|
+
return self.base_bet
|
|
116
|
+
|
|
117
|
+
confidence = prediction_data['confidence']
|
|
118
|
+
model_health = prediction_data.get('model_health', 100.0)
|
|
119
|
+
micro_regime = prediction_data.get('micro_regime', 'NORMAL')
|
|
120
|
+
|
|
121
|
+
if self.circuit_breaker_active:
|
|
122
|
+
self.circuit_breaker_steps -= 1
|
|
123
|
+
if self.circuit_breaker_steps <= 0:
|
|
124
|
+
self.circuit_breaker_active = False
|
|
125
|
+
return self.base_bet
|
|
126
|
+
|
|
127
|
+
if self.cooldown_steps > 0:
|
|
128
|
+
self.cooldown_steps -= 1
|
|
129
|
+
return self.base_bet
|
|
130
|
+
|
|
131
|
+
if not self.burst_active:
|
|
132
|
+
threshold = self.min_confidence_threshold
|
|
133
|
+
if model_health < 70:
|
|
134
|
+
threshold += 0.1
|
|
135
|
+
if micro_regime == "CHAOS":
|
|
136
|
+
threshold += 0.15
|
|
137
|
+
|
|
138
|
+
if confidence > threshold or (random.random() < 0.10 and model_health > 80):
|
|
139
|
+
self.burst_active = True
|
|
140
|
+
self.burst_step = 1
|
|
141
|
+
bet = self.base_bet * self.burst_multiplier
|
|
142
|
+
else:
|
|
143
|
+
return self.base_bet
|
|
144
|
+
else:
|
|
145
|
+
if self.burst_step == 1:
|
|
146
|
+
bet = self.base_bet * self.burst_multiplier
|
|
147
|
+
else:
|
|
148
|
+
bet = self.base_bet * (self.burst_multiplier * (self.compound_rate ** (self.burst_step - 1)))
|
|
149
|
+
|
|
150
|
+
bet = min(bet, self.base_bet * 100.0)
|
|
151
|
+
|
|
152
|
+
jitter = bet * (random.uniform(-self.jitter_factor, self.jitter_factor))
|
|
153
|
+
final_bet = bet + jitter
|
|
154
|
+
final_bet = max(self.base_bet, final_bet)
|
|
155
|
+
|
|
156
|
+
if self.drawdown > self.max_drawdown_pct:
|
|
157
|
+
final_bet = self.base_bet
|
|
158
|
+
self.burst_active = False
|
|
159
|
+
|
|
160
|
+
return final_bet
|
|
161
|
+
|
|
162
|
+
def update(self, bet_size, won, profit):
|
|
163
|
+
"""Update state based on result"""
|
|
164
|
+
self.bankroll += profit
|
|
165
|
+
self.peak_bankroll = max(self.peak_bankroll, self.bankroll)
|
|
166
|
+
self.drawdown = (self.peak_bankroll - self.bankroll) / self.peak_bankroll
|
|
167
|
+
|
|
168
|
+
self.trade_history.append({'profit': profit, 'bet': bet_size})
|
|
169
|
+
self.short_term_memory.append(1 if won else 0)
|
|
170
|
+
|
|
171
|
+
if len(self.short_term_memory) >= 5:
|
|
172
|
+
recent_wins = sum(list(self.short_term_memory)[-5:])
|
|
173
|
+
if recent_wins <= 1:
|
|
174
|
+
self.circuit_breaker_active = True
|
|
175
|
+
self.circuit_breaker_steps = 10
|
|
176
|
+
self.burst_active = False
|
|
177
|
+
self.retrain_requested = True
|
|
178
|
+
|
|
179
|
+
if self.burst_active:
|
|
180
|
+
if won:
|
|
181
|
+
self.burst_step += 1
|
|
182
|
+
if self.burst_step > self.max_burst_length:
|
|
183
|
+
self.burst_active = False
|
|
184
|
+
self.cooldown_steps = random.randint(2, 5)
|
|
185
|
+
else:
|
|
186
|
+
self.burst_active = False
|
|
187
|
+
self.cooldown_steps = random.randint(1, 3)
|
|
188
|
+
|
|
189
|
+
if self.burst_active and random.random() < self.random_reset_prob:
|
|
190
|
+
self.burst_active = False
|
|
191
|
+
self.cooldown_steps = 1
|
|
192
|
+
|
|
193
|
+
wins = [t for t in self.trade_history if t['profit'] > 0]
|
|
194
|
+
self.win_rate = len(wins) / len(self.trade_history) if self.trade_history else 0
|
|
195
|
+
|
|
196
|
+
if len(self.trade_history) > 10:
|
|
197
|
+
profits = [t['profit'] for t in self.trade_history]
|
|
198
|
+
self.sharpe_ratio = np.mean(profits) / (np.std(profits) + 1e-9)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
import torch
|
|
5
|
+
import torch.nn as nn
|
|
6
|
+
import torch.optim as optim
|
|
7
|
+
from torch.utils.data import DataLoader, TensorDataset
|
|
8
|
+
HAS_TORCH = True
|
|
9
|
+
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
10
|
+
except ImportError:
|
|
11
|
+
HAS_TORCH = False
|
|
12
|
+
DEVICE = None
|
|
13
|
+
|
|
14
|
+
class TimeSeriesTransformer(nn.Module if HAS_TORCH else object):
|
|
15
|
+
"""
|
|
16
|
+
Advanced Multi-Head Attention Transformer for Deep Sequence Forecasting.
|
|
17
|
+
Designed to catch non-linear microscopic patterns in RNG outputs.
|
|
18
|
+
"""
|
|
19
|
+
def __init__(self, input_dim=1, d_model=64, nhead=4, num_layers=3, dropout=0.1):
|
|
20
|
+
if not HAS_TORCH:
|
|
21
|
+
raise ImportError("PyTorch is required for the Transformer module.")
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.input_projection = nn.Linear(input_dim, d_model)
|
|
24
|
+
|
|
25
|
+
# Positional Encoding
|
|
26
|
+
self.pos_encoder = PositionalEncoding(d_model, dropout)
|
|
27
|
+
|
|
28
|
+
encoder_layers = nn.TransformerEncoderLayer(d_model, nhead, d_model*4, dropout, batch_first=True)
|
|
29
|
+
self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers)
|
|
30
|
+
|
|
31
|
+
self.decoder = nn.Sequential(
|
|
32
|
+
nn.Linear(d_model, d_model // 2),
|
|
33
|
+
nn.ReLU(),
|
|
34
|
+
nn.Dropout(dropout),
|
|
35
|
+
nn.Linear(d_model // 2, 1)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def forward(self, src):
|
|
39
|
+
# src shape: [batch_size, sequence_length, input_dim]
|
|
40
|
+
src = self.input_projection(src)
|
|
41
|
+
src = self.pos_encoder(src)
|
|
42
|
+
output = self.transformer_encoder(src)
|
|
43
|
+
# Take the last sequence output to predict the next step
|
|
44
|
+
last_out = output[:, -1, :]
|
|
45
|
+
return self.decoder(last_out)
|
|
46
|
+
|
|
47
|
+
class PositionalEncoding(nn.Module if HAS_TORCH else object):
|
|
48
|
+
def __init__(self, d_model, dropout=0.1, max_len=5000):
|
|
49
|
+
if not HAS_TORCH: return
|
|
50
|
+
super().__init__()
|
|
51
|
+
self.dropout = nn.Dropout(p=dropout)
|
|
52
|
+
|
|
53
|
+
position = torch.arange(max_len).unsqueeze(1)
|
|
54
|
+
div_term = torch.exp(torch.arange(0, d_model, 2) * (-np.log(10000.0) / d_model))
|
|
55
|
+
pe = torch.zeros(max_len, 1, d_model)
|
|
56
|
+
pe[:, 0, 0::2] = torch.sin(position * div_term)
|
|
57
|
+
pe[:, 0, 1::2] = torch.cos(position * div_term)
|
|
58
|
+
self.register_buffer('pe', pe)
|
|
59
|
+
|
|
60
|
+
def forward(self, x):
|
|
61
|
+
"""
|
|
62
|
+
Arguments:
|
|
63
|
+
x: Tensor, shape ``[batch_size, seq_len, embedding_dim]``
|
|
64
|
+
"""
|
|
65
|
+
x = x + self.pe[:x.size(1)].transpose(0, 1)
|
|
66
|
+
return self.dropout(x)
|
|
67
|
+
|
|
68
|
+
class TransformerPredictor:
|
|
69
|
+
"""Wrapper to easily train and infer using the Transformer."""
|
|
70
|
+
def __init__(self, sequence_length=20, epochs=50, lr=0.001):
|
|
71
|
+
self.sequence_length = sequence_length
|
|
72
|
+
self.epochs = epochs
|
|
73
|
+
self.lr = lr
|
|
74
|
+
self.model = None
|
|
75
|
+
self.is_trained = False
|
|
76
|
+
|
|
77
|
+
if HAS_TORCH:
|
|
78
|
+
self.model = TimeSeriesTransformer(input_dim=1).to(DEVICE)
|
|
79
|
+
self.optimizer = optim.Adam(self.model.parameters(), lr=self.lr)
|
|
80
|
+
self.criterion = nn.MSELoss()
|
|
81
|
+
|
|
82
|
+
def _prepare_data(self, history):
|
|
83
|
+
if len(history) <= self.sequence_length:
|
|
84
|
+
return None, None
|
|
85
|
+
|
|
86
|
+
X, y = [], []
|
|
87
|
+
for i in range(len(history) - self.sequence_length):
|
|
88
|
+
raw_x = history[i:i+self.sequence_length]
|
|
89
|
+
raw_y = history[i+self.sequence_length]
|
|
90
|
+
|
|
91
|
+
flat_x = [float(np.ravel(val)[0]) for val in raw_x]
|
|
92
|
+
flat_y = float(np.ravel(raw_y)[0])
|
|
93
|
+
|
|
94
|
+
X.append(flat_x)
|
|
95
|
+
y.append(flat_y)
|
|
96
|
+
|
|
97
|
+
# Normalize to 0-1 for better gradients
|
|
98
|
+
X = np.array(X, dtype=np.float32) / 100.0
|
|
99
|
+
y = np.array(y, dtype=np.float32) / 100.0
|
|
100
|
+
|
|
101
|
+
X = torch.tensor(X).unsqueeze(-1).to(DEVICE) # [Batch, Seq, 1]
|
|
102
|
+
y = torch.tensor(y).unsqueeze(-1).to(DEVICE) # [Batch, 1]
|
|
103
|
+
return X, y
|
|
104
|
+
|
|
105
|
+
def train(self, history):
|
|
106
|
+
if not HAS_TORCH or len(history) < self.sequence_length * 2:
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
X, y = self._prepare_data(history)
|
|
110
|
+
if X is None: return False
|
|
111
|
+
|
|
112
|
+
dataset = TensorDataset(X, y)
|
|
113
|
+
loader = DataLoader(dataset, batch_size=32, shuffle=True)
|
|
114
|
+
|
|
115
|
+
self.model.train()
|
|
116
|
+
for epoch in range(self.epochs):
|
|
117
|
+
total_loss = 0
|
|
118
|
+
for batch_x, batch_y in loader:
|
|
119
|
+
self.optimizer.zero_grad()
|
|
120
|
+
output = self.model(batch_x)
|
|
121
|
+
loss = self.criterion(output, batch_y)
|
|
122
|
+
loss.backward()
|
|
123
|
+
self.optimizer.step()
|
|
124
|
+
total_loss += loss.item()
|
|
125
|
+
|
|
126
|
+
self.is_trained = True
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
def predict(self, recent_history):
|
|
130
|
+
if not self.is_trained or not HAS_TORCH:
|
|
131
|
+
return None
|
|
132
|
+
if len(recent_history) < self.sequence_length:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
# Take the most recent sequences and flatten them in case they are nested
|
|
136
|
+
raw_seq = recent_history[-self.sequence_length:]
|
|
137
|
+
flat_seq = [float(np.ravel(x)[0]) for x in raw_seq]
|
|
138
|
+
seq = np.array(flat_seq, dtype=np.float32) / 100.0
|
|
139
|
+
seq = torch.tensor(seq).unsqueeze(0).unsqueeze(-1).to(DEVICE) # [1, Seq, 1]
|
|
140
|
+
|
|
141
|
+
self.model.eval()
|
|
142
|
+
with torch.no_grad():
|
|
143
|
+
pred = self.model(seq)
|
|
144
|
+
|
|
145
|
+
# Denormalize
|
|
146
|
+
return float(pred.item() * 100.0)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from collections import deque
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from sklearn.ensemble import IsolationForest
|
|
6
|
+
HAS_IFOREST = True
|
|
7
|
+
except ImportError:
|
|
8
|
+
HAS_IFOREST = False
|
|
9
|
+
|
|
10
|
+
class RNGAnomalyDetector:
|
|
11
|
+
"""
|
|
12
|
+
Monitors the stream of RNG values to detect statistically bizarre
|
|
13
|
+
sequences that indicate the Casino might have altered the seed
|
|
14
|
+
or logic server-side.
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, window_size=50, contamination=0.05):
|
|
17
|
+
self.window_size = window_size
|
|
18
|
+
self.contamination = contamination
|
|
19
|
+
self.history = deque(maxlen=2000)
|
|
20
|
+
self.model = None
|
|
21
|
+
self.is_fitted = False
|
|
22
|
+
|
|
23
|
+
if HAS_IFOREST:
|
|
24
|
+
self.model = IsolationForest(
|
|
25
|
+
n_estimators=100,
|
|
26
|
+
contamination=self.contamination,
|
|
27
|
+
random_state=42
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def _extract_window_features(self, sequence):
|
|
31
|
+
"""Extracts spatial/temporal properties of a sequence window."""
|
|
32
|
+
seq = np.array(sequence)
|
|
33
|
+
return [
|
|
34
|
+
np.mean(seq),
|
|
35
|
+
np.std(seq),
|
|
36
|
+
np.max(seq) - np.min(seq),
|
|
37
|
+
np.mean(np.abs(np.diff(seq))), # Average jump size
|
|
38
|
+
np.sum(seq > 50) / len(seq), # Ratio over 50
|
|
39
|
+
np.median(seq)
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
def add(self, roll):
|
|
43
|
+
self.history.append(roll)
|
|
44
|
+
|
|
45
|
+
# Fit the anomaly baseline when we have enough data
|
|
46
|
+
if HAS_IFOREST and not self.is_fitted and len(self.history) >= 500:
|
|
47
|
+
self.fit_baseline()
|
|
48
|
+
|
|
49
|
+
def fit_baseline(self):
|
|
50
|
+
"""Trains what a 'Normal' RNG sequence looks like for this seed."""
|
|
51
|
+
if not HAS_IFOREST or len(self.history) < self.window_size * 2:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
data = list(self.history)
|
|
55
|
+
features = []
|
|
56
|
+
for i in range(len(data) - self.window_size):
|
|
57
|
+
window = data[i:i+self.window_size]
|
|
58
|
+
features.append(self._extract_window_features(window))
|
|
59
|
+
|
|
60
|
+
if len(features) > 100:
|
|
61
|
+
self.model.fit(features)
|
|
62
|
+
self.is_fitted = True
|
|
63
|
+
|
|
64
|
+
def check_anomaly(self):
|
|
65
|
+
"""
|
|
66
|
+
Checks if the most recent window is anomalous compared to the baseline.
|
|
67
|
+
Returns: ExceptionScore (-1 for Anomaly, 1 for Normal), and Raw Distance.
|
|
68
|
+
"""
|
|
69
|
+
if not self.is_fitted or not HAS_IFOREST:
|
|
70
|
+
return 1, 0.0
|
|
71
|
+
|
|
72
|
+
if len(self.history) < self.window_size:
|
|
73
|
+
return 1, 0.0
|
|
74
|
+
|
|
75
|
+
recent_window = list(self.history)[-self.window_size:]
|
|
76
|
+
feat = np.array([self._extract_window_features(recent_window)])
|
|
77
|
+
|
|
78
|
+
prediction = self.model.predict(feat)[0]
|
|
79
|
+
score = self.model.decision_function(feat)[0]
|
|
80
|
+
|
|
81
|
+
return prediction, score
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
class ProphetBot:
|
|
2
|
+
"""A standard bot framework with typical dice bot functions."""
|
|
3
|
+
def __init__(self, initial_balance=1000.0):
|
|
4
|
+
self._balance = initial_balance
|
|
5
|
+
self._wager = 0.0
|
|
6
|
+
self._condition = "<"
|
|
7
|
+
self._target = 50.0
|
|
8
|
+
self._win = False
|
|
9
|
+
self._current_streak = 0
|
|
10
|
+
self._last_result = 0.0
|
|
11
|
+
self._profit = 0.0
|
|
12
|
+
|
|
13
|
+
def getbalance(self):
|
|
14
|
+
"""Returns the current balance."""
|
|
15
|
+
return self._balance
|
|
16
|
+
|
|
17
|
+
def setwager(self, amount):
|
|
18
|
+
"""Sets the current wager."""
|
|
19
|
+
self._wager = amount
|
|
20
|
+
|
|
21
|
+
def setcondition(self, condition):
|
|
22
|
+
"""Sets the win condition ('<' or '>')."""
|
|
23
|
+
self._condition = condition
|
|
24
|
+
|
|
25
|
+
def settarget(self, target):
|
|
26
|
+
"""Sets the target outcome (multiplier or chance)."""
|
|
27
|
+
self._target = target
|
|
28
|
+
|
|
29
|
+
def getresult(self):
|
|
30
|
+
"""Returns the result of the last roll."""
|
|
31
|
+
return self._last_result
|
|
32
|
+
|
|
33
|
+
def simulate_roll(self, result):
|
|
34
|
+
"""Simulates an incoming roll result from the casino."""
|
|
35
|
+
self._last_result = result
|
|
36
|
+
if self._condition == "<":
|
|
37
|
+
self._win = result < self._target
|
|
38
|
+
elif self._condition == ">":
|
|
39
|
+
self._win = result > self._target
|
|
40
|
+
|
|
41
|
+
if self._win:
|
|
42
|
+
# Simple assumption: 99% RTP
|
|
43
|
+
multiplier = 99.0 / self._target if self._target > 0 else 2.0
|
|
44
|
+
payout = self._wager * multiplier
|
|
45
|
+
self._profit += (payout - self._wager)
|
|
46
|
+
self._balance += (payout - self._wager)
|
|
47
|
+
self._current_streak = self._current_streak + 1 if self._current_streak > 0 else 1
|
|
48
|
+
else:
|
|
49
|
+
self._profit -= self._wager
|
|
50
|
+
self._balance -= self._wager
|
|
51
|
+
self._current_streak = self._current_streak - 1 if self._current_streak < 0 else -1
|
|
52
|
+
|
|
53
|
+
return self._win
|
|
54
|
+
|
|
55
|
+
def getprofit(self):
|
|
56
|
+
return self._profit
|
|
57
|
+
|
|
58
|
+
def is_win(self):
|
|
59
|
+
return self._win
|
|
60
|
+
|
|
61
|
+
def getstreak(self):
|
|
62
|
+
return self._current_streak
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import collections
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
import seaborn as sns
|
|
7
|
+
from IPython.display import clear_output
|
|
8
|
+
HAS_PLOTTING = True
|
|
9
|
+
except ImportError:
|
|
10
|
+
HAS_PLOTTING = False
|
|
11
|
+
|
|
12
|
+
class RealTimePlotter:
|
|
13
|
+
"""Provides real-time matplotlib/seaborn charts tracking predictions, accuracy, and bankroll."""
|
|
14
|
+
def __init__(self, max_points=200):
|
|
15
|
+
self.max_points = max_points
|
|
16
|
+
self.rolls = collections.deque(maxlen=max_points)
|
|
17
|
+
self.predictions = collections.deque(maxlen=max_points)
|
|
18
|
+
self.accuracy = collections.deque(maxlen=max_points)
|
|
19
|
+
self.bankroll = collections.deque(maxlen=max_points)
|
|
20
|
+
self.win_loss = collections.deque(maxlen=max_points)
|
|
21
|
+
|
|
22
|
+
if HAS_PLOTTING:
|
|
23
|
+
sns.set_theme(style="darkgrid")
|
|
24
|
+
plt.ion() # Interactive mode
|
|
25
|
+
self.fig, self.axs = plt.subplots(3, 1, figsize=(10, 12))
|
|
26
|
+
self.fig.canvas.manager.set_window_title('Prophet ML Auditor Dashboard')
|
|
27
|
+
plt.tight_layout(pad=3.0)
|
|
28
|
+
|
|
29
|
+
def update(self, roll_result, prediction, current_accuracy, current_bankroll, won):
|
|
30
|
+
"""Update tracker with new data from the latest roll step."""
|
|
31
|
+
self.rolls.append(roll_result)
|
|
32
|
+
self.predictions.append(prediction)
|
|
33
|
+
self.accuracy.append(current_accuracy)
|
|
34
|
+
self.bankroll.append(current_bankroll)
|
|
35
|
+
self.win_loss.append(1 if won else 0)
|
|
36
|
+
|
|
37
|
+
if HAS_PLOTTING and len(self.rolls) > 5:
|
|
38
|
+
self._draw()
|
|
39
|
+
|
|
40
|
+
def _draw(self):
|
|
41
|
+
"""Redraws the matplot canvas."""
|
|
42
|
+
for ax in self.axs:
|
|
43
|
+
ax.clear()
|
|
44
|
+
|
|
45
|
+
x_data = range(len(self.rolls))
|
|
46
|
+
|
|
47
|
+
# Plot 1: Rolls vs Predictions
|
|
48
|
+
self.axs[0].plot(x_data, self.rolls, label="Actual Roll", color="blue", alpha=0.6)
|
|
49
|
+
pred_array = np.array(self.predictions)
|
|
50
|
+
|
|
51
|
+
# Color correct predictions green, incorrect red
|
|
52
|
+
colors = ['green' if w else 'red' for w in self.win_loss]
|
|
53
|
+
self.axs[0].scatter(x_data, pred_array, c=colors, label="Prediction (G=Win, R=Loss)", s=15, zorder=5)
|
|
54
|
+
self.axs[0].axhline(y=50, color='gray', linestyle='--', alpha=0.5)
|
|
55
|
+
self.axs[0].set_title("Actual Rolls vs Predictions")
|
|
56
|
+
self.axs[0].set_ylabel("Outcome")
|
|
57
|
+
self.axs[0].legend(loc="upper left")
|
|
58
|
+
|
|
59
|
+
# Plot 2: Model Accuracy (Moving Average)
|
|
60
|
+
self.axs[1].plot(x_data, [a * 100 for a in self.accuracy], label="Overall Accuracy %", color="purple")
|
|
61
|
+
self.axs[1].axhline(y=50, color='red', linestyle='--', alpha=0.5, label="Baseline (50%)")
|
|
62
|
+
self.axs[1].set_title("Prediction Accuracy")
|
|
63
|
+
self.axs[1].set_ylabel("Accuracy (%)")
|
|
64
|
+
self.axs[1].set_ylim([0, 100])
|
|
65
|
+
self.axs[1].legend(loc="upper left")
|
|
66
|
+
|
|
67
|
+
# Plot 3: Bankroll
|
|
68
|
+
self.axs[2].plot(x_data, self.bankroll, label="Bankroll", color="gold")
|
|
69
|
+
self.axs[2].set_title("Bankroll Evolution")
|
|
70
|
+
self.axs[2].set_ylabel("Balance")
|
|
71
|
+
self.axs[2].set_xlabel("Roll Number")
|
|
72
|
+
self.axs[2].legend(loc="upper left")
|
|
73
|
+
|
|
74
|
+
plt.draw()
|
|
75
|
+
plt.pause(0.01)
|
|
76
|
+
|
|
77
|
+
def show_final(self):
|
|
78
|
+
"""Blocking call to show final charts."""
|
|
79
|
+
if HAS_PLOTTING:
|
|
80
|
+
plt.ioff()
|
|
81
|
+
plt.show()
|