Перейти к публикации

yellyex

User
  • Сообщений

    9 758
  • Зарегистрирован

  • Посещение

  • Дней в лидерах

    2
  • Время онлайн

    56д 9ч 44м 10с

6 подписчиков

О yellyex

  • Звание
    Отдыхающий
  • День рождения 22.11.1995

Profile Information

  • Gender
    Male

Посетители профиля

27 692 просмотра профиля
  1. 12 обезъян. На 3 из 5. Такое себе, разок посмотреть можно.
  2. yellyex

    Fighting Games Compilation

    Поздравляю ULSAN'а!
  3. yellyex

    Fighting Games Compilation

    LOWHIGH vs ULSAN
  4. yellyex

    Fighting Games Compilation

    Сегодня Гранд финал будет https://liquipedia.net/fighters/Esports_World_Cup/2025/T8 Начинается битва за третье место! А нет, это за выход в гранд финал!
  5. Конец надо тебе в рот положить что я сделал не так Мне показалось, что ты нацист, который ненавидит Украинцев.
  6. Конец надо тебе в рот положить
  7. yellyex

    Программирование[11]

    Так он мне впаривает, что всё нормально с его точки зрения. Ошибку он не видит. Даже мне впарил, что 90.09% это не винрейт, а лузрейт. Пришлось ему самому объяснить, что он ошибся в рассчётах. Сейчас попробую нейроннку запилить, чтобы он нашёл прибыльную страту.
  8. yellyex

    Программирование[11]

    - Выигрыш только если множитель меньше фактического краша (т.е. **строго больше**), иначе честно говорить: на границе — проигрыш. По правилам игры победа присуждается при: Число Краша >= Множитель Обновил код. Добавил округление не до 2 знаков, а до 5 или 10. В настройках можно указать. Тогда итоговый профит уже начинает скакать! Crash import sys import random import math import os from datetime import datetime from collections import deque from typing import List, Tuple, Dict, Any from PyQt6 import QtWidgets, QtCore, QtGui from PyQt6.QtCore import QThread, pyqtSignal, QTimer, QDateTime, Qt, QTime from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel, QPushButton, QMessageBox, QGroupBox, QSpinBox, QDoubleSpinBox, QCheckBox, QTableWidget, QTableWidgetItem, QHeaderView, QTabWidget, QTimeEdit, QFrame, QScrollArea, QSizePolicy ) import matplotlib matplotlib.use('QtAgg') from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure # --- APP_CONFIG --- APP_CONFIG = { 'APP_NAME': "Crash", 'APP_NAME_VERSION': "(v1.0)", 'STATS_FILE_NAME': "stats.txt", 'COLOR_BACKGROUND': "#1e1e2d", 'COLOR_SURFACE': "#2d2d44", 'COLOR_SURFACE_LIGHT': "#3d3d54", 'COLOR_TEXT': "#e0e0ff", 'COLOR_BORDER': "#444466", 'COLOR_PRIMARY': "#bb86fc", 'COLOR_SECONDARY': "#03dac6", 'COLOR_POSITIVE': "#00cc66", 'COLOR_NEGATIVE': "#ff5555", 'COLOR_WARNING': "#ffaa00", 'COLOR_INFO': "#66b3ff", 'TEXT_LABELS': { 'app_title': "Crash", 'save_settings': "Сохранить настройки", 'reset_settings': "Сбросить настройки", 'min_bet_label': "Минимальная ставка:", 'max_bet_label': "Максимальная ставка:", }, } # --- Crash Number Generator --- def generate_crash_number(precision: int = 2): """Генерирует случайное число краша""" # Генерируем равномерно распределённое число U ∈ [0, 1) U = random.uniform(0, 1) # Вычисляем множитель по формуле if U >= 0.99999: # Защита от деления на ноль crash_multiplier = 100000.00 else: crash_multiplier = 1.0 / (1.0 - U) # Ограничиваем множитель диапазоном [1.00, 100000.00] и округляем до заданной точности crash_multiplier = max(min(crash_multiplier, 100000.00), 1.00) return round(crash_multiplier, precision) # --- Crash History Table (для отображения последних 100 значений) --- class CrashHistoryTable(QWidget): def __init__(self, parent=None): super().__init__(parent) self.history = [] self.precision = 2 # Default precision self.init_ui() def init_ui(self): self.layout = QVBoxLayout() self.setLayout(self.layout) # Заголовок header_layout = QHBoxLayout() header_label = QLabel("История последних значений краша") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 12pt; font-weight: bold;") header_label.setToolTip("Последние 100 значений коэффициента краша") header_layout.addWidget(header_label) header_layout.addStretch() self.layout.addLayout(header_layout) # Таблица self.table = QTableWidget() self.table.setColumnCount(2) self.table.setHorizontalHeaderLabels(["#", "Краш"]) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) self.table.verticalHeader().setVisible(False) self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.table.setSelectionMode(QTableWidget.SelectionMode.NoSelection) self.table.setStyleSheet(f""" QTableWidget {{ font-size: 9pt; color: {APP_CONFIG['COLOR_TEXT']}; background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; gridline-color: {APP_CONFIG['COLOR_BORDER']}; alternate-background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; }} QTableWidget::item {{ padding: 4px; border-bottom: 1px solid {APP_CONFIG['COLOR_BORDER']}; }} QHeaderView::section {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 6px; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; font-weight: bold; }} QTableWidget::item:selected {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; color: white; }} """) self.table.setToolTip("Таблица с последними 100 значениями краша") self.layout.addWidget(self.table) def set_precision(self, precision: int): """Установить точность округления""" self.precision = precision # Перерисовать таблицу с новой точностью, если она уже заполнена if self.history: self.update_history(self.history) def update_history(self, crash_history: List[float]): """Обновляет таблицу всей историей крашей""" self.history = crash_history[-100:] if len(crash_history) > 100 else crash_history self.table.setRowCount(len(self.history)) for i, value in enumerate(reversed(self.history)): # Номер num_item = QTableWidgetItem(str(len(self.history) - i)) num_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) self.table.setItem(i, 0, num_item) # Значение краша crash_item = QTableWidgetItem(f"{value:.{self.precision}f}x") crash_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # Цвет в зависимости от значения if value >= 10: crash_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_POSITIVE'])) crash_item.setFont(QtGui.QFont("Arial", 9, QtGui.QFont.Weight.Bold)) elif value >= 2: crash_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_SECONDARY'])) else: crash_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_NEGATIVE'])) self.table.setItem(i, 1, crash_item) # Выравнивание текста по центру for i in range(self.table.rowCount()): for j in range(self.table.columnCount()): item = self.table.item(i, j) if item: item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # --- Actions History Table (для отображения последних 100 действий) --- class ActionsHistoryTable(QWidget): def __init__(self, parent=None): super().__init__(parent) self.history = [] self.precision = 2 # Default precision self.init_ui() def init_ui(self): self.layout = QVBoxLayout() self.setLayout(self.layout) # Заголовок header_layout = QHBoxLayout() header_label = QLabel("История последних действий") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 12pt; font-weight: bold;") header_label.setToolTip("Последние 100 записей о действиях в тестировании") header_layout.addWidget(header_label) header_layout.addStretch() self.layout.addLayout(header_layout) # Таблица self.table = QTableWidget() self.table.setColumnCount(10) self.table.setHorizontalHeaderLabels([ "Дата/Время", "Раунд", "Банк", "Ставка", "Множитель", "Краш", "Результат", "Новый банк", "Профит", "ROI" ]) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) self.table.verticalHeader().setVisible(False) self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.table.setSelectionMode(QTableWidget.SelectionMode.NoSelection) self.table.setStyleSheet(f""" QTableWidget {{ font-size: 8pt; color: {APP_CONFIG['COLOR_TEXT']}; background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; gridline-color: {APP_CONFIG['COLOR_BORDER']}; alternate-background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; }} QTableWidget::item {{ padding: 2px; border-bottom: 1px solid {APP_CONFIG['COLOR_BORDER']}; }} QHeaderView::section {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 4px; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; font-weight: bold; }} QTableWidget::item:selected {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; color: white; }} """) self.table.setToolTip("Таблица с последними 100 записями о действиях в тестировании") self.layout.addWidget(self.table) def set_precision(self, precision: int): """Установить точность округления""" self.precision = precision # Перерисовать таблицу с новой точностью, если она уже заполнена if self.history: self.update_history(self.history) def update_history(self, actions_history: List[Dict[str, Any]]): """Обновляет таблицу всей историей действий""" self.history = actions_history[-100:] if len(actions_history) > 100 else actions_history self.table.setRowCount(len(self.history)) format_str = f".{self.precision}f" for i, data in enumerate(reversed(self.history)): timestamp = data['timestamp'].strftime("%H:%M:%S.%f")[:-3] # Обрезаем до миллисекунд self.table.setItem(i, 0, QTableWidgetItem(timestamp)) self.table.setItem(i, 1, QTableWidgetItem(str(data['round_num']))) self.table.setItem(i, 2, QTableWidgetItem(f"{data['balance_before']:{format_str}}")) self.table.setItem(i, 3, QTableWidgetItem(str(data['bet']))) self.table.setItem(i, 4, QTableWidgetItem(f"{data['multiplier']:{format_str}}")) self.table.setItem(i, 5, QTableWidgetItem(f"{data['crash_value']:{format_str}}")) # Результат if data['win']: result_text = f"+{data['reward']:{format_str}}" result_item = QTableWidgetItem(result_text) result_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_POSITIVE'])) result_item.setFont(QtGui.QFont("Arial", 8, QtGui.QFont.Weight.Bold)) else: result_text = f"-{data['bet']:{format_str}}" result_item = QTableWidgetItem(result_text) result_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_NEGATIVE'])) result_item.setFont(QtGui.QFont("Arial", 8, QtGui.QFont.Weight.Bold)) self.table.setItem(i, 6, result_item) self.table.setItem(i, 7, QTableWidgetItem(f"{data['balance_after']:{format_str}}")) self.table.setItem(i, 8, QTableWidgetItem(f"{data['profit']:{format_str}}")) self.table.setItem(i, 9, QTableWidgetItem(f"{data['roi']:{format_str}}%")) # Выравнивание текста по центру for i in range(self.table.rowCount()): for j in range(self.table.columnCount()): item = self.table.item(i, j) if item: item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # --- Testing Thread --- class TestingWorker(QThread): # Сигналы для обновления GUI batch_finished = pyqtSignal(dict) # Передаем данные батча testing_finished = pyqtSignal(dict) # итоговая статистика testing_stopped = pyqtSignal() error_occurred = pyqtSignal(str) plot_data_ready = pyqtSignal(list, list) # данные для графика def __init__(self, num_rounds: int, min_bet: int, max_bet: int, min_multiplier: float, max_multiplier: float, initial_balance: float, update_interval: int, precision: int = 2): super().__init__() self.num_rounds = num_rounds self.min_bet = min_bet self.max_bet = max_bet self.min_multiplier = min_multiplier self.max_multiplier = max_multiplier self.initial_balance = initial_balance self.update_interval = update_interval # 0 = только в конце self.precision = precision # Новое поле для точности self._is_running = True self.balance = initial_balance self.crash_history = [] self.actions_history = [] self.testing_rounds_data = [] self.testing_profit_data = [] self.total_wins = 0 self.total_losses = 0 self.current_win_streak = 0 self.current_loss_streak = 0 self.max_win_streak = 0 self.max_loss_streak = 0 self.total_invested = 0 # Для правильного расчета ROI self.processed_rounds = 0 def stop(self): self._is_running = False def run(self): try: batch_data = { 'rounds': [], 'crash_values': [], 'bets': [], 'multipliers': [], 'wins': [], 'rewards': [], 'balances_before': [], 'balances_after': [], 'profits': [], 'rois': [], 'winrates': [], 'timestamps': [], 'total_wins_list': [], 'total_losses_list': [], 'max_win_streaks': [], 'max_loss_streaks': [] } for i in range(1, self.num_rounds + 1): if not self._is_running: self.testing_stopped.emit() return # 1. Генерация краша с заданной точностью crash_value = generate_crash_number(self.precision) self.crash_history.append(crash_value) # 2. Генерация случайной ставки и множителя с заданной точностью bet = random.randint(self.min_bet, self.max_bet) multiplier = round(random.uniform(self.min_multiplier, self.max_multiplier), self.precision) # 3. Симуляция результата balance_before = self.balance win = crash_value >= multiplier reward = 0 if bet > 0: self.total_invested += bet # Увеличиваем общую сумму ставок self.balance -= bet if win: win_amount = bet * multiplier self.balance += win_amount reward = win_amount - bet # Чистый выигрыш self.total_wins += 1 self.current_win_streak += 1 self.current_loss_streak = 0 if self.current_win_streak > self.max_win_streak: self.max_win_streak = self.current_win_streak else: reward = -bet # Потеря ставки self.total_losses += 1 self.current_loss_streak += 1 self.current_win_streak = 0 if self.current_loss_streak > self.max_loss_streak: self.max_loss_streak = self.current_loss_streak profit = self.balance - self.initial_balance # Исправленный расчет ROI roi = (profit / self.total_invested * 100) if self.total_invested > 0 else 0 winrate = (self.total_wins / i * 100) if i > 0 else 0 # Winrate от общего количества раундов # 4. Сохранение данных для истории действий action_data = { 'round_num': i, 'crash_value': crash_value, 'bet': bet, 'multiplier': multiplier, 'win': win, 'reward': reward, 'balance_before': balance_before, 'balance_after': self.balance, 'profit': profit, 'roi': roi, 'winrate': winrate, 'timestamp': datetime.now(), 'total_wins': self.total_wins, 'total_losses': self.total_losses, 'max_win_streak': self.max_win_streak, 'max_loss_streak': self.max_loss_streak } self.actions_history.append(action_data) # 5. Обновление данных для графика self.testing_rounds_data.append(i) self.testing_profit_data.append(profit) # 6. Подготовка данных для батча (если нужно обновление) if self.update_interval > 0: batch_data['rounds'].append(i) batch_data['crash_values'].append(crash_value) batch_data['bets'].append(bet) batch_data['multipliers'].append(multiplier) batch_data['wins'].append(win) batch_data['rewards'].append(reward) batch_data['balances_before'].append(balance_before) batch_data['balances_after'].append(self.balance) batch_data['profits'].append(profit) batch_data['rois'].append(roi) batch_data['winrates'].append(winrate) batch_data['timestamps'].append(datetime.now()) batch_data['total_wins_list'].append(self.total_wins) batch_data['total_losses_list'].append(self.total_losses) batch_data['max_win_streaks'].append(self.max_win_streak) batch_data['max_loss_streaks'].append(self.max_loss_streak) self.processed_rounds += 1 # 7. Эмит сигналов при достижении интервала обновления if self.update_interval > 0 and self.processed_rounds % self.update_interval == 0: batch_stats = { 'processed_rounds': self.processed_rounds, 'total_rounds': self.num_rounds, 'current_balance': self.balance, 'current_profit': profit, 'current_roi': roi, 'current_winrate': winrate, 'total_wins': self.total_wins, 'total_losses': self.total_losses, 'max_win_streak': self.max_win_streak, 'max_loss_streak': self.max_loss_streak, 'crash_history': self.crash_history.copy(), 'actions_history': self.actions_history.copy(), 'plot_rounds': self.testing_rounds_data.copy(), 'plot_profits': self.testing_profit_data.copy() } self.batch_finished.emit(batch_stats) # Очищаем батч данные batch_data = { 'rounds': [], 'crash_values': [], 'bets': [], 'multipliers': [], 'wins': [], 'rewards': [], 'balances_before': [], 'balances_after': [], 'profits': [], 'rois': [], 'winrates': [], 'timestamps': [], 'total_wins_list': [], 'total_losses_list': [], 'max_win_streaks': [], 'max_loss_streaks': [] } # Небольшая задержка только для очень маленьких интервалов if self.update_interval > 0 and self.update_interval < 10: self.msleep(1) # Тестирование завершено final_stats = { 'total_rounds': self.num_rounds, 'final_balance': self.balance, 'total_profit': self.balance - self.initial_balance, 'roi': ((self.balance - self.initial_balance) / self.total_invested * 100) if self.total_invested > 0 else 0, 'winrate': (self.total_wins / self.num_rounds * 100) if self.num_rounds > 0 else 0, 'total_wins': self.total_wins, 'total_losses': self.total_losses, 'max_win_streak': self.max_win_streak, 'max_loss_streak': self.max_loss_streak, 'crash_history': self.crash_history, 'actions_history': self.actions_history, 'plot_rounds': self.testing_rounds_data, 'plot_profits': self.testing_profit_data } self.testing_finished.emit(final_stats) except Exception as e: self.error_occurred.emit(str(e)) # --- Main Window --- class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(f"{APP_CONFIG['APP_NAME']} {APP_CONFIG['APP_NAME_VERSION']}") self.setGeometry(100, 100, 1400, 900) self.setStyleSheet(f""" QMainWindow {{ background-color: {APP_CONFIG['COLOR_BACKGROUND']}; }} QWidget {{ background-color: {APP_CONFIG['COLOR_BACKGROUND']}; color: {APP_CONFIG['COLOR_TEXT']}; font-family: 'Segoe UI', Arial, sans-serif; }} QLabel {{ color: {APP_CONFIG['COLOR_TEXT']}; background-color: transparent; }} QGroupBox {{ border: 2px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 8px; margin-top: 10px; padding: 10px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; background-color: {APP_CONFIG['COLOR_SURFACE']}; }} QGroupBox::title {{ subline-offset: -15px; padding: 0px 5px 0px 5px; color: {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 8px 15px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 100px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton:pressed {{ background-color: {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton:disabled {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: #888888; border: 1px solid #555555; }} QSpinBox, QDoubleSpinBox {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; color: {APP_CONFIG['COLOR_TEXT']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 4px; padding: 4px; selection-background-color: {APP_CONFIG['COLOR_PRIMARY']}; }} QSpinBox::up-button, QDoubleSpinBox::up-button {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; border: none; width: 16px; }} QSpinBox::down-button, QDoubleSpinBox::down-button {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; border: none; width: 16px; }} QCheckBox {{ color: {APP_CONFIG['COLOR_TEXT']}; }} QTabWidget::pane {{ border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; background-color: {APP_CONFIG['COLOR_BACKGROUND']}; }} QTabBar::tab {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 8px 15px; margin-right: 2px; border-top-left-radius: 4px; border-top-right-radius: 4px; }} QTabBar::tab:selected {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_SECONDARY']}; font-weight: bold; }} QTabBar::tab:hover {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; }} QHeaderView::section {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 6px; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; font-weight: bold; }} """) # Инициализация UI self.init_ui() def init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) self.main_layout = QVBoxLayout(central_widget) self.main_layout.setSpacing(10) self.setup_main_tabs() def setup_main_tabs(self): self.tabs = QTabWidget() self.tab_main = self.create_main_tab_interface() self.tab_settings = self.create_settings_tab_interface() self.tab_testing = self.create_testing_tab_interface() self.tab_history = self.create_history_tables_tab_interface() # Новая вкладка self.tabs.addTab(self.tab_main, "Главная") self.tabs.addTab(self.tab_settings, "Настройки") self.tabs.addTab(self.tab_testing, "Тестирование") self.tabs.addTab(self.tab_history, "Таблицы (История)") # Добавляем новую вкладку self.main_layout.addWidget(self.tabs) def create_main_tab_interface(self): tab = QWidget() layout = QVBoxLayout() layout.setSpacing(15) tab.setLayout(layout) # Вкладка теперь полностью пустая, как требовалось layout.addStretch() return tab def create_settings_tab_interface(self): tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Заголовок header_label = QLabel("Настройки тестирования") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 16pt; font-weight: bold; padding: 10px;") header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) header_label.setToolTip("Настройка параметров для симуляции игры Crash") layout.addWidget(header_label) # Основные настройки main_settings_group = QGroupBox("Основные параметры") main_settings_layout = QFormLayout() main_settings_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight) main_settings_layout.setHorizontalSpacing(20) main_settings_layout.setVerticalSpacing(10) main_settings_group.setToolTip("Основные параметры симуляции") # Начальный банк self.initial_balance_input = QSpinBox() self.initial_balance_input.setRange(0, 10000000) self.initial_balance_input.setValue(0) self.initial_balance_input.setGroupSeparatorShown(True) self.initial_balance_input.setToolTip("Начальный баланс для тестирования") main_settings_layout.addRow(QLabel("Начальный банк:"), self.initial_balance_input) # Минимальная ставка self.min_bet_input = QSpinBox() self.min_bet_input.setRange(0, 10000000) self.min_bet_input.setValue(100) self.min_bet_input.setGroupSeparatorShown(True) self.min_bet_input.setToolTip("Минимальный размер ставки для одной игры") main_settings_layout.addRow(QLabel(APP_CONFIG['TEXT_LABELS']['min_bet_label']), self.min_bet_input) # Максимальная ставка self.max_bet_input = QSpinBox() self.max_bet_input.setRange(0, 10000000) self.max_bet_input.setValue(100) self.max_bet_input.setGroupSeparatorShown(True) self.max_bet_input.setToolTip("Максимальный размер ставки для одной игры") main_settings_layout.addRow(QLabel("Максимальная ставка:"), self.max_bet_input) # Минимальный множитель self.min_multiplier_input = QDoubleSpinBox() self.min_multiplier_input.setRange(1.01, 100.0) self.min_multiplier_input.setSingleStep(0.1) self.min_multiplier_input.setDecimals(2) self.min_multiplier_input.setValue(1.11) self.min_multiplier_input.setToolTip("Минимальный множитель для кэшаута") main_settings_layout.addRow(QLabel("Мин множитель:"), self.min_multiplier_input) # Максимальный множитель self.max_multiplier_input = QDoubleSpinBox() self.max_multiplier_input.setRange(1.01, 100.0) self.max_multiplier_input.setSingleStep(0.1) self.max_multiplier_input.setDecimals(2) self.max_multiplier_input.setValue(1.11) self.max_multiplier_input.setToolTip("Максимальный множитель для кэшаута") main_settings_layout.addRow(QLabel("Макс множитель:"), self.max_multiplier_input) # --- НОВАЯ НАСТРОЙКА ТОЧНОСТИ --- # Точность округления self.precision_input = QSpinBox() self.precision_input.setRange(0, 10) self.precision_input.setValue(2) self.precision_input.setToolTip("Количество знаков после запятой для отображения чисел") main_settings_layout.addRow(QLabel("Точность округления:"), self.precision_input) # --- КОНЕЦ НОВОЙ НАСТРОЙКИ --- main_settings_group.setLayout(main_settings_layout) layout.addWidget(main_settings_group) # Кнопки действий buttons_layout = QHBoxLayout() buttons_layout.addStretch() self.save_settings_button = QPushButton(APP_CONFIG['TEXT_LABELS']['save_settings']) self.save_settings_button.clicked.connect(self.save_settings) self.save_settings_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 150px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_POSITIVE']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} """) self.save_settings_button.setToolTip("Сохранить текущие настройки") buttons_layout.addWidget(self.save_settings_button) self.reset_settings_button = QPushButton(APP_CONFIG['TEXT_LABELS']['reset_settings']) self.reset_settings_button.clicked.connect(self.restore_default_settings) self.reset_settings_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 150px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_WARNING']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} """) self.reset_settings_button.setToolTip("Сбросить все настройки к значениям по умолчанию") buttons_layout.addWidget(self.reset_settings_button) buttons_layout.addStretch() layout.addLayout(buttons_layout) layout.addStretch() return tab def create_testing_tab_interface(self): tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Заголовок header_label = QLabel("Тестирование стратегии") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 16pt; font-weight: bold; padding: 10px;") header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) header_label.setToolTip("Запуск и управление симуляцией игры Crash") layout.addWidget(header_label) # Панель управления тестированием control_group = QGroupBox("Управление тестированием") control_layout = QFormLayout() control_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight) control_layout.setHorizontalSpacing(20) control_layout.setVerticalSpacing(10) control_group.setToolTip("Параметры и управление процессом тестирования") self.num_rounds_input = QSpinBox() self.num_rounds_input.setRange(1, 100000000) self.num_rounds_input.setValue(1000000) self.num_rounds_input.setGroupSeparatorShown(True) self.num_rounds_input.setToolTip("Общее количество раундов для симуляции") control_layout.addRow(QLabel("Количество раундов:"), self.num_rounds_input) self.update_interval_input = QSpinBox() self.update_interval_input.setRange(0, 100000000) self.update_interval_input.setValue(0) self.update_interval_input.setGroupSeparatorShown(True) self.update_interval_input.setToolTip("Интервал обновления интерфейса в раундах (0 = только в конце)") control_layout.addRow(QLabel("Интервал обновления интерфейса:"), self.update_interval_input) # Кнопки buttons_layout = QHBoxLayout() buttons_layout.addStretch() self.start_testing_button = QPushButton("Начать тестирование") self.start_testing_button.clicked.connect(self.start_testing) self.start_testing_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 180px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_POSITIVE']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} """) self.start_testing_button.setToolTip("Запустить симуляцию игры с текущими настройками") buttons_layout.addWidget(self.start_testing_button) self.stop_testing_button = QPushButton("Завершить тестирование") self.stop_testing_button.clicked.connect(self.stop_testing) self.stop_testing_button.setEnabled(False) self.stop_testing_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 180px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_NEGATIVE']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton:disabled {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: #888888; border: 1px solid #555555; }} """) self.stop_testing_button.setToolTip("Остановить текущую симуляцию") buttons_layout.addWidget(self.stop_testing_button) buttons_layout.addStretch() control_layout.addRow(buttons_layout) control_group.setLayout(control_layout) layout.addWidget(control_group) # Панель статистики тестирования self.testing_stats_group = QGroupBox("Статистика тестирования") stats_layout = QFormLayout() stats_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight) stats_layout.setHorizontalSpacing(20) stats_layout.setVerticalSpacing(8) self.testing_stats_group.setToolTip("Текущие результаты симуляции") # Статус self.lbl_testing_status = QLabel("Ожидание запуска...") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_status.setToolTip("Текущий статус тестирования") stats_layout.addRow(QLabel("Статус:"), self.lbl_testing_status) # Прогресс self.lbl_testing_rounds = QLabel("0 / 0") self.lbl_testing_rounds.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-weight: bold;") self.lbl_testing_rounds.setToolTip("Прогресс выполнения тестирования") stats_layout.addRow(QLabel("Прогресс:"), self.lbl_testing_rounds) # Профит self.lbl_testing_profit = QLabel("0.00") self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_profit.setToolTip("Текущий профит (доход/убыток)") stats_layout.addRow(QLabel("Профит:"), self.lbl_testing_profit) # ROI self.lbl_testing_roi = QLabel("0.00%") self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_roi.setToolTip("Возврат на инвестиции (ROI) в процентах") stats_layout.addRow(QLabel("ROI:"), self.lbl_testing_roi) # Винрейт self.lbl_testing_winrate = QLabel("0.00%") self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_winrate.setToolTip("Процент выигранных раундов") stats_layout.addRow(QLabel("Винрейт:"), self.lbl_testing_winrate) # --- ИЗМЕНЕНИЯ НАЧАЛИСЬ ЗДЕСЬ --- # Победы self.lbl_testing_wins = QLabel("0") self.lbl_testing_wins.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") self.lbl_testing_wins.setToolTip("Количество выигранных раундов") stats_layout.addRow(QLabel("Победы:"), self.lbl_testing_wins) # Поражения self.lbl_testing_losses = QLabel("0") self.lbl_testing_losses.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") self.lbl_testing_losses.setToolTip("Количество проигранных раундов") stats_layout.addRow(QLabel("Поражения:"), self.lbl_testing_losses) # Макс. серия побед self.lbl_testing_max_win_streak = QLabel("0") self.lbl_testing_max_win_streak.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") self.lbl_testing_max_win_streak.setToolTip("Максимальная серия побед подряд за всё время тестирования") stats_layout.addRow(QLabel("Макс. серия побед (за всё время):"), self.lbl_testing_max_win_streak) # Макс. серия поражений self.lbl_testing_max_loss_streak = QLabel("0") self.lbl_testing_max_loss_streak.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") self.lbl_testing_max_loss_streak.setToolTip("Максимальная серия поражений подряд за всё время тестирования") stats_layout.addRow(QLabel("Макс. серия поражений (за всё время):"), self.lbl_testing_max_loss_streak) # --- ИЗМЕНЕНИЯ ЗАКОНЧИЛИСЬ --- self.testing_stats_group.setLayout(stats_layout) layout.addWidget(self.testing_stats_group) # График тестирования self.testing_graph_panel = self.build_testing_graph_panel() layout.addWidget(self.testing_graph_panel) layout.addStretch() return tab def create_history_tables_tab_interface(self): """Создает новую вкладку с таблицами истории""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Заголовок header_label = QLabel("Таблицы истории") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 16pt; font-weight: bold; padding: 10px;") header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) header_label.setToolTip("Подробная история крашей и действий") layout.addWidget(header_label) # Таблицы истории tables_layout = QHBoxLayout() # Таблица истории крашей self.crash_history_table = CrashHistoryTable() self.crash_history_table.setToolTip("Таблица с последними 100 значениями краша") tables_layout.addWidget(self.crash_history_table) # Таблица истории действий self.actions_history_table = ActionsHistoryTable() self.actions_history_table.setToolTip("Таблица с последними 100 записями о действиях") tables_layout.addWidget(self.actions_history_table) layout.addLayout(tables_layout) layout.addStretch() return tab def build_testing_graph_panel(self): """Создает панель с графиком тестирования.""" panel = QGroupBox("График профита (Тестирование)") layout = QVBoxLayout(panel) layout.setContentsMargins(10, 10, 10, 10) panel.setToolTip("График динамики изменения профита по ходу тестирования") self.testing_graph_figure = Figure(figsize=(8, 4), dpi=100) self.testing_graph_canvas = FigureCanvas(self.testing_graph_figure) self.testing_graph_ax = self.testing_graph_figure.add_subplot(111) self.testing_graph_ax.set_title("Профит по раундам", color=APP_CONFIG['COLOR_TEXT'], fontsize=12) self.testing_graph_ax.set_xlabel("Раунд", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.set_ylabel("Профит", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.grid(True, color=APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.set_facecolor(APP_CONFIG['COLOR_SURFACE']) self.testing_graph_figure.patch.set_facecolor(APP_CONFIG['COLOR_BACKGROUND']) self.testing_graph_ax.tick_params(colors=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.spines['bottom'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.spines['top'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.spines['left'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.spines['right'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.title.set_color(APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.xaxis.label.set_color(APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.yaxis.label.set_color(APP_CONFIG['COLOR_TEXT']) layout.addWidget(self.testing_graph_canvas) return panel def start_testing(self): try: if hasattr(self, 'testing_worker') and self.testing_worker.isRunning(): QMessageBox.warning(self, "Предупреждение", "Тестирование уже запущено.") return # Считываем настройки num_rounds = self.num_rounds_input.value() update_interval = self.update_interval_input.value() min_bet = self.min_bet_input.value() max_bet = self.max_bet_input.value() min_multiplier = self.min_multiplier_input.value() max_multiplier = self.max_multiplier_input.value() initial_balance = self.initial_balance_input.value() # --- СЧИТЫВАЕМ НОВУЮ НАСТРОЙКУ --- precision = self.precision_input.value() # --- КОНЕЦ СЧИТЫВАНИЯ НОВОЙ НАСТРОЙКИ --- # Обновляем UI self.start_testing_button.setEnabled(False) self.stop_testing_button.setEnabled(True) self.lbl_testing_status.setText("Тестирование...") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_WARNING']}; font-weight: bold;") self.lbl_testing_rounds.setText(f"0 / {num_rounds:,}") # Создаем и запускаем поток тестирования с новым параметром точности self.testing_worker = TestingWorker( num_rounds, min_bet, max_bet, min_multiplier, max_multiplier, initial_balance, update_interval, precision # Передаем точность ) self.testing_worker.batch_finished.connect(self.on_testing_batch_finished) self.testing_worker.testing_finished.connect(self.on_testing_finished) self.testing_worker.testing_stopped.connect(self.on_testing_stopped) self.testing_worker.error_occurred.connect(self.on_testing_error) self.testing_worker.plot_data_ready.connect(self.update_testing_graph) self.testing_worker.start() except Exception as e: QMessageBox.critical(self, "Ошибка", f"Произошла ошибка при запуске тестирования: {str(e)}") def stop_testing(self): if hasattr(self, 'testing_worker') and self.testing_worker.isRunning(): self.testing_worker.stop() self.lbl_testing_status.setText("Остановка...") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") def on_testing_batch_finished(self, batch_stats: dict): """Вызывается по завершении батча раундов.""" try: # --- ПОЛУЧАЕМ ТОЧНОСТЬ ИЗ НАСТРОЕК --- precision = self.precision_input.value() format_str_profit = f"+.{precision}f" format_str_roi = f".{precision}f" format_str_winrate = f".{precision}f" # --- КОНЕЦ ПОЛУЧЕНИЯ ТОЧНОСТИ --- # Обновляем текстовую статистику self.lbl_testing_rounds.setText(f"{batch_stats['processed_rounds']:,} / {batch_stats['total_rounds']:,}") # Профит profit_value = batch_stats['current_profit'] self.lbl_testing_profit.setText(f"{profit_value:{format_str_profit}}") if profit_value >= 0: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 11pt;") else: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 11pt;") # ROI roi_value = batch_stats['current_roi'] self.lbl_testing_roi.setText(f"{roi_value:{format_str_roi}}%") if roi_value >= 0: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") else: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") # Винрейт winrate_value = batch_stats['current_winrate'] self.lbl_testing_winrate.setText(f"{winrate_value:{format_str_winrate}}%") if winrate_value >= 50: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") else: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") # Победы/Поражения self.lbl_testing_wins.setText(str(batch_stats['total_wins'])) self.lbl_testing_losses.setText(str(batch_stats['total_losses'])) # Серии self.lbl_testing_max_win_streak.setText(str(batch_stats['max_win_streak'])) self.lbl_testing_max_loss_streak.setText(str(batch_stats['max_loss_streak'])) # Обновляем таблицы (теперь они находятся на другой вкладке) с новой точностью self.crash_history_table.set_precision(precision) self.actions_history_table.set_precision(precision) self.crash_history_table.update_history(batch_stats['crash_history']) self.actions_history_table.update_history(batch_stats['actions_history']) # Обновляем график if len(batch_stats['plot_rounds']) > 1000: rounds_to_send = batch_stats['plot_rounds'][-1000:] profits_to_send = batch_stats['plot_profits'][-1000:] else: rounds_to_send = batch_stats['plot_rounds'] profits_to_send = batch_stats['plot_profits'] self.update_testing_graph(rounds_to_send, profits_to_send) except Exception as e: pass def update_testing_graph(self, rounds_list: list, profit_list: list): """Вызывается для обновления графика.""" try: # Обновляем график self.testing_graph_ax.clear() if rounds_list and profit_list: self.testing_graph_ax.plot(rounds_list, profit_list, color=APP_CONFIG['COLOR_SECONDARY'], linewidth=1.5) # Заливка под графиком self.testing_graph_ax.fill_between(rounds_list, profit_list, alpha=0.3, color=APP_CONFIG['COLOR_SECONDARY']) self.testing_graph_ax.set_title("Профит по раундам", color=APP_CONFIG['COLOR_TEXT'], fontsize=12) self.testing_graph_ax.set_xlabel("Раунд", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.set_ylabel("Профит", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.grid(True, color=APP_CONFIG['COLOR_BORDER']) self.testing_graph_canvas.draw() except Exception as e: pass def on_testing_finished(self, final_stats: dict): """Вызывается по завершении тестирования.""" try: # --- ПОЛУЧАЕМ ТОЧНОСТЬ ИЗ НАСТРОЕК --- precision = self.precision_input.value() format_str_profit = f"+.{precision}f" format_str_roi = f".{precision}f" format_str_winrate = f".{precision}f" # --- КОНЕЦ ПОЛУЧЕНИЯ ТОЧНОСТИ --- self.start_testing_button.setEnabled(True) self.stop_testing_button.setEnabled(False) self.lbl_testing_status.setText("Завершено") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") # Обновляем всю статистику # Профит profit_value = final_stats['total_profit'] self.lbl_testing_profit.setText(f"{profit_value:{format_str_profit}}") if profit_value >= 0: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 12pt;") else: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 12pt;") # ROI roi_value = final_stats['roi'] self.lbl_testing_roi.setText(f"{roi_value:{format_str_roi}}%") if roi_value >= 0: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 11pt;") else: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 11pt;") # Винрейт winrate_value = final_stats['winrate'] self.lbl_testing_winrate.setText(f"{winrate_value:{format_str_winrate}}%") if winrate_value >= 50: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 11pt;") else: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 11pt;") self.lbl_testing_wins.setText(str(final_stats['total_wins'])) self.lbl_testing_losses.setText(str(final_stats['total_losses'])) self.lbl_testing_max_win_streak.setText(str(final_stats['max_win_streak'])) self.lbl_testing_max_loss_streak.setText(str(final_stats['max_loss_streak'])) # Обновляем таблицы и график (таблицы теперь на другой вкладке) с новой точностью self.crash_history_table.set_precision(precision) self.actions_history_table.set_precision(precision) self.crash_history_table.update_history(final_stats['crash_history']) self.actions_history_table.update_history(final_stats['actions_history']) self.update_testing_graph(final_stats['plot_rounds'], final_stats['plot_profits']) QMessageBox.information(self, "Тестирование завершено", f"Тестирование {final_stats['total_rounds']:,} раундов завершено.\n" f"Итоговый профит: {final_stats['total_profit']:{format_str_profit}}\n" f"ROI: {final_stats['roi']:{format_str_roi}}%\n" f"Винрейт: {final_stats['winrate']:{format_str_winrate}}%") except Exception as e: pass def on_testing_stopped(self): """Вызывается при остановке тестирования пользователем.""" self.start_testing_button.setEnabled(True) self.stop_testing_button.setEnabled(False) self.lbl_testing_status.setText("Остановлено") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") QMessageBox.information(self, "Тестирование остановлено", "Тестирование было остановлено пользователем.") def on_testing_error(self, error_msg: str): """Вызывается при ошибке в процессе тестирования.""" self.start_testing_button.setEnabled(True) self.stop_testing_button.setEnabled(False) self.lbl_testing_status.setText("Ошибка") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") QMessageBox.critical(self, "Ошибка тестирования", f"Произошла ошибка во время тестирования:\n{error_msg}") def save_settings(self): QMessageBox.information(self, "Настройки", "Настройки сохранены.") self.statusBar().showMessage("Настройки сохранены", 3000) def restore_default_settings(self): reply = QMessageBox.question(self, 'Сброс настроек', 'Вы уверены, что хотите сбросить все настройки?', QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: try: # Сброс основных настроек self.initial_balance_input.setValue(0) self.min_bet_input.setValue(100) self.max_bet_input.setValue(100) self.min_multiplier_input.setValue(1.11) self.max_multiplier_input.setValue(1.11) # --- СБРОС НОВОЙ НАСТРОЙКИ --- self.precision_input.setValue(2) # --- КОНЕЦ СБРОСА НОВОЙ НАСТРОЙКИ --- self.statusBar().showMessage("Настройки сброшены", 3000) except Exception as e: QMessageBox.critical(self, "Ошибка", f"Произошла ошибка при сбросе настроек: {str(e)}") if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec())
  9. yellyex

    Программирование[11]

    @Arzanis Это что у тебя за чат?
  10. yellyex

    Программирование[11]

    Так она по чистому мат ожиданию не должна давать +! Она должна быть около нуля + - . Но ни как не +470к. И не в жёсткий минус тоже. Процент казино вообще роли не играет. Тут ошибка в математике и в коде. Это алиса
  11. yellyex

    Программирование[11]

    Задача: Мин/Макс ставка = 100 Мин/Макс множитель (автовывод) = 1.11 Начальный банк = 0. Но разрешено производить расчёты, если банк <= 0. Если число Краша >= множитель, то выигрыш. Выигрыш: банк = ставка * множитель - ставка. Если число Краша < множитель, то проигрыш. Проигрыш: банк = банк - ставка Нужно сыграть 1 миллион раундов. Посчитай итоговый профит. Тут ошибка либо у меня, либо у ИИ! Я уже не ебу что делать! Я сдаюсь! ГПТ 5 памаги! :) Но ведь шанс того, что Краш будет больше или равно 1.11 составляет 90.09%. Qwen3-Coder: Вы абсолютно правы, и за это приношу свои извинения. В предыдущем расчете была допущена ошибка в интерпретации формулы и, как следствие, в вычислении вероятности. Давайте пересчитаем правильно, используя формулу из кода generate_crash_number: краш = 1.0 / (1.0 - U), где U — случайное число от 0 до 1. Найдем U, при котором краш = 1.11: 1.11 = 1.0 / (1.0 - U) 1.0 - U = 1.0 / 1.11 U = 1.0 - (1.0 / 1.11) = 1.0 - 0.9009009... ≈ 0.0990991 Вероятность U < 0.0990991 равна 0.0990991. Следовательно, вероятность краш >= 1.11 (выигрыш) равна P_win = 1 - 0.0990991 = 0.9009009, или примерно 90.09%. Вероятность краш < 1.11 (проигрыш) равна P_lose = 0.0990991, или примерно 9.91%. Теперь пересчитаем профит с правильными вероятностями: Условия: Количество раундов: N = 1,000,000 Ставка: S = 100 (фиксированная) Множитель: M = 1.11 (фиксированный) Чистый выигрыш за раунд: W = S * M - S = 100 * 1.11 - 100 = 11 Проигрыш за раунд: L = -S = -100 P_win = 0.9009009 P_lose = 0.0990991 Расчёт: Ожидаемое количество выигрышей и проигрышей: Ожидаемое_кол_во_выигрышей = N * P_win = 1,000,000 * 0.9009009 ≈ 900,901 Ожидаемое_кол_во_проигрышей = N * P_lose = 1,000,000 * 0.0990991 ≈ 99,099 Общий выигрыш: Общий_выигрыш = Ожидаемое_кол_во_выигрышей * W = 900,901 * 11 = 9,909,911 Общий проигрыш (сумма всех потерянных ставок): Общий_проигрыш = Ожидаемое_кол_во_проигрышей * S = 99,099 * 100 = 9,909,900 Итоговый профит: Профит = Общий_выигрыш - Общий_проигрыш = 9,909,911 - 9,909,900 = 11 Правильный вывод: При заданных условиях (ставка 100, автовывод 1.11, 1 миллион раундов) ожидаемый итоговый профит составляет примерно +11. Спасибо за вашу внимательность!
  12. yellyex

    Программирование[11]

    Ребят помогите пожалуйста с кодом. python Пробую создать аналог игры Краш (онлайн-казино) С помощью ИИ написал простенький код для тестов, но у меня на дистанции не выдаёт реальное мат ожидание. Я ставлю мин/макс множитель 1.11 (как в казино). ставка фиксированная мин/макс 100. Это только для тестов. Делаю раза 3-4 по 1 миллион тестовых раундов и всё равно получаю положительное мат ожидание, но этого не может быть! Я должен сыграть в минус или в 0. Если я не решу эту проблему, то двигаться дальше я не смогу! Помогите пожалуйста!!! Вот формула генерации числа Краша (без процента казино (чистая)). Может быть в ней ошибка? def generate_crash_number(): """Генерирует случайное число краша""" # Генерируем равномерно распределённое число U ∈ [0, 1) U = random.uniform(0, 1) # Вычисляем множитель по формуле if U >= 0.99999: # Защита от деления на ноль crash_multiplier = 100000.00 else: crash_multiplier = 1.0 / (1.0 - U) # Ограничиваем множитель диапазоном [1.00, 100000.00] и округляем до 2 знаков crash_multiplier = max(min(crash_multiplier, 100000.00), 1.00) return round(crash_multiplier, 2) Вот ниже я прикреплю код. Может у кого есть доступ к ГПТ 5? Спросите у него, что за фигня? Qwen говорит, что ошибки в коде нет. Что генерация и вычисления тоже верны! А вот математика говорит, что профита 470-480 тысяч на 1 миллион раундов (ставка 100, множитель 1.11) просто не может быть. Код import sys import random import math import os from datetime import datetime from collections import deque from typing import List, Tuple, Dict, Any from PyQt6 import QtWidgets, QtCore, QtGui from PyQt6.QtCore import QThread, pyqtSignal, QTimer, QDateTime, Qt, QTime from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel, QPushButton, QMessageBox, QGroupBox, QSpinBox, QDoubleSpinBox, QCheckBox, QTableWidget, QTableWidgetItem, QHeaderView, QTabWidget, QTimeEdit, QFrame, QScrollArea, QSizePolicy ) import matplotlib matplotlib.use('QtAgg') from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure # --- APP_CONFIG --- APP_CONFIG = { 'APP_NAME': "Crash", 'APP_NAME_VERSION': "(v1.0)", 'STATS_FILE_NAME': "stats.txt", 'COLOR_BACKGROUND': "#1e1e2d", 'COLOR_SURFACE': "#2d2d44", 'COLOR_SURFACE_LIGHT': "#3d3d54", 'COLOR_TEXT': "#e0e0ff", 'COLOR_BORDER': "#444466", 'COLOR_PRIMARY': "#bb86fc", 'COLOR_SECONDARY': "#03dac6", 'COLOR_POSITIVE': "#00cc66", 'COLOR_NEGATIVE': "#ff5555", 'COLOR_WARNING': "#ffaa00", 'COLOR_INFO': "#66b3ff", 'TEXT_LABELS': { 'app_title': "Crash", 'save_settings': "Сохранить настройки", 'reset_settings': "Сбросить настройки", 'min_bet_label': "Минимальная ставка:", 'max_bet_label': "Максимальная ставка:", }, } # --- Crash Number Generator --- def generate_crash_number(): """Генерирует случайное число краша""" # Генерируем равномерно распределённое число U ∈ [0, 1) U = random.uniform(0, 1) # Вычисляем множитель по формуле if U >= 0.99999: # Защита от деления на ноль crash_multiplier = 100000.00 else: crash_multiplier = 1.0 / (1.0 - U) # Ограничиваем множитель диапазоном [1.00, 100000.00] и округляем до 2 знаков crash_multiplier = max(min(crash_multiplier, 100000.00), 1.00) return round(crash_multiplier, 2) # --- Crash History Table (для отображения последних 100 значений) --- class CrashHistoryTable(QWidget): def __init__(self, parent=None): super().__init__(parent) self.history = [] self.init_ui() def init_ui(self): self.layout = QVBoxLayout() self.setLayout(self.layout) # Заголовок header_layout = QHBoxLayout() header_label = QLabel("История последних значений краша") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 12pt; font-weight: bold;") header_label.setToolTip("Последние 100 значений коэффициента краша") header_layout.addWidget(header_label) header_layout.addStretch() self.layout.addLayout(header_layout) # Таблица self.table = QTableWidget() self.table.setColumnCount(2) self.table.setHorizontalHeaderLabels(["#", "Краш"]) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) self.table.verticalHeader().setVisible(False) self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.table.setSelectionMode(QTableWidget.SelectionMode.NoSelection) self.table.setStyleSheet(f""" QTableWidget {{ font-size: 9pt; color: {APP_CONFIG['COLOR_TEXT']}; background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; gridline-color: {APP_CONFIG['COLOR_BORDER']}; alternate-background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; }} QTableWidget::item {{ padding: 4px; border-bottom: 1px solid {APP_CONFIG['COLOR_BORDER']}; }} QHeaderView::section {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 6px; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; font-weight: bold; }} QTableWidget::item:selected {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; color: white; }} """) self.table.setToolTip("Таблица с последними 100 значениями краша") self.layout.addWidget(self.table) def update_history(self, crash_history: List[float]): """Обновляет таблицу всей историей крашей""" self.history = crash_history[-100:] if len(crash_history) > 100 else crash_history self.table.setRowCount(len(self.history)) for i, value in enumerate(reversed(self.history)): # Номер num_item = QTableWidgetItem(str(len(self.history) - i)) num_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) self.table.setItem(i, 0, num_item) # Значение краша crash_item = QTableWidgetItem(f"{value:.2f}x") crash_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # Цвет в зависимости от значения if value >= 10: crash_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_POSITIVE'])) crash_item.setFont(QtGui.QFont("Arial", 9, QtGui.QFont.Weight.Bold)) elif value >= 2: crash_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_SECONDARY'])) else: crash_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_NEGATIVE'])) self.table.setItem(i, 1, crash_item) # Выравнивание текста по центру for i in range(self.table.rowCount()): for j in range(self.table.columnCount()): item = self.table.item(i, j) if item: item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # --- Actions History Table (для отображения последних 100 действий) --- class ActionsHistoryTable(QWidget): def __init__(self, parent=None): super().__init__(parent) self.history = [] self.init_ui() def init_ui(self): self.layout = QVBoxLayout() self.setLayout(self.layout) # Заголовок header_layout = QHBoxLayout() header_label = QLabel("История последних действий") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 12pt; font-weight: bold;") header_label.setToolTip("Последние 100 записей о действиях в тестировании") header_layout.addWidget(header_label) header_layout.addStretch() self.layout.addLayout(header_layout) # Таблица self.table = QTableWidget() self.table.setColumnCount(10) self.table.setHorizontalHeaderLabels([ "Дата/Время", "Раунд", "Банк", "Ставка", "Множитель", "Краш", "Результат", "Новый банк", "Профит", "ROI" ]) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) self.table.verticalHeader().setVisible(False) self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) self.table.setSelectionMode(QTableWidget.SelectionMode.NoSelection) self.table.setStyleSheet(f""" QTableWidget {{ font-size: 8pt; color: {APP_CONFIG['COLOR_TEXT']}; background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; gridline-color: {APP_CONFIG['COLOR_BORDER']}; alternate-background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; }} QTableWidget::item {{ padding: 2px; border-bottom: 1px solid {APP_CONFIG['COLOR_BORDER']}; }} QHeaderView::section {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 4px; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; font-weight: bold; }} QTableWidget::item:selected {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; color: white; }} """) self.table.setToolTip("Таблица с последними 100 записями о действиях в тестировании") self.layout.addWidget(self.table) def update_history(self, actions_history: List[Dict[str, Any]]): """Обновляет таблицу всей историей действий""" self.history = actions_history[-100:] if len(actions_history) > 100 else actions_history self.table.setRowCount(len(self.history)) for i, data in enumerate(reversed(self.history)): timestamp = data['timestamp'].strftime("%H:%M:%S.%f")[:-3] # Обрезаем до миллисекунд self.table.setItem(i, 0, QTableWidgetItem(timestamp)) self.table.setItem(i, 1, QTableWidgetItem(str(data['round_num']))) self.table.setItem(i, 2, QTableWidgetItem(f"{data['balance_before']:.2f}")) self.table.setItem(i, 3, QTableWidgetItem(str(data['bet']))) self.table.setItem(i, 4, QTableWidgetItem(f"{data['multiplier']:.2f}")) self.table.setItem(i, 5, QTableWidgetItem(f"{data['crash_value']:.2f}")) # Результат if data['win']: result_text = f"+{data['reward']:.2f}" result_item = QTableWidgetItem(result_text) result_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_POSITIVE'])) result_item.setFont(QtGui.QFont("Arial", 8, QtGui.QFont.Weight.Bold)) else: result_text = f"-{data['bet']:.2f}" result_item = QTableWidgetItem(result_text) result_item.setForeground(QtGui.QColor(APP_CONFIG['COLOR_NEGATIVE'])) result_item.setFont(QtGui.QFont("Arial", 8, QtGui.QFont.Weight.Bold)) self.table.setItem(i, 6, result_item) self.table.setItem(i, 7, QTableWidgetItem(f"{data['balance_after']:.2f}")) self.table.setItem(i, 8, QTableWidgetItem(f"{data['profit']:.2f}")) self.table.setItem(i, 9, QTableWidgetItem(f"{data['roi']:.2f}%")) # Выравнивание текста по центру for i in range(self.table.rowCount()): for j in range(self.table.columnCount()): item = self.table.item(i, j) if item: item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # --- Testing Thread --- class TestingWorker(QThread): # Сигналы для обновления GUI batch_finished = pyqtSignal(dict) # Передаем данные батча testing_finished = pyqtSignal(dict) # итоговая статистика testing_stopped = pyqtSignal() error_occurred = pyqtSignal(str) plot_data_ready = pyqtSignal(list, list) # данные для графика def __init__(self, num_rounds: int, min_bet: int, max_bet: int, min_multiplier: float, max_multiplier: float, initial_balance: float, update_interval: int): super().__init__() self.num_rounds = num_rounds self.min_bet = min_bet self.max_bet = max_bet self.min_multiplier = min_multiplier self.max_multiplier = max_multiplier self.initial_balance = initial_balance self.update_interval = update_interval # 0 = только в конце self._is_running = True self.balance = initial_balance self.crash_history = [] self.actions_history = [] self.testing_rounds_data = [] self.testing_profit_data = [] self.total_wins = 0 self.total_losses = 0 self.current_win_streak = 0 self.current_loss_streak = 0 self.max_win_streak = 0 self.max_loss_streak = 0 self.total_invested = 0 # Для правильного расчета ROI self.processed_rounds = 0 def stop(self): self._is_running = False def run(self): try: batch_data = { 'rounds': [], 'crash_values': [], 'bets': [], 'multipliers': [], 'wins': [], 'rewards': [], 'balances_before': [], 'balances_after': [], 'profits': [], 'rois': [], 'winrates': [], 'timestamps': [], 'total_wins_list': [], 'total_losses_list': [], 'max_win_streaks': [], 'max_loss_streaks': [] } for i in range(1, self.num_rounds + 1): if not self._is_running: self.testing_stopped.emit() return # 1. Генерация краша crash_value = generate_crash_number() self.crash_history.append(crash_value) # 2. Генерация случайной ставки и множителя bet = random.randint(self.min_bet, self.max_bet) multiplier = round(random.uniform(self.min_multiplier, self.max_multiplier), 2) # 3. Симуляция результата balance_before = self.balance win = crash_value >= multiplier reward = 0 if bet > 0: self.total_invested += bet # Увеличиваем общую сумму ставок self.balance -= bet if win: win_amount = bet * multiplier self.balance += win_amount reward = win_amount - bet # Чистый выигрыш self.total_wins += 1 self.current_win_streak += 1 self.current_loss_streak = 0 if self.current_win_streak > self.max_win_streak: self.max_win_streak = self.current_win_streak else: reward = -bet # Потеря ставки self.total_losses += 1 self.current_loss_streak += 1 self.current_win_streak = 0 if self.current_loss_streak > self.max_loss_streak: self.max_loss_streak = self.current_loss_streak profit = self.balance - self.initial_balance # Исправленный расчет ROI roi = (profit / self.total_invested * 100) if self.total_invested > 0 else 0 winrate = (self.total_wins / i * 100) if i > 0 else 0 # Winrate от общего количества раундов # 4. Сохранение данных для истории действий action_data = { 'round_num': i, 'crash_value': crash_value, 'bet': bet, 'multiplier': multiplier, 'win': win, 'reward': reward, 'balance_before': balance_before, 'balance_after': self.balance, 'profit': profit, 'roi': roi, 'winrate': winrate, 'timestamp': datetime.now(), 'total_wins': self.total_wins, 'total_losses': self.total_losses, 'max_win_streak': self.max_win_streak, 'max_loss_streak': self.max_loss_streak } self.actions_history.append(action_data) # 5. Обновление данных для графика self.testing_rounds_data.append(i) self.testing_profit_data.append(profit) # 6. Подготовка данных для батча (если нужно обновление) if self.update_interval > 0: batch_data['rounds'].append(i) batch_data['crash_values'].append(crash_value) batch_data['bets'].append(bet) batch_data['multipliers'].append(multiplier) batch_data['wins'].append(win) batch_data['rewards'].append(reward) batch_data['balances_before'].append(balance_before) batch_data['balances_after'].append(self.balance) batch_data['profits'].append(profit) batch_data['rois'].append(roi) batch_data['winrates'].append(winrate) batch_data['timestamps'].append(datetime.now()) batch_data['total_wins_list'].append(self.total_wins) batch_data['total_losses_list'].append(self.total_losses) batch_data['max_win_streaks'].append(self.max_win_streak) batch_data['max_loss_streaks'].append(self.max_loss_streak) self.processed_rounds += 1 # 7. Эмит сигналов при достижении интервала обновления if self.update_interval > 0 and self.processed_rounds % self.update_interval == 0: batch_stats = { 'processed_rounds': self.processed_rounds, 'total_rounds': self.num_rounds, 'current_balance': self.balance, 'current_profit': profit, 'current_roi': roi, 'current_winrate': winrate, 'total_wins': self.total_wins, 'total_losses': self.total_losses, 'max_win_streak': self.max_win_streak, 'max_loss_streak': self.max_loss_streak, 'crash_history': self.crash_history.copy(), 'actions_history': self.actions_history.copy(), 'plot_rounds': self.testing_rounds_data.copy(), 'plot_profits': self.testing_profit_data.copy() } self.batch_finished.emit(batch_stats) # Очищаем батч данные batch_data = { 'rounds': [], 'crash_values': [], 'bets': [], 'multipliers': [], 'wins': [], 'rewards': [], 'balances_before': [], 'balances_after': [], 'profits': [], 'rois': [], 'winrates': [], 'timestamps': [], 'total_wins_list': [], 'total_losses_list': [], 'max_win_streaks': [], 'max_loss_streaks': [] } # Небольшая задержка только для очень маленьких интервалов if self.update_interval > 0 and self.update_interval < 10: self.msleep(1) # Тестирование завершено final_stats = { 'total_rounds': self.num_rounds, 'final_balance': self.balance, 'total_profit': self.balance - self.initial_balance, 'roi': ((self.balance - self.initial_balance) / self.total_invested * 100) if self.total_invested > 0 else 0, 'winrate': (self.total_wins / self.num_rounds * 100) if self.num_rounds > 0 else 0, 'total_wins': self.total_wins, 'total_losses': self.total_losses, 'max_win_streak': self.max_win_streak, 'max_loss_streak': self.max_loss_streak, 'crash_history': self.crash_history, 'actions_history': self.actions_history, 'plot_rounds': self.testing_rounds_data, 'plot_profits': self.testing_profit_data } self.testing_finished.emit(final_stats) except Exception as e: self.error_occurred.emit(str(e)) # --- Main Window --- class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(f"{APP_CONFIG['APP_NAME']} {APP_CONFIG['APP_NAME_VERSION']}") self.setGeometry(100, 100, 1400, 900) self.setStyleSheet(f""" QMainWindow {{ background-color: {APP_CONFIG['COLOR_BACKGROUND']}; }} QWidget {{ background-color: {APP_CONFIG['COLOR_BACKGROUND']}; color: {APP_CONFIG['COLOR_TEXT']}; font-family: 'Segoe UI', Arial, sans-serif; }} QLabel {{ color: {APP_CONFIG['COLOR_TEXT']}; background-color: transparent; }} QGroupBox {{ border: 2px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 8px; margin-top: 10px; padding: 10px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; background-color: {APP_CONFIG['COLOR_SURFACE']}; }} QGroupBox::title {{ subline-offset: -15px; padding: 0px 5px 0px 5px; color: {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 8px 15px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 100px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton:pressed {{ background-color: {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton:disabled {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: #888888; border: 1px solid #555555; }} QSpinBox, QDoubleSpinBox {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; color: {APP_CONFIG['COLOR_TEXT']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 4px; padding: 4px; selection-background-color: {APP_CONFIG['COLOR_PRIMARY']}; }} QSpinBox::up-button, QDoubleSpinBox::up-button {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; border: none; width: 16px; }} QSpinBox::down-button, QDoubleSpinBox::down-button {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; border: none; width: 16px; }} QCheckBox {{ color: {APP_CONFIG['COLOR_TEXT']}; }} QTabWidget::pane {{ border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; background-color: {APP_CONFIG['COLOR_BACKGROUND']}; }} QTabBar::tab {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 8px 15px; margin-right: 2px; border-top-left-radius: 4px; border-top-right-radius: 4px; }} QTabBar::tab:selected {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_SECONDARY']}; font-weight: bold; }} QTabBar::tab:hover {{ background-color: {APP_CONFIG['COLOR_PRIMARY']}; }} QHeaderView::section {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: {APP_CONFIG['COLOR_TEXT']}; padding: 6px; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; font-weight: bold; }} """) # Инициализация UI self.init_ui() def init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) self.main_layout = QVBoxLayout(central_widget) self.main_layout.setSpacing(10) self.setup_main_tabs() def setup_main_tabs(self): self.tabs = QTabWidget() self.tab_main = self.create_main_tab_interface() self.tab_settings = self.create_settings_tab_interface() self.tab_testing = self.create_testing_tab_interface() self.tab_history = self.create_history_tables_tab_interface() # Новая вкладка self.tabs.addTab(self.tab_main, "Главная") self.tabs.addTab(self.tab_settings, "Настройки") self.tabs.addTab(self.tab_testing, "Тестирование") self.tabs.addTab(self.tab_history, "Таблицы (История)") # Добавляем новую вкладку self.main_layout.addWidget(self.tabs) def create_main_tab_interface(self): tab = QWidget() layout = QVBoxLayout() layout.setSpacing(15) tab.setLayout(layout) # Вкладка теперь полностью пустая, как требовалось layout.addStretch() return tab def create_settings_tab_interface(self): tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Заголовок header_label = QLabel("Настройки тестирования") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 16pt; font-weight: bold; padding: 10px;") header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) header_label.setToolTip("Настройка параметров для симуляции игры Crash") layout.addWidget(header_label) # Основные настройки main_settings_group = QGroupBox("Основные параметры") main_settings_layout = QFormLayout() main_settings_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight) main_settings_layout.setHorizontalSpacing(20) main_settings_layout.setVerticalSpacing(10) main_settings_group.setToolTip("Основные параметры симуляции") # Начальный банк self.initial_balance_input = QSpinBox() self.initial_balance_input.setRange(0, 10000000) self.initial_balance_input.setValue(0) self.initial_balance_input.setGroupSeparatorShown(True) self.initial_balance_input.setToolTip("Начальный баланс для тестирования") main_settings_layout.addRow(QLabel("Начальный банк:"), self.initial_balance_input) # Минимальная ставка self.min_bet_input = QSpinBox() self.min_bet_input.setRange(0, 10000000) self.min_bet_input.setValue(100) self.min_bet_input.setGroupSeparatorShown(True) self.min_bet_input.setToolTip("Минимальный размер ставки для одной игры") main_settings_layout.addRow(QLabel(APP_CONFIG['TEXT_LABELS']['min_bet_label']), self.min_bet_input) # Максимальная ставка self.max_bet_input = QSpinBox() self.max_bet_input.setRange(0, 10000000) self.max_bet_input.setValue(100) self.max_bet_input.setGroupSeparatorShown(True) self.max_bet_input.setToolTip("Максимальный размер ставки для одной игры") main_settings_layout.addRow(QLabel("Максимальная ставка:"), self.max_bet_input) # Минимальный множитель self.min_multiplier_input = QDoubleSpinBox() self.min_multiplier_input.setRange(1.01, 100.0) self.min_multiplier_input.setSingleStep(0.1) self.min_multiplier_input.setDecimals(2) self.min_multiplier_input.setValue(1.11) self.min_multiplier_input.setToolTip("Минимальный множитель для кэшаута") main_settings_layout.addRow(QLabel("Мин множитель:"), self.min_multiplier_input) # Максимальный множитель self.max_multiplier_input = QDoubleSpinBox() self.max_multiplier_input.setRange(1.01, 100.0) self.max_multiplier_input.setSingleStep(0.1) self.max_multiplier_input.setDecimals(2) self.max_multiplier_input.setValue(1.11) self.max_multiplier_input.setToolTip("Максимальный множитель для кэшаута") main_settings_layout.addRow(QLabel("Макс множитель:"), self.max_multiplier_input) main_settings_group.setLayout(main_settings_layout) layout.addWidget(main_settings_group) # Кнопки действий buttons_layout = QHBoxLayout() buttons_layout.addStretch() self.save_settings_button = QPushButton(APP_CONFIG['TEXT_LABELS']['save_settings']) self.save_settings_button.clicked.connect(self.save_settings) self.save_settings_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 150px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_POSITIVE']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} """) self.save_settings_button.setToolTip("Сохранить текущие настройки") buttons_layout.addWidget(self.save_settings_button) self.reset_settings_button = QPushButton(APP_CONFIG['TEXT_LABELS']['reset_settings']) self.reset_settings_button.clicked.connect(self.restore_default_settings) self.reset_settings_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 150px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_WARNING']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} """) self.reset_settings_button.setToolTip("Сбросить все настройки к значениям по умолчанию") buttons_layout.addWidget(self.reset_settings_button) buttons_layout.addStretch() layout.addLayout(buttons_layout) layout.addStretch() return tab def create_testing_tab_interface(self): tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Заголовок header_label = QLabel("Тестирование стратегии") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 16pt; font-weight: bold; padding: 10px;") header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) header_label.setToolTip("Запуск и управление симуляцией игры Crash") layout.addWidget(header_label) # Панель управления тестированием control_group = QGroupBox("Управление тестированием") control_layout = QFormLayout() control_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight) control_layout.setHorizontalSpacing(20) control_layout.setVerticalSpacing(10) control_group.setToolTip("Параметры и управление процессом тестирования") self.num_rounds_input = QSpinBox() self.num_rounds_input.setRange(1, 100000000) self.num_rounds_input.setValue(1000000) self.num_rounds_input.setGroupSeparatorShown(True) self.num_rounds_input.setToolTip("Общее количество раундов для симуляции") control_layout.addRow(QLabel("Количество раундов:"), self.num_rounds_input) self.update_interval_input = QSpinBox() self.update_interval_input.setRange(0, 100000000) self.update_interval_input.setValue(0) self.update_interval_input.setGroupSeparatorShown(True) self.update_interval_input.setToolTip("Интервал обновления интерфейса в раундах (0 = только в конце)") control_layout.addRow(QLabel("Интервал обновления интерфейса:"), self.update_interval_input) # Кнопки buttons_layout = QHBoxLayout() buttons_layout.addStretch() self.start_testing_button = QPushButton("Начать тестирование") self.start_testing_button.clicked.connect(self.start_testing) self.start_testing_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 180px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_POSITIVE']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} """) self.start_testing_button.setToolTip("Запустить симуляцию игры с текущими настройками") buttons_layout.addWidget(self.start_testing_button) self.stop_testing_button = QPushButton("Завершить тестирование") self.stop_testing_button.clicked.connect(self.stop_testing) self.stop_testing_button.setEnabled(False) self.stop_testing_button.setStyleSheet(f""" QPushButton {{ background-color: {APP_CONFIG['COLOR_SURFACE']}; border: 1px solid {APP_CONFIG['COLOR_BORDER']}; border-radius: 5px; padding: 10px 20px; font-weight: bold; color: {APP_CONFIG['COLOR_TEXT']}; min-width: 180px; }} QPushButton:hover {{ background-color: {APP_CONFIG['COLOR_NEGATIVE']}; border: 1px solid {APP_CONFIG['COLOR_SECONDARY']}; }} QPushButton:disabled {{ background-color: {APP_CONFIG['COLOR_SURFACE_LIGHT']}; color: #888888; border: 1px solid #555555; }} """) self.stop_testing_button.setToolTip("Остановить текущую симуляцию") buttons_layout.addWidget(self.stop_testing_button) buttons_layout.addStretch() control_layout.addRow(buttons_layout) control_group.setLayout(control_layout) layout.addWidget(control_group) # Панель статистики тестирования self.testing_stats_group = QGroupBox("Статистика тестирования") stats_layout = QFormLayout() stats_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight) stats_layout.setHorizontalSpacing(20) stats_layout.setVerticalSpacing(8) self.testing_stats_group.setToolTip("Текущие результаты симуляции") # Статус self.lbl_testing_status = QLabel("Ожидание запуска...") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_status.setToolTip("Текущий статус тестирования") stats_layout.addRow(QLabel("Статус:"), self.lbl_testing_status) # Прогресс self.lbl_testing_rounds = QLabel("0 / 0") self.lbl_testing_rounds.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-weight: bold;") self.lbl_testing_rounds.setToolTip("Прогресс выполнения тестирования") stats_layout.addRow(QLabel("Прогресс:"), self.lbl_testing_rounds) # Профит self.lbl_testing_profit = QLabel("0.00") self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_profit.setToolTip("Текущий профит (доход/убыток)") stats_layout.addRow(QLabel("Профит:"), self.lbl_testing_profit) # ROI self.lbl_testing_roi = QLabel("0.00%") self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_roi.setToolTip("Возврат на инвестиции (ROI) в процентах") stats_layout.addRow(QLabel("ROI:"), self.lbl_testing_roi) # Винрейт self.lbl_testing_winrate = QLabel("0.00%") self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_INFO']}; font-weight: bold;") self.lbl_testing_winrate.setToolTip("Процент выигранных раундов") stats_layout.addRow(QLabel("Винрейт:"), self.lbl_testing_winrate) # --- ИЗМЕНЕНИЯ НАЧАЛИСЬ ЗДЕСЬ --- # Победы self.lbl_testing_wins = QLabel("0") self.lbl_testing_wins.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") self.lbl_testing_wins.setToolTip("Количество выигранных раундов") stats_layout.addRow(QLabel("Победы:"), self.lbl_testing_wins) # Поражения self.lbl_testing_losses = QLabel("0") self.lbl_testing_losses.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") self.lbl_testing_losses.setToolTip("Количество проигранных раундов") stats_layout.addRow(QLabel("Поражения:"), self.lbl_testing_losses) # Макс. серия побед self.lbl_testing_max_win_streak = QLabel("0") self.lbl_testing_max_win_streak.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") self.lbl_testing_max_win_streak.setToolTip("Максимальная серия побед подряд за всё время тестирования") stats_layout.addRow(QLabel("Макс. серия побед (за всё время):"), self.lbl_testing_max_win_streak) # Макс. серия поражений self.lbl_testing_max_loss_streak = QLabel("0") self.lbl_testing_max_loss_streak.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") self.lbl_testing_max_loss_streak.setToolTip("Максимальная серия поражений подряд за всё время тестирования") stats_layout.addRow(QLabel("Макс. серия поражений (за всё время):"), self.lbl_testing_max_loss_streak) # --- ИЗМЕНЕНИЯ ЗАКОНЧИЛИСЬ --- self.testing_stats_group.setLayout(stats_layout) layout.addWidget(self.testing_stats_group) # График тестирования self.testing_graph_panel = self.build_testing_graph_panel() layout.addWidget(self.testing_graph_panel) layout.addStretch() return tab def create_history_tables_tab_interface(self): """Создает новую вкладку с таблицами истории""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Заголовок header_label = QLabel("Таблицы истории") header_label.setStyleSheet(f"color: {APP_CONFIG['COLOR_SECONDARY']}; font-size: 16pt; font-weight: bold; padding: 10px;") header_label.setAlignment(Qt.AlignmentFlag.AlignCenter) header_label.setToolTip("Подробная история крашей и действий") layout.addWidget(header_label) # Таблицы истории tables_layout = QHBoxLayout() # Таблица истории крашей self.crash_history_table = CrashHistoryTable() self.crash_history_table.setToolTip("Таблица с последними 100 значениями краша") tables_layout.addWidget(self.crash_history_table) # Таблица истории действий self.actions_history_table = ActionsHistoryTable() self.actions_history_table.setToolTip("Таблица с последними 100 записями о действиях") tables_layout.addWidget(self.actions_history_table) layout.addLayout(tables_layout) layout.addStretch() return tab def build_testing_graph_panel(self): """Создает панель с графиком тестирования.""" panel = QGroupBox("График профита (Тестирование)") layout = QVBoxLayout(panel) layout.setContentsMargins(10, 10, 10, 10) panel.setToolTip("График динамики изменения профита по ходу тестирования") self.testing_graph_figure = Figure(figsize=(8, 4), dpi=100) self.testing_graph_canvas = FigureCanvas(self.testing_graph_figure) self.testing_graph_ax = self.testing_graph_figure.add_subplot(111) self.testing_graph_ax.set_title("Профит по раундам", color=APP_CONFIG['COLOR_TEXT'], fontsize=12) self.testing_graph_ax.set_xlabel("Раунд", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.set_ylabel("Профит", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.grid(True, color=APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.set_facecolor(APP_CONFIG['COLOR_SURFACE']) self.testing_graph_figure.patch.set_facecolor(APP_CONFIG['COLOR_BACKGROUND']) self.testing_graph_ax.tick_params(colors=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.spines['bottom'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.spines['top'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.spines['left'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.spines['right'].set_color(APP_CONFIG['COLOR_BORDER']) self.testing_graph_ax.title.set_color(APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.xaxis.label.set_color(APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.yaxis.label.set_color(APP_CONFIG['COLOR_TEXT']) layout.addWidget(self.testing_graph_canvas) return panel def start_testing(self): try: if hasattr(self, 'testing_worker') and self.testing_worker.isRunning(): QMessageBox.warning(self, "Предупреждение", "Тестирование уже запущено.") return # Считываем настройки num_rounds = self.num_rounds_input.value() update_interval = self.update_interval_input.value() min_bet = self.min_bet_input.value() max_bet = self.max_bet_input.value() min_multiplier = self.min_multiplier_input.value() max_multiplier = self.max_multiplier_input.value() initial_balance = self.initial_balance_input.value() # Обновляем UI self.start_testing_button.setEnabled(False) self.stop_testing_button.setEnabled(True) self.lbl_testing_status.setText("Тестирование...") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_WARNING']}; font-weight: bold;") self.lbl_testing_rounds.setText(f"0 / {num_rounds:,}") # Создаем и запускаем поток тестирования self.testing_worker = TestingWorker( num_rounds, min_bet, max_bet, min_multiplier, max_multiplier, initial_balance, update_interval ) self.testing_worker.batch_finished.connect(self.on_testing_batch_finished) self.testing_worker.testing_finished.connect(self.on_testing_finished) self.testing_worker.testing_stopped.connect(self.on_testing_stopped) self.testing_worker.error_occurred.connect(self.on_testing_error) self.testing_worker.plot_data_ready.connect(self.update_testing_graph) self.testing_worker.start() except Exception as e: QMessageBox.critical(self, "Ошибка", f"Произошла ошибка при запуске тестирования: {str(e)}") def stop_testing(self): if hasattr(self, 'testing_worker') and self.testing_worker.isRunning(): self.testing_worker.stop() self.lbl_testing_status.setText("Остановка...") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") def on_testing_batch_finished(self, batch_stats: dict): """Вызывается по завершении батча раундов.""" try: # Обновляем текстовую статистику self.lbl_testing_rounds.setText(f"{batch_stats['processed_rounds']:,} / {batch_stats['total_rounds']:,}") # Профит profit_value = batch_stats['current_profit'] self.lbl_testing_profit.setText(f"{profit_value:+.2f}") if profit_value >= 0: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 11pt;") else: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 11pt;") # ROI roi_value = batch_stats['current_roi'] self.lbl_testing_roi.setText(f"{roi_value:+.2f}%") if roi_value >= 0: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") else: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") # Винрейт winrate_value = batch_stats['current_winrate'] self.lbl_testing_winrate.setText(f"{winrate_value:.2f}%") if winrate_value >= 50: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") else: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") # Победы/Поражения self.lbl_testing_wins.setText(str(batch_stats['total_wins'])) self.lbl_testing_losses.setText(str(batch_stats['total_losses'])) # Серии self.lbl_testing_max_win_streak.setText(str(batch_stats['max_win_streak'])) self.lbl_testing_max_loss_streak.setText(str(batch_stats['max_loss_streak'])) # Обновляем таблицы (теперь они находятся на другой вкладке) self.crash_history_table.update_history(batch_stats['crash_history']) self.actions_history_table.update_history(batch_stats['actions_history']) # Обновляем график if len(batch_stats['plot_rounds']) > 1000: rounds_to_send = batch_stats['plot_rounds'][-1000:] profits_to_send = batch_stats['plot_profits'][-1000:] else: rounds_to_send = batch_stats['plot_rounds'] profits_to_send = batch_stats['plot_profits'] self.update_testing_graph(rounds_to_send, profits_to_send) except Exception as e: pass def update_testing_graph(self, rounds_list: list, profit_list: list): """Вызывается для обновления графика.""" try: # Обновляем график self.testing_graph_ax.clear() if rounds_list and profit_list: self.testing_graph_ax.plot(rounds_list, profit_list, color=APP_CONFIG['COLOR_SECONDARY'], linewidth=1.5) # Заливка под графиком self.testing_graph_ax.fill_between(rounds_list, profit_list, alpha=0.3, color=APP_CONFIG['COLOR_SECONDARY']) self.testing_graph_ax.set_title("Профит по раундам", color=APP_CONFIG['COLOR_TEXT'], fontsize=12) self.testing_graph_ax.set_xlabel("Раунд", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.set_ylabel("Профит", color=APP_CONFIG['COLOR_TEXT']) self.testing_graph_ax.grid(True, color=APP_CONFIG['COLOR_BORDER']) self.testing_graph_canvas.draw() except Exception as e: pass def on_testing_finished(self, final_stats: dict): """Вызывается по завершении тестирования.""" try: self.start_testing_button.setEnabled(True) self.stop_testing_button.setEnabled(False) self.lbl_testing_status.setText("Завершено") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold;") # Обновляем всю статистику # Профит profit_value = final_stats['total_profit'] self.lbl_testing_profit.setText(f"{profit_value:+.2f}") if profit_value >= 0: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 12pt;") else: self.lbl_testing_profit.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 12pt;") # ROI roi_value = final_stats['roi'] self.lbl_testing_roi.setText(f"{roi_value:+.2f}%") if roi_value >= 0: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 11pt;") else: self.lbl_testing_roi.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 11pt;") # Винрейт winrate_value = final_stats['winrate'] self.lbl_testing_winrate.setText(f"{winrate_value:.2f}%") if winrate_value >= 50: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_POSITIVE']}; font-weight: bold; font-size: 11pt;") else: self.lbl_testing_winrate.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold; font-size: 11pt;") self.lbl_testing_wins.setText(str(final_stats['total_wins'])) self.lbl_testing_losses.setText(str(final_stats['total_losses'])) self.lbl_testing_max_win_streak.setText(str(final_stats['max_win_streak'])) self.lbl_testing_max_loss_streak.setText(str(final_stats['max_loss_streak'])) # Обновляем таблицы и график (таблицы теперь на другой вкладке) self.crash_history_table.update_history(final_stats['crash_history']) self.actions_history_table.update_history(final_stats['actions_history']) self.update_testing_graph(final_stats['plot_rounds'], final_stats['plot_profits']) QMessageBox.information(self, "Тестирование завершено", f"Тестирование {final_stats['total_rounds']:,} раундов завершено.\n" f"Итоговый профит: {final_stats['total_profit']:+.2f}\n" f"ROI: {final_stats['roi']:+.2f}%\n" f"Винрейт: {final_stats['winrate']:.2f}%") except Exception as e: pass def on_testing_stopped(self): """Вызывается при остановке тестирования пользователем.""" self.start_testing_button.setEnabled(True) self.stop_testing_button.setEnabled(False) self.lbl_testing_status.setText("Остановлено") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") QMessageBox.information(self, "Тестирование остановлено", "Тестирование было остановлено пользователем.") def on_testing_error(self, error_msg: str): """Вызывается при ошибке в процессе тестирования.""" self.start_testing_button.setEnabled(True) self.stop_testing_button.setEnabled(False) self.lbl_testing_status.setText("Ошибка") self.lbl_testing_status.setStyleSheet(f"color: {APP_CONFIG['COLOR_NEGATIVE']}; font-weight: bold;") QMessageBox.critical(self, "Ошибка тестирования", f"Произошла ошибка во время тестирования:\n{error_msg}") def save_settings(self): QMessageBox.information(self, "Настройки", "Настройки сохранены (имитация).") self.statusBar().showMessage("Настройки сохранены", 3000) def restore_default_settings(self): reply = QMessageBox.question(self, 'Сброс настроек', 'Вы уверены, что хотите сбросить все настройки?', QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: try: # Сброс основных настроек self.initial_balance_input.setValue(0) self.min_bet_input.setValue(100) self.max_bet_input.setValue(100) self.min_multiplier_input.setValue(1.11) self.max_multiplier_input.setValue(1.11) self.statusBar().showMessage("Настройки сброшены", 3000) except Exception as e: QMessageBox.critical(self, "Ошибка", f"Произошла ошибка при сбросе настроек: {str(e)}") if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) Crash.txt
  13. Стрим live https://dzen.ru/news/rubric/putin_tramp?utm_source=third_rubric
  14. yellyex

    Весёлые смешинки

    5b59a6c0-8745-4429-99e9-99e38bda861c.mp4 https://prodota.ru/forum/uploads/monthly_2025_08/5b59a6c0-8745-4429-99e9-99e38bda861c.mp4.eda53e29a6957c05f0b5a61ed43274c9.mp4 e1ae1b03-5b10-4d2c-9e72-268fc7d51329.mp4 /monthly_2025_08/e1ae1b03-5b10-4d2c-9e72-268fc7d51329.mp4.d98fcd28e445f29cce95a5bfa21903a5.mp4
  15. yellyex

    Fighting Games Compilation

    Пидоры
×
×
  • Создать...